<
OpenGL.ES学习总结
>
上一篇

RXJava基础总结
下一篇

Android动态权限适配

记录Demo完成过程中对OpenGL.ES的学习以及问题整理

简单的渲染流程:

1.初始化顶点坐标,坐标数据转为FloatBuffer——>OpenGL.ES program 2.创建,编译,启动OpenGL着色器

(3).激活并绑定纹理单元 (4).绑定图片到纹理单元 5.开始渲染绘制

坐标:

世界坐标:用于显示的坐标即像素点显示的位置,纹理坐标:颜色所在的位置 两者需要做正确的映射才能正常显示 OpenGL.ES所有的画面都是由三角形构成,构建模式也不同 一般使用 GL_TRIANGLE_STRIP 顶点复用,绘制模式 (世界坐标,左下角开始,逆时针)———— 对应的纹理坐标也要和顶点坐标顺序一致(纹理坐标就是Android屏幕坐标,左下角开始,逆时针) 总结:

  1. 纹理坐标与顶点坐标是对应关系
  2. 前置摄像头纹理坐标为:起始点 顺时针旋转90°
  3. 后置摄像头纹理坐标为:起始点 顺时针旋转90° 再延Y轴翻转

翻转y值:用1减去y坐标


GLSL简单介绍:

  1. attribute:一般用于各个顶点各不相同的量。如顶点颜色,坐标等
  2. uniform:一般用于对于3D物体中所有顶点都相同的量。如光源位置,统一变换矩阵等
  3. varying:表示易变量,一般用于顶点着色器传递到片元着色器的量
  4. const:常量

gl_Position:顶点坐标 gl_pointSize:点的大小,没赋值默认为1

gl_FragColor:当前片元颜色

private fun getVertexShader(): String {
    return  //顶点坐标
            "attribute vec4 aPosition;" +
            //纹理坐标
            "attribute vec2 aCoordinate;" +
            //用于传递纹理坐标给片元着色器,命名和片元着色器中的一致
            "varying vec2 vCoordinate;" +
            "void main() {" +
            "  gl_Position = aPosition;" +
            "  vCoordinate = aCoordinate;" +
            "}"
}
private fun getFragmentShader(): String {
    return  //配置float精度,使用了float数据一定要配置:lowp(低)/mediump(中)/hight(高)
            "precision mediump float;" +
            //从Java传递进入来的纹理单元
            "uniform sampler2D uTexture;" +
            //从顶点着色器传递进来的纹理坐标
            "varying vec2 vCoordinate;" +
            "void main() {" +
            //根据纹理坐标,从纹理单元中取色,texture2D是内置函数称之为采样器-获取纹理上指定位置的颜色值
            "  vec4 color = texture2D(uTexture, vCoordinate);" +
            "  gl_FragColor = color;" +
            "}"
}

//(3)激活并绑定纹理单元
private fun activateTexture() {
    //激活指定纹理单元
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
    //绑定纹理ID到纹理单元
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId)
    //将激活的纹理单元传递到着色器里面
    GLES20.glUniform1i(mTextureHandler, 0)
    //配置纹理过滤模式
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR.toFloat())
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
    //配置纹理环绕方式
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
}

纹理过滤模式:纹理坐标不依赖于分辨率,所以OpenGL需要知道怎样将纹理像素映射到纹理坐标。 2种模式:GL_NEAREST(临近过滤),GL_LINEAR(线性过滤)

//(4)绑定图片到纹理单元
激活纹理单元后调用texImage2D就可以把bmp绑定到指定的单元纹理上了图片的显示需要用到纹理单元来传递整张图片的内容
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);

OpenGL.ES的2D绘制流程:

  1. 通过GLSurfaceView配置OpenGL.ES版本,指定Render
  2. 实现GLSurfaceView.Render,复写暴露的方法,并配置OpenGL显示窗口,清屏
  3. 创建纹理ID
  4. 配置好顶点坐标,纹理坐标
  5. 初始化坐标变换矩阵
  6. 初始化OpenGL program,编译,链接着色器,获取GLSL中的变量属性
  7. 激活纹理单元,绑定纹理ID,传入着色器,配置纹理过滤模式/环绕模式
  8. 绑定纹理(如将bitmap绑定给纹理)
  9. 启动绘制

(图片和视频还是稍有不同的)

//draw step
1.onClear()//clear
2.onUseProgram()//use program
3.onSetExpandData()//active and bind custom data,额外给GLSL添加的属性,应用变化矩阵等
4.onBindTexture()//bing texture,激活绑定纹理数据
5.onDraw()//normal draw

note:渲染多个视频,其实就是生成多个纹理ID,利用这个ID生成一个Surface渲染表面,最后把这个Surface给到Codec渲染即可

如何传递参数到片元着色器:

要把Java中的值传递到片元着色器,直接传值是不行的,需要通过顶点着色器,间接传递 获取顶点着色器的alpha,然后在绘制前把值传递进去 在片元着色器中,修改从纹理中取出的颜色值的alpha,最后赋值给gl_FragColor

mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha")
GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha)

水印,透明等效果注意开启OpenGL混合模式,glBendFunc

private fun getVertexShader(): String {
    return "attribute vec4 aPosition;" +
            "precision mediump float;" +
            "uniform mat4 uMatrix;" +
            "attribute vec2 aCoordinate;" +
            "varying vec2 vCoordinate;" +
            "attribute float alpha;" +
            "varying float inAlpha;" +
            "void main() {" +
            "    gl_Position = uMatrix*aPosition;" +
            "    vCoordinate = aCoordinate;" +
            "    inAlpha = alpha;" +
            "}"
}

