记录Demo完成过程中对OpenGL.ES的学习以及问题整理
1.初始化顶点坐标,坐标数据转为FloatBuffer——>OpenGL.ES program 2.创建,编译,启动OpenGL着色器
(3).激活并绑定纹理单元 (4).绑定图片到纹理单元 5.开始渲染绘制
世界坐标:用于显示的坐标即像素点显示的位置,纹理坐标:颜色所在的位置 两者需要做正确的映射才能正常显示 OpenGL.ES所有的画面都是由三角形构成,构建模式也不同 一般使用 GL_TRIANGLE_STRIP 顶点复用,绘制模式 (世界坐标,左下角开始,逆时针)———— 对应的纹理坐标也要和顶点坐标顺序一致(纹理坐标就是Android屏幕坐标,左下角开始,逆时针) 总结:
翻转y值:用1减去y坐标
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);
(图片和视频还是稍有不同的)
//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作为OpenGL与本地窗口渲染的中间桥梁 GLSurfaceView中已经封装好EGL了,所以我们平常接触不到
OpenGL仅能操作GPU并不能将图像渲染到设备显示窗口,所以需要中间层来链接OpenGL和设备窗口,于是EGL出现了 EGL为OpenGL指令创建Context,绘制目标等… EGLDisplay:抽象的系统显示类,用于操作设备窗口 EGLConfig:EGL配置 EGLSurface:渲染缓存,一块内存空间,所有要渲染到屏幕上的图像数据,都要先缓存在EGLSurface上 EGLContext:OpenGL上下文,存储OpenGL的绘制状态信息,数据
note:初始化EGL的过程其实就是配置以上信息的过程
EGL只初始化一次,无论后面Surface销毁和重建多少次
离屏窗口(不可见):eglCreatePbufferSurface创建
第一种最常用,通常将页面上的SurfaceView持有的Surface或SurfaceTexture传进去绑定。这样OpenGL处理的图像就能显示在屏幕上了 第二种用于离屏渲染,将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并不包含缓冲数据,仅保存缓冲数据的索引地址 分2类:
2D中,通常只用到了颜色附着 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
先将OES纹理,绑定到FBO上。同时会在FBO上绑定一个新的textureId(命名为OffScreenTextureId)。 然后调用绘制OES纹理的方法,数据就会传递到FBO上。 而我们可以通过绑定在其上的OffScreenTextureId得到其数据!!! 把绑定FBO和绘制到这个新的OffScreenTextureId代表的纹理的过程,称为离屏渲染!!!
1.只有左侧矩阵的列数与右侧矩阵的行数相等,两个矩阵才能相乘 2.矩阵相乘不遵循交换律,即A * B ≠ B * A