由于本人懒的绘图,尽可能的用语言进行描述。
1、什么是OpenGL ES?和 OpenGL 是什么关系?
OpenGL ES是OpenGL三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计的,去除了许多不必要和性能较低的API接口。这里也提下EGL, EGL 是链接OpenGL 与 设备窗口的 管理器,属于跨平台的设计。
2、坐标
在绘图的时候,总会有纹理渲染问题,诸如偏移、倒转以及镜像等等,这里是因为对于各种坐标系还不太了解的原因,接下来梳理下可能用到的各种坐标系。
1、Android view坐标系
竖屏状态下,坐标原点在左上角,x轴 由左上角点 向右 延申,y轴 由左上角点 向下 延申。
一般textureCoords就是按照此种方式进行描述,如左上角(0, 0)、右上角(1, 0)、左下角(0, 1)、右下角(1, 1)
2、OpenGL世界坐标系
坐标原点在屏幕中心,x轴为水平方向,y轴为竖直方向,z轴穿过坐标原点向外为正方向,满足右手法则。
这也是我们绘图要打交道最多的一个坐标系,一般的vertexCoords就是按照此种坐标系描述,如左上角(-1, 1)、右上角(1, 1)、左下角(-1, -1)、右下角(1, -1)
3、立体坐标系
此种坐标系在OpenGL中 3D立体绘制 用的较多,数学模型中坐标系为:在2d平面上,水平方向为x轴,y轴垂直于x轴,垂足为坐标原点,z轴穿过坐标原点向上,延伸成为 3D 坐标系。
在立体几何的绘制过程中,由于 OpenGL 的世界坐标系 与 数学模型中的 立体几何坐标系是存在差异的,所以绘制的时候要多注意,该旋转的旋转。
3、旋转的几种情况
既然上文提到了旋转,这里也简单说下常用的旋转:
1、通过GLSL 在着色器中进行上下翻转,如下:
"#extension GL_OES_EGL_image_external : require"
"precision mediump float;" +
"varying vec2 vCoordinate;" +
"uniform samplerExternalOES uTexture;" +
"void main() {" +
" gl_FragColor = texture2D(uTexture, vec2(vCoordinate.x, 1.0 - vCoordinate.y));"
"}";
我这里有用的采样器因为截取的渲染视频的着色器,所以是拓展采样器,渲染静态的bmp 用的是 Sampler2d。
这里 顶点着色器 把 纹理坐标传过来后,在要采样的时候,把xy坐标拆出来,由于GL中是归一化的参数,所以上下颠倒就可以很容易的写出:(1 - vCoordinate.y),这种情况用的比较多
2、通过矩阵翻转顶点
这里和 第一种思想是一样的,就不多做解释了,一般不适用。
3、翻转原纹理图片
这种方法是通过 Matrix 把原始图片进行翻转,然后再进行渲染,一般不适用
4、纹理
1、普通图片纹理
2、从surface流中获取纹理
两种获取原始贴图纹理的方式,本质上是一样的。后续再补充。
5、FBO
FBO其实可以理解为一个容器缓冲,并不能直接用他绘制,里面包含了 多个颜色附着,一个深度附着,以及一个模板附着。
一搬使用流程:创建FBO textureID -> 创建FBO -> 绑定FBO -> draw -> 解绑FBO -> 激活显示纹理单元 -> draw
1、创建 FBO textureID
public static int createFBOTextureID(int width, int height) {
//新建纹理
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
//绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
//根据参数,为上面的纹理ID,生成一个2D纹理
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//配置边缘过渡参数
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textures[0];
}
2、创建FBO
public static int createFBO() {
int[] frameBuffer = new int[1];
GLES20.glGenFramebuffers(1, frameBuffer, 0);
return frameBuffer[0];
}
3、绑定 FBO
public static void bindFBO(int fbo, int textureID) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureID, 0);
}
绑定之后,下一步的draw动作只会发生在 FBO 中,不会直接显示出来。
4、draw
4.1、在draw之前,根据需要,更新下坐标系以及显示窗口,因为FBO坐标系需要恢复为opengl的默认坐标(上面有坐标系中有说过),这样采样器能正常取色。
4.2、执行纹理draw流程。
4.3、如果在4.1中进行了操作,要及时变更回来,方便下方进行绘制。
5、解绑FBO
public static void unBindFBO() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
解绑之后,接下来的draw动作,会显示出来。
6、激活纹理显示单元
activateTexture(GLES20.GL_TEXTURE_2D, mSoulTextureId, 1, mSoulTextureHandler);
7、draw
执行纹理draw流程
这里区别于 第四步的 draw,第四步的draw 是绘制到缓冲的FBO中,并不可见,所以需要draw 到屏幕中。
6、示例 Demo
还没来得及上传到git,后续有时间再上传。
7、番外
OpenGLES 个人觉得 本质上 是面向 textureID 的操作,无论是在 FBO 中的处理,还是 后续的显示draw,都是先绑定 ID,在 用的最多的camera 预览中,也是如此,由GL创建好surface后,传入到camera输出流数组中,后续其实还是和视频数据一样,从流中更新图元数据即可,大概的流程就是这样。
当然也可以同一个实体,渲染不同的纹理,只要给出纹理坐标以及相应的纹理资源即可,比如3D实体魔方等。(瞎猜,hhh)
能用OpenGLES 来实现的还有很多小东西,一直在学的路上。