private fun getFragmentShader(): String {
    //一定要加换行"\n",否则会和下一行的precision混在一起,导致编译出错
    return "#extension GL_OES_EGL_image_external : require\n" +
            "precision mediump float;" +
            "varying vec2 vCoordinate;" +
            "varying float inAlpha;" +
            "uniform samplerExternalOES uTexture;" +
            "void main() {" +
            "  vec4 color = texture2D(uTexture, vCoordinate);" +
            "  gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +
            "}"
}

EGL:

EGL作为OpenGL与本地窗口渲染的中间桥梁 GLSurfaceView中已经封装好EGL了,所以我们平常接触不到

EGL是什么:

OpenGL仅能操作GPU并不能将图像渲染到设备显示窗口,所以需要中间层来链接OpenGL和设备窗口,于是EGL出现了 EGL为OpenGL指令创建Context,绘制目标等… EGLDisplay:抽象的系统显示类,用于操作设备窗口 EGLConfig:EGL配置 EGLSurface:渲染缓存,一块内存空间,所有要渲染到屏幕上的图像数据,都要先缓存在EGLSurface上 EGLContext:OpenGL上下文,存储OpenGL的绘制状态信息,数据

note:初始化EGL的过程其实就是配置以上信息的过程

EGL如何使用:

  1. 初始化init,分3步骤:
  1. 配置上下文getConfig:

EGL只初始化一次,无论后面Surface销毁和重建多少次

  1. 创建EGLSurface,2种模式:
  1. 绑定OpenGL渲染线程和绘制上下文:makeCurrent

Q:两个GLSurfaceView在渲染画面,OpenGL能正确的把画面分别绘制到两个GLSurfaceView中? A:因为glMakeCurrent,实现了设备显示窗口(EGLDisplay),OpenGL上下文(EGLContext)。图像数据缓存(EGLSurface),当前线程的绑定!

在EGL初始化后,即渲染环境就绪后,需要在渲染线程明确的调用glMakeCurrent。这时底层会将,OpenGL渲染环境绑定到当前线程

1.交换缓存数据并显示图像:swapBuffers EGL提供的用来将EGLSurface数据显示到设备屏幕上。在OpenGL绘制完图像后,调用该方法,才能真正显示出来 2.解绑数据缓存表面,释放资源

当页面重回前台时,会重建surface,这时只需要重建EGLSurface,并绑定上下文和EGLSurface,就可以继续渲染画面,无需开启新的渲染线程

FBO

帧缓冲对象,是一个对象,包含了多个缓存索引;FBO并不包含缓冲数据,仅保存缓冲数据的索引地址 分2类:

  1. 纹理附着(颜色附着):主要用于将颜色渲染到纹理中
  2. 渲染缓冲对象RBO:主要用于渲染深度信息,模板信息

2D中,通常只用到了颜色附着 FBO主要用于离屏渲染 使用流程:

  1. 新建纹理
  2. 新建FBO
  3. 绑定,将纹理附着到FBO的颜色附着点上
  4. 渲染
  5. 解绑FBO
  6. 删除FBO

纹理:

纹理ID就相当于纹理内容在内存中的索引,通过这个ID可以继续操作我们绑定的纹理

为了能把纹理映射到图像上,需要指定图形的每个顶点各自对应纹理的哪部分! 这样每个顶点就会关联着一个纹理坐标,用来标明该从纹理图像的哪个部分采样(采集片段颜色)!!! 纹理的环绕方式和过滤就是为了告诉OpenGL该怎样对纹理采样!!!

GLSL有一个供纹理对象使用的内建数据类型,叫做采样器(Sample),以纹理类型作为后缀,如sample2D。可以声明一个采样器把一个纹理添加到片段着色器中(JAVA代码中) texture函数来采样纹理的颜色,第一个参数是纹理采样器,第二个参数是对应的纹理坐标。 texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的纹理坐标上的颜色!!!

纹理单元:

sample2D变量是uniform为什么不用glUniform而是glUniform1i,因为我们可以给纹理采样器分配一个位置值,这样我们能够在一个片段着色器中设置多个纹理!!! 一个纹理的位置通常称为纹理单元。一个纹理的默认纹理单元是0,它是默认的激活纹理单元。 纹理单元主要目的就是让我们在着色器中可以使用多个纹理。通过把纹理单元赋值给采样器,我们一次可以绑定多个纹理

GLES30.glActiveTexture(GLES30.GL_TEXTURE0);//激活纹理单元
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mInputTextureId);//绑定OES纹理
GLES30.glUniform1i(s_Texture, 0);//将纹理设置给Shader

预览:

  1. Camera中得到的ImageStream由SurfaceTexture接收,并转换成OpenGL.ES纹理
  2. 创建GLSurfaceView。在OpenGL环境下,用GLSurfaceView将纹理绘制出来 其实也可以理解成:创建一个纹理ID,根据ID生成TextureView传给Camera,再由OpenGL根据这个相同的ID进行绘制 (因为Camera需要TextureView,而OpenGL操作的是纹理)

离屏绘制

先将OES纹理,绑定到FBO上。同时会在FBO上绑定一个新的textureId(命名为OffScreenTextureId)。 然后调用绘制OES纹理的方法,数据就会传递到FBO上。 而我们可以通过绑定在其上的OffScreenTextureId得到其数据!!! 把绑定FBO和绘制到这个新的OffScreenTextureId代表的纹理的过程,称为离屏渲染!!!

矩阵:

1.只有左侧矩阵的列数与右侧矩阵的行数相等,两个矩阵才能相乘 2.矩阵相乘不遵循交换律,即A * B ≠ B * A

Top
Foot