《OpenGL ES 应用开发实践指南》读书笔记 No.2
原文链接 http://www.rogerblog.cn/2016/07/18/OpenGL-serise-No2/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
Android OpenGL ES 第二章 - 顶点和着色器
本系列所有源码地址:https://github.com/Rogero0o/OpenGL_Demo
上一章我们学习了如何搭建最基本的 OpenGL Android 程序,接下来我们学习顶点和着色器
顶点
在 OpenGL 中,所有的物体都是由顶点集合来构建的,一个顶点就是一个代表几何对象的拐角,有很多附加属性,最重要的属性就是位置,代表了这个顶点在空间中的定位。
这一章我们要学习用顶点来构建一个桌子,将之前的项目复制一份,重命名为 AirHockey1。
我们需要构建的桌子顶点如图:
如何保存这些顶点呢?这些顶点辉表示为一个浮点数列表,一个标记 x 轴位置,一个标记 y 轴位置。在 AirHockeyRenderer 类中,加入以下常量:
private static final int POSITION_COMPONENT_COUNT = 2;
float[] tableVertices = {
0f, 0f,
0f, 14f,
9f, 14f,
9f, 0f
};
这四个点为桌面的四个顶点,然而需要注意的是,在 OpenGL 中,只能绘制点、直线以及三角形!
所以我们要将一个正方形切割为两个三角形:
因此,需要将 tableVertices改为:
float[] tableVerticesWithTriangles = {
// Triangle 1
0f, 0f,
9f, 14f,
0f, 14f,
// Triangle 2
0f, 0f,
9f, 0f,
9f, 14f
}
还有一个需要注意的点是,在定义三角形时,我们总是以逆时针的顺序排列顶点,这称为卷曲顺序。以为在任何地方都使用这种一致的卷曲顺序排列顶点,可以优化性能,可以指出一个三角形输入任何给定物体的前面或者后面, OpenGL 可以忽略那些无论如何都无法被看到的后面的三角形。
完成这些之后,我们已经在 java 层定义好了顶点,然而这个定义并不能被 OpenGL 直接使用,我们需要将这些数据复制到本地堆:
在构造函数的结尾处加入以下代码:
vertexData = ByteBuffer
.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(tableVerticesWithTriangles);
ByteBuffer.allocateDirect 方法分配了一块本地内存,这块内存是不会被垃圾回收机制回收的,参数是分配多少字节的内存块。 order(ByteOrder.nativeOrder()) 告诉字节缓冲区按照本地字节序组织它的内容。最后,调用 asFloatBuffer() 得到一个可以反映底层字节的 FloatBuffer 类实例。再调用 vertexData.put(tableVerticesWithTriangles); 将数据从虚拟机复制到本地内存。
上面有很多专有名词的确是很难懂,但是我们不必纠结于细节,只需要大概了解整个方法的功能和用途即可。
着色器
接下来是着色器部分,这部分是比较难懂的,有说得不对的地方请大家拍砖指正。
任何 OpenGL 程序都需要两种着色器,顶点着色器 ( vertex shader ) 和片段着色器 ( fragment shader ) 。
- 顶点着色器生成每个顶点的最终位置,针对每个顶点,它都会执行一次,一旦最终确定了位置,OpenGL 就可以根据这些顶点的集合组装成点、直线以及三角形。
- 片段着色器为组成点、线、三角形的每个片段生成最终的颜色,针对每个片段,它都会执行一次,一个片段是一个小的、单一颜色的长方形区域,类似于计算机屏幕上的一个像素。一旦最终颜色生成了,OpenGL 就会把它们写到一块称为帧缓冲区的内存块中,然后 Android 会把这个块显示到屏幕上。
这个过程可以简单的归纳为七个阶段:读取顶点数据 -> 执行顶点着色器 -> 组装图元 -> 光栅化图元 -> 执行片段着色器 -> 写入帧缓冲区 -> 显示在屏幕上 。
创建一个顶点着色器
在 res 的 raw 文件夹中创建一个顶点着色器 simple_vertex_shader.glsl 。
attribute vec4 a_Position;
void main()
{
gl_Position = a_Position;
gl_PointSize = 10.0;
}
attribute 是一种 OpenGL 中的类型修饰符,建议到 Link 中了解一下详细。vec4 是包含四个分量的向量,如果没有指定,默认情况下前三个分量都为0,最后一个为1。之后通过 gl_Position 将位置信息传给 OpenGL ,顶点着色器必须对 gl_Position 赋值。 OpenGL 会把这个值作为当前顶点的最终位置,并把这些顶点组装成点、直线和三角形。
创建一个片段着色器
在此之前,你需要知道什么是光栅化:Link
接下来让我们创建一个片段着色器,在 res 的 raw 文件夹中创建一个片段着色器 simple_fragment_shader.glsl 。
precision mediump float;
uniform vec4 u_Color;
void main()
{
gl_FragColor = u_Color;
}
precision 是默认精度修饰符,具体可参考 Link 4.5.3 中的内容。 mediump 表示使用中等精度的值,就像java中选择是使用 float 还是 double 类似。 uniform 在 Link 中有解释,在片段着色器中,我们需要对 gl_FragColor 进行赋值, OpenGL 会使用这个颜色作为当前片段的最终颜色。
这章我们学习了顶点和着色器,这些都是 OpenGL 中的重点难点,后边课程中都是基于顶点和着色器的扩展和细化,所以一些基础知识还是需要反复记忆巩固的。下一章我们将学习如何编译着色器以及在屏幕上显示。