Android OpenGLES2.0开发(八):Camera预览

严以律己,宽以待人

引言

终于到该章节了,还记得Android OpenGLES2.0开发(一):艰难的开始章节说的吗?写这个系列的初衷就是因为每次用到GLSurfaceView+Camera预览时,总是Ctrl+CCtrl+V从来没有研究过里面的代码,也不知道如何修改。经过前面章节的铺垫,现在可以自信的说Camera+OpenGL ES轻松拿捏。

外部纹理

上一篇中我们已经讲过如何显示一张图片,而Camera预览其实也是显示一张一张的图片。我们将Camera的预览帧数据转化为Bitmap传给OpenGL ES就可以了。但是这种方式就失去了使用OpenGL ES效率高的优势,NV21转Bitmap是CPU操作极其耗性能。

那么有没有更好的方式?答案是有的,我们可以将NV21数据直接传给OpenGL ES进行处理预览,这样操作就快了很多。我们知道OpenGL ES显示的是RGBA的数据,相当于OpenGL ES要将NV21转为RGBA,效率肯定比上面的情况好很多,但是操作略微复杂,这也不是最终方案,有没有更简单的方式呢?

Android的Camera及Camera2都可以使用SurfaceTexture作为预览载体,但是它们所使用的SurfaceTexture传入的OpenGL ES texture object name必须为GLES11Ext.GL_TEXTURE_EXTERNAL_OESGL_TEXTURE_EXTERNAL_OES是一种特殊的纹理类型,只用于处理外部图像或视频数据,如从摄像头捕捉的实时图像和外部视频流

GL_TEXTURE_EXTERNAL_OES的特点:

  • 需采用特殊的采样器类型和纹理着色器扩展
  • 使用二维纹理坐标进行操作,与GL_TEXTURE_2D相似
  • 专门用于处理外部图像或视频数据,可直接从BufferQueue中接收的数据渲染纹理多边形,从而提供更高效的视频处理和渲染性能

纹理渲染

Android Camera系列(三):GLSurfaceView+Camera这篇文章我们详细介绍了Camera使用GLSurfaceView进行预览操作,但唯独缺失了使用OpenGL ES绘制部分,而本篇是时候填坑了。

开始编写代码前,我们需要将上一篇的Image类拷贝一份命名为CameraFilter

1. 修改纹理着色器

首先,我们需要修改我们的着色器,将顶点着色器修改为:

// 顶点着色器代码
private final String vertexShaderCode ="uniform mat4 uMVPMatrix;\n" +// 顶点坐标"attribute vec4 vPosition;\n" +"uniform mat4 uTexPMatrix;\n" +// 纹理坐标"attribute vec4 vTexCoordinate;\n" +"varying vec2 aTexCoordinate;\n" +"void main() {\n" +"  gl_Position = uMVPMatrix * vPosition;\n" +"  aTexCoordinate = (uTexPMatrix * vTexCoordinate).xy;\n" +"}";

顶点着色器中的代码和渲染图片顶点着色器代码基本一致,增加了一个uTexPMatrix变量,这个是用来对纹理坐标进行变换的矩阵

uTexPMatrix纹理顶点变换的矩阵其实可以不用,我们只用顶点变换矩阵也是可以的,但是我们就需要对Camera前后置旋转变换要做一个处理。而SurfaceTexture中会自带一个变换矩阵,我们拿来直接用就不用处理Camera的前后置及旋转方向的问题了。

// 片段着色器代码
private final String fragmentShaderCode ="#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;\n" +"uniform samplerExternalOES vTexture;\n" +"varying vec2 aTexCoordinate;\n" +"void main() {\n" +"  gl_FragColor = texture2D(vTexture, aTexCoordinate);\n" +"}\n";

片段着色器中我们不再使用sampler2D采样,而是使用samplerExternalOES纹理采样器,并且要在头部增加使用扩展纹理的声明#extension GL_OES_EGL_image_external : require

2. 设置顶点坐标和纹理坐标

