updateTexImage()方法作用是将底层的 SurfaceTexture 中最新的图像帧更新为 GLES 纹理,以便可被 OpenGL ES 渲染。
具体地说,当使用 SurfaceTexture 获取预览图像时,每次预览图像变更,就会触发 SurfaceTexture 中一次新图像帧的接收操作。这个新图像帧将被更新到 SurfaceTexture 的缓存区中,并可以通过调用 updateTexImage() 方法将其更新到 OpenGL ES 的纹理中。
updateTexImage() 方法会将最新的图像帧拷贝到 OpenGL ES 纹理中的纹理图像中。在拷贝图像帧之前,如果当前的图像帧已经准备好被渲染,那么我们需要激活新的纹理,并将纹理坐标系进行变换,从而保证渲染图像的连续性;同时,将新纹理创建之前的初始状态恢复,以保证新图像帧的正确性。
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() 方法是 OpenGL ES 中的一个函数,用于设置当前活动的纹理单元,是使用多个纹理的必选步骤。在调用 glActiveTexture() 方法之后,所有纹理的操作都将被应用于当前所选中的活动纹理单元。
当我们使用多个纹理时,需要分配一个纹理号(texture unit)以指示每个纹理的相对位置。纹理号是一个从 GL_TEXTURE0 开始的整数,可以使用 glActiveTexture() 方法选择。默认情况下,当前所选的纹理单元是 GL_TEXTURE0。因此,在使用其他纹理单元之前,需要首先选择并激活其余纹理单元。
例如,如果我们要使用 GL_TEXTURE1 作为当前所选的纹理单元,则可以使用以下代码:
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
此时,如果我们使用 glBindTexture() 方法将一张纹理绑定到当前所选的纹理单元上,那么该纹理将与 GL_TEXTURE1 关联。
在多纹理应用中,使用 glActiveTexture() 方法来切换当前活动的纹理单元是非常重要的,它能够使不同的纹理以并行的方式进行绑定、配置和渲染,从而提高效率和灵活性。
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;
}
片元着色器:
precision highp float;
uniform sampler2D texture;
varying vec2 v_texCoord;
void main()
{
float edge = 0.004;
float edgeValue = 1.0 - edge/ (1.0 - edge);
float thickness = 0.03;
float threshold = 1.0 - thickness/(1.0 - edge);
vec4 color = texture2D(texture, v_texCoord);
vec4 thresholded = vec4(0.0);
vec4 edgeColor = vec4(0.0);
if (color.r > threshold)
thresholded.r = 1.0;
if (color.g > threshold)
thresholded.g = 1.0;
if (color.b > threshold)
thresholded.b = 1.0;
vec4 uvOffset = vec4(0.0, 0.0, 1.0 / textureWidth, 1.0 / textureHeight);
if (color.r > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(-uvOffset.z,-uvOffset.w));
if (color.g > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(0.0,-uvOffset.w));
if (color.b > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(uvOffset.z,-uvOffset.w));
if (color.r > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(-uvOffset.z,0.0));
edgeColor += texture2D(texture, v_texCoord);
if (color.b > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(uvOffset.z,0.0));
if (color.r > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(-uvOffset.z,uvOffset.w));
if (color.g > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(0.0,uvOffset.w));
if (color.b > edgeValue)
edgeColor += texture2D(texture, v_texCoord + vec2(uvOffset.z,uvOffset.w));
edgeColor = edgeColor/9.0 * 0.7;
gl_FragColor = mix(thresholded, edgeColor, thickness*100.0);
}
具体的操作包括:
创建 EGL 实例和 OpenGL ES 上下文,并使用它们创建 EGLSurface 并将其与 SurfaceTexture 和 OpenGL ES 上下文绑定的详细步骤如下:
使用 EGL 实现 OpenGL ES 渲染时,您需要使用 EGL 类,首先,您需要使用 EGL10 类将其实例化:
EGL10 egl = (EGL10) EGLContext.getEGL();
显示是 EGL 实例渲染时需要渲染的设备,我们可以通过 eglGetDisplay() 方法获取默认显示:
EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
然后,您需要使用 eglInitialize() 方法初始化 EGL:
int[] version = new int[2];
egl.eglInitialize(eglDisplay, version);
在 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");
}
然后,您需要使用 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);
这是您已经获取了 TextureView 的 SurfaceTexture。
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
使用 EGL surface 将 OpenGL ES 图像渲染到 TextureView 的 SurfaceTexture 上, 您可以使用 EGLSurface,并将其与 SurfaceTexture 和 EGL 上下文绑定:
EGLSurface eglSurface = egl.eglCreateWindowSurface(eglDisplay, configs[0], surfaceTexture, null);
将 EGL 上下文和 EGLSurface 绑定,可以使用 eglMakeCurrent():
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
绑定完成后,可以在 TextureView 上使用 OpenGL ES 进行渲染了。
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流导致预览黑屏(低级错误,但是耗费大量时间在排除问题上)