OpenGL ES笔记 + Camera2 API + TextureView + 滤镜预览

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;
}

片元着色器:

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);
}

使用 TextureView 配置 OpenGL ES 渲染的步骤如下:

  1. 获取 TextureView 的 SurfaceTexture 对象。
  2. 创建 EGL 实例和 OpenGL ES 上下文,并使用它们创建 EGLSurface,并将其与 SurfaceTexture 和 OpenGL ES 上下文绑定。
  3. 在 TextureView 的 SurfaceTextureListener 回调方法中,处理 EGLSurface 的创建和销毁。
  4. 实现 OpenGL ES 渲染器,例如绘制图形和其他渲染逻辑。

具体的操作包括:

  1. 用EGL10类实例化EGL。
  2. 通过EGL实例,选择EGLConfig和创建EGL上下文和EGLSurface。
  3. 在TextureView的SurfaceTextureListener的回调方法中处理EGLSurface的创建和销毁,例如重绘建议或TextureView销毁时销毁EGLSurface。
  4. 在OpenGL ES渲染器中实现图形绘制和其他渲染逻辑,例如在onDrawFrame方法中调用glDrawArrays()或glDrawElements()绘制图形。

创建 EGL 实例和 OpenGL ES 上下文,并使用它们创建 EGLSurface 并将其与 SurfaceTexture 和 OpenGL ES 上下文绑定的详细步骤如下:

  1. 获取 EGL 实例

使用 EGL 实现 OpenGL ES 渲染时,您需要使用 EGL 类,首先,您需要使用 EGL10 类将其实例化:

EGL10 egl = (EGL10) EGLContext.getEGL();

  1. 获取默认显示

显示是 EGL 实例渲染时需要渲染的设备,我们可以通过 eglGetDisplay() 方法获取默认显示:

EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

  1. 初始化 EGL

然后,您需要使用 eglInitialize() 方法初始化 EGL:

int[] version = new int[2];
egl.eglInitialize(eglDisplay, version);

  1. 配置 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");
}
  1. 创建 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);
  1. 获取 SurfaceTexture

这是您已经获取了 TextureView 的 SurfaceTexture。

SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
  1. 创建 EGLSurface

使用 EGL surface 将 OpenGL ES 图像渲染到 TextureView 的 SurfaceTexture 上, 您可以使用 EGLSurface,并将其与 SurfaceTexture 和 EGL 上下文绑定:

EGLSurface eglSurface = egl.eglCreateWindowSurface(eglDisplay, configs[0], surfaceTexture, null);
  1. 将 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流导致预览黑屏(低级错误,但是耗费大量时间在排除问题上)

你可能感兴趣的:(#,OpenGL,ES,elasticsearch,笔记)