updateTexImage()
updateTexImage()方法作用是将底层的 SurfaceTexture 中最新的图像帧更新为 GLES 纹理,以便可被 OpenGL ES 渲染。
具体地说,当使用 SurfaceTexture 获取预览图像时,每次预览图像变更,就会触发 SurfaceTexture 中一次新图像帧的接收操作。这个新图像帧将被更新到 SurfaceTexture 的缓存区中,并可以通过调用 updateTexImage() 方法将其更新到 OpenGL ES 的纹理中。
updateTexImage() 方法会将最新的图像帧拷贝到 OpenGL ES 纹理中的纹理图像中。在拷贝图像帧之前,如果当前的图像帧已经准备好被渲染,那么我们需要激活新的纹理,并将纹理坐标系进行变换,从而保证渲染图像的连续性;同时,将新纹理创建之前的初始状态恢复,以保证新图像帧的正确性。
getTransformMatrix()
SurfaceTexture 的 getTransformMatrix() 方法可以用来获取当前 SurfaceTexture 的变换矩阵,这个变换矩阵描述了从纹理坐标系到渲染目标坐标系的映射关系,并用于在 SurfaceTexture 的缓存区中已有的图像帧被完成 GPU 解码之前进行纹理坐标系变换和纹理位移操作。
与 SurfaceTexture 的 updateTexImage() 方法类似,getTransformMatrix() 方法也经常与 OpenGL ES 一起使用。具体地说,它通常在 OpenGL ES 的纹理绑定过程中使用,以将 SurfaceTexture 产生的纹理图像与 OpenGL ES 相关联。
在获取 SurfaceTexture 的变换矩阵时,可以使用一个float类型的缓冲区来接收它。这个缓冲区应该是至少有16个元素(即4行4列)的 float 数组,用来存储 OpenGL ES 的纹理映射矩阵,也就是变换矩阵。变换矩阵是一个4 x 4矩阵,它将纹理坐标系下的任意点映射到了渲染目标(例如SurfaceTexture所连接的SurfaceView)的坐标系中。
可以使用变换矩阵对纹理坐标进行转换,并在 OpenGL ES 中将纹理图像与当前的渲染目标相对应。这样就可以确保预览图像的正确位置和大小,并正确显示在渲染目标(例如 SurfaceView)中。
glActiveTexture()
glActiveTexture() 方法是 OpenGL ES 中的一个函数,用于设置当前活动的纹理单元,是使用多个纹理的必选步骤。在调用 glActiveTexture() 方法之后,所有纹理的操作都将被应用于当前所选中的活动纹理单元。
当我们使用多个纹理时,需要分配一个纹理号(texture unit)以指示每个纹理的相对位置。纹理号是一个从 GL_TEXTURE0 开始的整数,可以使用 glActiveTexture() 方法选择。默认情况下,当前所选的纹理单元是 GL_TEXTURE0。因此,在使用其他纹理单元之前,需要首先选择并激活其余纹理单元。
例如,如果我们要使用 GL_TEXTURE1 作为当前所选的纹理单元,则可以使用以下代码:
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
此时,如果我们使用 glBindTexture() 方法将一张纹理绑定到当前所选的纹理单元上,那么该纹理将与 GL_TEXTURE1 关联。
在多纹理应用中,使用 glActiveTexture() 方法来切换当前活动的纹理单元是非常重要的,它能够使不同的纹理以并行的方式进行绑定、配置和渲染,从而提高效率和灵活性。
glVertexAttrib2f()
glVertexAttrib2f() 方法是 OpenGL 中的一个函数,用于为顶点属性数组指定静态值。该方法将两个浮点数作为参数,并将它们分别存储在当前活动的顶点属性数组中。
在 OpenGL ES 中,通常使用 glVertexAttrib2f() 方法来指定顶点的纹理坐标。具体来说,可以使用 glVertexAttribPointer() 方法开启顶点属性数组,并使用 glEnableVertexAttribArray() 方法激活该属性数组之后,调用 glVertexAttrib2f() 方法为该属性数组中的每一个顶点指定一个二维纹理坐标值。
例如,下面的代码片段展示了如何在 OpenGL ES 中为纹理坐标属性数组设置静态值:
// 定义纹理坐标属性的位置
int texCoordLocation = GLES20.glGetAttribLocation(program, "a_texCoord");
// 启用纹理坐标属性数组
GLES20.glEnableVertexAttribArray(texCoordLocation);
// 指定纹理坐标属性数组的值
GLES20.glVertexAttrib2f(texCoordLocation, 0.0f, 0.0f);
GLES20.glVertexAttrib2f(texCoordLocation, 0.0f, 1.0f);
GLES20.glVertexAttrib2f(texCoordLocation, 1.0f, 1.0f);
素描滤镜
以下是一个简单的素描滤镜的顶点着色器和片元着色器。
顶点着色器:
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 v_texCoord;
void main()
{
v_texCoord = texCoord;
gl_Position = position;
}
片元着色器:
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES uTextureSampler;
const vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
varying vec2 vTextureCoord;
void main()
{
//黑白滤镜
// vec4 vCameraColor = texture2D(uTextureSampler, vTextureCoord);
// float fGrayColor = (0.3*vCameraColor.r + 0.59*vCameraColor.g + 0.11*vCameraColor.b);
// gl_FragColor = vec4(fGrayColor, fGrayColor, fGrayColor, 1.0);
vec4 color = texture2D(uTextureSampler, vTextureCoord);
float luminance = dot(color.rgb, luminanceWeighting);
// 将灰度值进行转换,让亮度较高的区域更黑,较低的区域更白
const float gamma = 2.2;
luminance = pow(luminance, gamma);
vec3 sketchColor = vec3(clamp(luminance * 20.0, 0.0, 1.0));
gl_FragColor = vec4(sketchColor, color.a);
}
使用 TextureView 配置 OpenGL ES 渲染的步骤如下:
- 获取 TextureView 的 SurfaceTexture 对象。
- 创建 EGL 实例和 OpenGL ES 上下文,并使用它们创建 EGLSurface,并将其与 SurfaceTexture 和 OpenGL ES 上下文绑定。
- 在 TextureView 的 SurfaceTextureListener 回调方法中,处理 EGLSurface 的创建和销毁。
- 实现 OpenGL ES 渲染器,例如绘制图形和其他渲染逻辑。
具体的操作包括:
- 用EGL10类实例化EGL。
- 通过EGL实例,选择EGLConfig和创建EGL上下文和EGLSurface。
- 在TextureView的SurfaceTextureListener的回调方法中处理EGLSurface的创建和销毁,例如重绘建议或TextureView销毁时销毁EGLSurface。
- 在OpenGL ES渲染器中实现图形绘制和其他渲染逻辑,例如在onDrawFrame方法中调用glDrawArrays()或glDrawElements()绘制图形。
创建 EGL 实例和 OpenGL ES 上下文,并使用它们创建 EGLSurface 并将其与 SurfaceTexture 和 OpenGL ES 上下文绑定的详细步骤如下:
- 获取 EGL 实例
使用 EGL 实现 OpenGL ES 渲染时,您需要使用 EGL 类,首先,您需要使用 EGL10 类将其实例化:
EGL10 egl = (EGL10) EGLContext.getEGL();
- 获取默认显示
显示是 EGL 实例渲染时需要渲染的设备,我们可以通过 eglGetDisplay() 方法获取默认显示:
EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
- 初始化 EGL
然后,您需要使用 eglInitialize() 方法初始化 EGL:
int[] version = new int[2];
egl.eglInitialize(eglDisplay, version);
- 配置 EGL
在 EGL 上下文中配置颜色等属性:
int[] attributes = {
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_RENDERABLE_TYPE, 4,
EGL10.EGL_NONE
};
在上述代码中,我们使用 eglChooseConfig() 方法从上述配置属性中获取 EGL 配置:
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
boolean result = egl.eglChooseConfig(eglDisplay, attributes, configs, configs.length, numConfigs);
if (!result) {
throw new IllegalStateException("eglChooseConfig() failed");
}
- 创建 EGL 上下文
然后,您需要使用 EGLContext 类创建 EGL 上下文:
int[] contextAttributes = { EGL10.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext eglContext = egl.eglCreateContext(eglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, contextAttributes);
- 获取 SurfaceTexture
这是您已经获取了 TextureView 的 SurfaceTexture。
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
- 创建 EGLSurface
使用 EGL surface 将 OpenGL ES 图像渲染到 TextureView 的 SurfaceTexture 上, 您可以使用 EGLSurface,并将其与 SurfaceTexture 和 EGL 上下文绑定:
EGLSurface eglSurface = egl.eglCreateWindowSurface(eglDisplay, configs[0], surfaceTexture, null);
- 将 EGL 上下文和 EGLSurface 绑定
将 EGL 上下文和 EGLSurface 绑定,可以使用 eglMakeCurrent():
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
绑定完成后,可以在 TextureView 上使用 OpenGL ES 进行渲染了。
SamplerExternalOES
SamplerExternalOES是OpenGL ES中的一个变量类型,用于在着色器中访问Android系统的预览帧或其他外部图像源的纹理数据。
这种类型只能通过GL_OES_EGL_image_external扩展创建,使用前需要使用扩展方法将OES_EGL_image_external类型图像数据转化为OpenGL ES中的二维纹理类型(GL_TEXTURE_2D)。
SamplerExternalOES类型的变量可用于向着色器提供OpenGL ES中的外部纹理数据。
在OpenGL ES的着色器代码中,可以通过声明samplerExternalOES类型的变量,然后将它绑定到一个外部纹理上,以便从外部源读取纹理数据。例如:
#extension GL_OES_EGL_image_external : require
uniform samplerExternalOES sTexture;
void main() {
vec4 color = texture2D(sTexture, vTextureCoord);
gl_FragColor = color;
}
在此示例中,samplerExternalOES类型的sTexture变量被声明为uniform变量。在main函数中,通过调用texture2D(sTexture, vTextureCoord)来从外部纹理读取对应坐标的颜色,然后将此颜色赋值给输出颜色gl_FragColor。
总而言之,SamplerExternalOES使得在OpenGL ES中访问来自外部图像源的纹理数据变得更加容易。正如名字中所示的那样,这种变量类型专门用于从具有扩展支持的外部源中读取纹理数据。
问题总结:
1.Camera2 API接入OpenGL ES后预览黑屏,不接入OpenGL预览正常。
原因:在初始化initOESTexture时需要通过glGenTextures获取的TextureID来构建一个SurfaceTexture, 将获取的SurfaceTexture传入Camera所配置的流中;问题原因在于将glGenTextures获取TextureID的步骤放入了子线程RenderThread,导致了时序问题,TextureID还未创建成功,主线程将默认的异常值作为参数构建了SurfaceTexture传入Camera流导致预览黑屏(低级错误,但是耗费大量时间在排除问题上)
2.在GLSurfaceView的Renderer实现类回调方法onSurfaceCreated中调用方法glGenTextures生成一个纹理Texture01,并将这个纹理Texture01放入一个SurfaceTexture中,那么SurfaceTexture中生成的图像流是怎么渲染到GLSurfaceView中的?
答:SurfaceTexture 作为纹理ID传递给 OpenGL ES 的 glTexImage2D 方法,在每个渲染帧中使用这个纹理ID来绘制图形。
预览的图像流通过 SurfaceTexture 提供给绘制目标消费。可以通过调用 SurfaceTexture#updateTexImage() 方法来更新 SurfaceTexture 中的纹理,以在每个渲染帧中将相机预览数据渲染到 GLSurfaceView 中。
3.OpenGL ES是如何区分传入的纹理与被绘制的纹理
答:OpenGL ES 中使用纹理对象来代表纹理数据,每个纹理对象有一个对应的纹理 ID(Texture ID),并与一个纹理缓存中存储的实际纹理数据相对应。
在 Android 中,要将纹理对象传递给 OpenGL ES 进行渲染,通常需要对纹理对象进行绑定操作,使用 glBindTexture 函数将纹理对象绑定到希望使用的纹理类型(如 GL_TEXTURE_2D)上。
而在纹理绑定完毕后,OpenGL ES 会跟踪绑定的纹理对象,并在程序绘制时使用这些绑定的纹理对象进行纹理采样。这样,即使有多个纹理对象被绑定到同一纹理类型上(如 GL_TEXTURE_2D),OpenGL ES 也可以根据需要自动选择正确的纹理对象进行绘制。文章来源:https://www.toymoban.com/news/detail-490109.html
因此,在很多绘制流程最后几行代码都会调用类似GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);来解除绑定。文章来源地址https://www.toymoban.com/news/detail-490109.html
到了这里,关于OpenGL ES笔记 + Camera2 API + TextureView + 滤镜预览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!