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

这篇具有很好参考价值的文章主要介绍了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;
}

片元着色器:

#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 渲染的步骤如下:

  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流导致预览黑屏(低级错误,但是耗费大量时间在排除问题上)

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 也可以根据需要自动选择正确的纹理对象进行绘制。

因此,在很多绘制流程最后几行代码都会调用类似GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);来解除绑定。文章来源地址https://www.toymoban.com/news/detail-490109.html

到了这里,关于OpenGL ES笔记 + Camera2 API + TextureView + 滤镜预览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Camera2处理预览回调数据时出现native错误的解决方法

    前言:在使用camera api 2 时,若是增加了回调数据的监听器,那么需要在OnImageAvailableListener回调函数中从ImageReader获取图像,并转化成我们需要的格式(比如使用自己写的yv12转nv21格式的图像格式转化函数),比如NV21格式。在退出预览界面时,有时候会在图像转化格式函数中出现

    2024年02月12日
    浏览(34)
  • Android流媒体开发之路一:Camera2采集摄像头数据并手动预览

    最近研究了一下android摄像头开发相关的技术,也看了Google提供的Camera2Basic调用示例,以及网上一部分代码,但都是在TextureView等预览基础上实现,而我想要做的是在不预览的情况下,能获取到摄像头原始数据流,并由自己来决定是否绘制显示。经过一番折腾,初步实现了自己

    2024年02月16日
    浏览(41)
  • Android Camera2 —CameraManager API详解

    一、CameraManager类概述 CameraManager是用于检测、表征和连接到 CameraDevices 的系统服务管理器。 CameraManager 是一个负责查询和建立相机连接的系统服务,它的功能不多,这里列出几个 CameraManager 的关键功能: 1)、将相机信息封装到 Camera Characteristics 中,并提获取 CameraCharacterist

    2024年02月12日
    浏览(43)
  • 一看就懂的OpenGL ES教程——仿抖音滤镜的各种奇技淫巧(一)_opengl es添加视频

    上一篇文章一看就懂的OpenGL ES教程——渲染宫崎骏动漫重拾童年 已经详细阐述了如何用OpenGL es将原始的YUV数据组成的视频渲染到屏幕上,想必有很多童鞋在阅读了它之后依然觉得回味无穷,学习的胃口也越来越大了,因为你们知道仅仅渲染视频是不够的,我们要的是,能够在

    2024年04月25日
    浏览(34)
  • 一看就懂的OpenGL ES教程——仿抖音滤镜的各种奇技淫巧(一)_opengl es添加视频(1)

    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。 深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学

    2024年04月16日
    浏览(30)
  • 学习笔记 -- 从零开始学习Android Camera2 -- (1)

    学习一个框架,第一步学习肯定是照着代码看文档。 既然要看代码,就要看最权威的,这里我是代码是参照https://github.com/android/camera-samples android给的官方示例,结合官方文档https://developer.android.com/reference/android/hardware/camera2/package-summary来看,所以首先要先看一遍文档,然后重

    2024年02月03日
    浏览(36)
  • 【Camera2 教程二】Camera2相机打开和关闭接口调用详细说明

    上一章《Camera2教程一》里我们介绍了一些 Camera2 的基础知识,但是并没有涉及太多的 API,从本章开始我们会开发一个具有完整相机功能的应用程序,并且将相机知识分成多个篇章进行介绍,而本章所要介绍的就是相机的开启流程。 阅读本章之后,你将学会以下几个知识点:

    2024年04月25日
    浏览(27)
  • 【Camera2教程一】Camera2的框架Pipeline和framework中核心类和接口的详细介绍

    一,框架pipeline 在Android中,Camera2 API提供了一个全新的框架来访问和控制设备上的相机硬件。这个框架的设计更加灵活和强大,允许开发者进行更精细的控制,同时支持更复杂的相机功能。Camera2 API的pipeline可以大致划分为以下几个关键部分: 相机访问: 首先,应用需要请求

    2024年04月15日
    浏览(27)
  • Android 高通Camera2 Camera Device Close

     1、很多人看到这个日志第一感觉可能觉得哪里没有合理释放,于是带着这个思路去进行百度探索 2、一开始我去寻找 ImageReader.OnImageAvailableListener 这个问题 3、后面网上去寻找因为  Camera2最大连拍限制是 2 网上很多数包括Google相机源码 需要单独开个线程去处理图片的逻辑

    2023年04月09日
    浏览(31)
  • android camera系列(Camera1、Camera2、CameraX)的使用以及输出的图像格式

    1.1.1、布局 1.1.2、实现预览 Camera.open() 打开摄像头 setPreviewDisplay 设置预览展示的控件 startPreview 开始预览 发现预览是横着的,需要使用 setDisplayOrientation 调整预览图像的方向 1.1.3、获取摄像头的原始数据 setPreviewCallback 设置预览数据的回调 2560*1440 默认返回图像的分辨率 Image

    2024年02月21日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包