上一篇中我们已经正确设置了坐标,所以这两个顶点坐标保持不变

3. 初始化

初始化我们不再需要传入Bitmap

public CameraFilter() {
...
}

4. 创建外部纹理

我们需要在surfaceCreated中创建外部纹理,相机预览使用EXTERNAL_OES纹理,创建方式与2D纹理创建基本相同

public void surfaceCreated() {// 加载顶点着色器程序...// 创建纹理句柄textureId = createTexture();
}public int createTexture() {int[] texture = new int[1];GLES20.glGenTextures(1, texture, 0);GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);// 取消绑定纹理GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);return texture[0];
}

由于我们创建的是扩展纹理,所以绑定的时候我们也需要绑定到扩展纹理上才可以正常使用,GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,texture[0])

5. 计算变换矩阵

surfaceChanged中计算变换矩阵,由于视图的变换现在交给了纹理顶点坐标,所以我们顶点坐标矩阵使用原始矩阵即可。

public void surfaceChanged(int width, int height) {GLES20.glViewport(0, 0, width, height);// 获取原始矩阵,与原始矩阵相乘坐标不变Matrix.setIdentityM(mMVPMatrix, 0);
}

现在出现了两个矩阵,一个是顶点变换矩阵,一个是纹理变换矩阵。只要有顶点坐标都可以进行变换,但是我们最好控制变量,不要两个同时变换。如果还想继续使用顶点坐标矩阵变换,那么可以删除纹理矩阵参数。

5. 渲染

我们修改draw方法中的部分代码

  • 将纹理变换矩阵传入给顶点着色器
  • glBindTexture改为GLES11Ext.GL_TEXTURE_EXTERNAL_OES
public void draw(float[] texMatrix) {...// 将纹理坐标变换矩阵传递给顶点着色器GLES20.glUniformMatrix4fv(vTexPMatrixHandle, 1, false, texMatrix, 0);...// 激活纹理编号0GLES20.glActiveTexture(GLES20.GL_TEXTURE0);// 绑定纹理GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);// 设置纹理采样器编号,该编号和glActiveTexture中设置的编号相同GLES20.glUniform1i(texHandle, 0);// 绘制GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);// 取消绑定纹理GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);...
}

Camera预览

GLSurfaceView的模板代码我就不再这里列了,我这里只把Renderer中的代码再贴出来,看下他是如何使用CameraFilter进行渲染的

static class MyRenderer implements Renderer {private CameraFilter mCameraFilter;private int mTextureId;private SurfaceTexture mSurfaceTexture;private CameraGLSurfaceView mView;private final float[] mDisplayProjectionMatrix = new float[16];public MyRenderer(CameraGLSurfaceView glSurfaceView) {mView = glSurfaceView;mCameraFilter = new CameraFilter();}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {mCameraFilter.surfaceCreated();mTextureId = mCameraFilter.getTextureId();mSurfaceTexture = new SurfaceTexture(mTextureId);mView.mMainHandler.post(() -> mView.surfaceTextureCreated(mSurfaceTexture));}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {mCameraFilter.surfaceChanged(width, height);}@Overridepublic void onDrawFrame(GL10 gl) {// 更新最新纹理mSurfaceTexture.updateTexImage();// 获取SurfaceTexture变换矩阵mSurfaceTexture.getTransformMatrix(mDisplayProjectionMatrix);// 将SurfaceTexture绘制到GLSurfaceView上mCameraFilter.draw(mDisplayProjectionMatrix);}
}

Renderer的代码比较简单,在对应的生命周期中调用CameraFilter的生命周期方法即可。我们需要注意的就是onDrawFrame方法中的功能:

  • mSurfaceTexture.updateTexImage:
    从OpenGL上下文线程,即当前渲染线程中更新图像数据流最近的一帧纹理图像。调用该方法我们可以获取一帧新的图像用来渲染。
  • mSurfaceTexture.getTransformMatrix:
    获取刚那一帧纹理数据的变换矩阵,我们只需将该矩阵传入着色器就可以获取一个方向正确的预览视图。

为什么getTransformMatrix获取到的矩阵可以获取到正确的预览方向?原因在于Camera在初始化时设置了正确的预览方向,他会把正确的方向映射给SurfaceTexture。所以我们不需要再写复杂的投影变化了,直接用getTransformMatrix获取的变换矩阵即可。

请添加图片描述
预览效果如上,搞定手工!

最后

该篇章主要讲解了OpenGL ES对外部纹理Camera预览数据如何进行渲染,因为有了前面的基础,在对外部纹理渲染时我们只修改了部分代码即可实现。Camera的相关操作我们在Android Camera系列中有详细的讲解,这里没有在重复说明,回顾前面的篇章本篇享用更佳。

OpenGL ES系列:https://github.com/xiaozhi003/OpenGLDemo.git
Camera系列:https://github.com/xiaozhi003/AndroidCamera.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/16869.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基础 IO

目录 一、基本共识 二、复习C语言中的文件操作 三、与文件操作有关的系统调用接口 1. open 与 close 1.1 umask 2. write 3. read 四、如何理解文件 1. 文件描述符 fd 2. 文件fd分配规则 3. 重定向的引入 4. 重定向的本质 5. dup2 6. 理解 >、>>、…

ThriveX 博客管理系统前后端项目部署教程

前端 前端项目地址:https://github.com/LiuYuYang01/ThriveX-Blog 控制端项目地址:https://github.com/LiuYuYang01/ThriveX-Admin Vercel 首先以 Vercel 进行部署,两种方式部署都是一样的,我们以前端项目进行演示 首先我们先…

[含文档+PPT+源码等]精品基于springboot实现的原生Andriod手机使用管理软件

软件开发环境及开发工具: 数据库管理工具:phpstudy/Navicat或者phpstudy/sqlyog 开发工具:Android Studio 后台管理系统涉及技术: 后台使用框架:Springboot 前端使用技术:Vue,HTML5,CSS3、JavaScript等…

华为三层交换机禁止VLAN间通讯(两种解决方案)

在日常办公中,有时会禁止内网中各个部门间的访问,例如: ①访客不能访问内网任何终端及服务器 ②财务部门不能被其他部门访问 实验环境:华为Ensp模拟器 内网架构:三层网络 环境说明:三层交换机承载着网…

为以人工智能为中心的工作负载重新设计的全局控制台

MinIO 控制台多年来一直是一个不断发展的产品。每次学习时,我们都会思考如何改进交互框架中这个非常重要的部分。首先是控制台,它在推出后的一年内就被广泛采用。更具体地说,超过 10K 个组织。接下来是企业控制台。这从对象存储与其 GUI 之间…

stm32在linux环境下的开发与调试

环境安装 注:文末提供一键脚本 下载安装stm32cubeclt 下载地址为:https://www.st.com/en/development-tools/stm32cubeclt.html 选择 linux版本下载安装 安装好后默认在家目录st下 > $ ls ~/st/stm32cubeclt_1.16.0 …

【leetcode】N皇后 回溯法c++

目录 51.N皇后 52.N皇后II 51.N皇后 51. N 皇后 - 力扣(LeetCode) 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间…

GESP4级考试语法知识(贪心算法(六))

寻找平面上的极大点代码 #include<iostream> #include<algorithm> using namespace std; struct node {int x,y; }a[101]; bool vis[101]; bool cmp(node A,node B) {if(A.x!B.x) return A.x<B.x;return A.y<B.y; } int main() {int n;cin>>n;for(int…

如何构建高效的知识库系统?实现智能信息管理

在当今信息化迅速发展的时代&#xff0c;企业和组织面临着海量信息的挑战。如何有效地管理这些信息&#xff0c;使其安全、易于访问&#xff0c;并且能够快速响应用户的需求&#xff0c;成为了智慧管理的核心。而知识库系统(Knowledge Base System)正是为了解决这一问题而应运而…

动态规划29:673. 最长递增子序列的个数

动态规划解题步骤&#xff1a; 1.确定状态表示&#xff1a;dp[i]是什么 2.确定状态转移方程&#xff1a;dp[i]等于什么 3.初始化&#xff1a;确保状态转移方程不越界 4.确定填表顺序&#xff1a;根据状态转移方程即可确定填表顺序 5.确定返回值 题目链接&#xff1a;673.…

AI驱动的桌面笔记应用Reor

网友 竹林风 说&#xff0c;已经成功的用 mxbai-embed-large 映射到 text-embedding-ada-002&#xff0c;并测试成功了。不愧是爱折腾的人&#xff0c;老苏还没时间试&#xff0c;因为又找到了另一个支持 AI 的桌面版笔记 Reor Reor 简介 什么是 Reor ? Reor 是一款由人工智…

AWS CLI

一、介绍 1、简介 aws configure 是 AWS 提供的一个命令行工具&#xff0c;用于快速配置 AWS CLI&#xff08;命令行界面&#xff09;和 AWS SDK&#xff08;软件开发工具包&#xff09;中使用的凭证、默认区域以及输出格式。这个命令是 AWS CLI 中的一个配置工具&#xff0c…

Unity图形学之Shader2.0 深度测试

1.什么是深度&#xff1a;物体1和物体2的深度都是10&#xff0c;深度是物体和相机视角水平垂线的投影 物体3的深度是8 2.什么是深度缓存 要渲染的像素的深度信息&#xff0c;比当前存在Gbuffer里的缓存的深度信息小&#xff08;靠近相机&#xff09;的时候&#xff0c;就会替换…

39.安卓逆向-壳-smali语法3(方法)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

QT QLineEdit失去焦点事件问题与解决

本文介绍如何获得QLineEdit的失去焦点事件和获得焦点的输入框也会触发失去焦点事件的问题&#xff01; 目录 一、QLineEdit获得失去焦点事件 1.自定义类继承自QLineEdit 2.重写 focusOutEvent 3.使用 二、失去焦点事件问题 1.问题描述 2.问题解决 三、源码分享 lineed…

【Goland】——Gin 框架中间件详解:从基础到实战

中间件是 Web 应用开发中常见的功能模块&#xff0c;Gin 框架支持自定义和使用内置的中间件&#xff0c;让你在请求到达路由处理函数前进行一系列预处理操作。这篇博客将涵盖中间件的概念、内置中间件的用法、如何编写自定义中间件&#xff0c;以及在实际应用中的一些最佳实践。…

leetcode 3239. 最少翻转次数使二进制矩阵回文 I

给你一个 m x n 的二进制矩阵 grid 。 如果矩阵中一行或者一列从前往后与从后往前读是一样的&#xff0c;那么我们称这一行或者这一列是 回文 的。 你可以将 grid 中任意格子的值 翻转 &#xff0c;也就是将格子里的值从 0 变成 1 &#xff0c;或者从 1 变成 0 。 请你返回 …

【C++笔记】vector使用详解及模拟实现

前言 各位读者朋友们&#xff0c;大家好&#xff01;上期我们讲了string类的模拟实现&#xff0c;这期我们开启vector的讲解。 一.vector的介绍及使用 1.1 vector的介绍 vector的文档 使用STL的三个境界&#xff1a;能用、明理、能扩展&#xff0c;下面学习vector&#xff…

GOLANG+VUE后台管理系统

1.截图 2.后端工程截图 3.前端工程截图

推荐一款流程图和图表绘制工具:WizFlow Flowcharter Pro

WizFlow Flowcharter是一款易于使用、功能丰富的Windows流程图和图表绘制工具。它允许用户使用超过一百种预定义的形状和箭头定义形状“样式”。您可以将自己的样式保存在图表模板中&#xff0c;以建立自己的绘图方法。WizFlow附带了完整的流程图模板&#xff0c;以帮助您入门。…