首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南(Android卷)》的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载。
《Android学习笔记——OpenGL ES的基本用法、绘制流程与着色器编译》中实现了OpenGL ES的Android版HelloWorld,并且阐明了OpenGL ES的绘制流程,以及编译着色器的流程及注意事项。本文将从现实世界中图形显示的角度,说明OpenGL ES如何使得图像在移动设备上显示的更加真实。首先,物体有各种颜色的变化,在OpenGL ES中为了生成比较真实的图像,对图像进行平滑着色是一种常见的操作。其次,移动设备存在横竖屏的切换,进行图像显示时,需要根据屏幕方向考虑屏幕的宽高比,使图像不因屏幕切换而变形。最后,现实中的物体都是三维的,我们观察物体都带有一定的视角,因此需要在OpenGL ES实现三维图像的显示。本文主要包括以下内容:
- 平滑着色
- 自适应宽高
- 三维图像生成
一、平滑着色
平滑着色是通过在三角形的每个点上定义不同的颜色,在三角形的表面混合这些颜色得到的。那么,如何用三角形构成实际物体的表面呢?如何混合定义在顶点出的不同颜色呢?
首先引入三角形扇的概念。以一个中心顶点作为起始,使用相邻的两个顶点创建第一个三角形,接下来的每个顶点都会创建一个三角形,围绕起始的中心点按扇形展开。为了使扇形闭合,只需在最后重复第二个点。在OpenGL中通过GL_TRIANGLE_FAN指定数据代表三角形扇。
glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
上述代码中,glDrawArrays的参数列表为:
// C function void glDrawArrays ( GLenum mode, GLint first, GLsizei count )
public static native void glDrawArrays(
int mode,
int first,
int count
);
可知,0代表第一顶点的位置,6表示6个顶点绘制一个三角形扇。
接下来会把每个点上的颜色定义为一个顶点属性,需要两部分的工作:(1)顶点数据;(2)着色器。《Android学习笔记——OpenGL ES的基本用法、绘制流程与着色器编译》中涉及到的顶点数据只有X/Y坐标,添加颜色属性,则在顶点坐标后增加了R/G/B值。具体格式如下:
float[] tableVerticesWithTriangles = {
// Order of coordinates: X, Y, R, G, B
// Triangle Fan
0f, 0f, 1f, 1f, 1f,
-0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
0.5f, 0.5f, 0.7f, 0.7f, 0.7f,
-0.5f, 0.5f, 0.7f, 0.7f, 0.7f,
-0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
};
同样的,相比于上一篇中涉及到的顶点着色器,增加颜色属性。
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main()
{
v_Color = a_Color;
gl_Position = a_Position;
gl_PointSize = 10.0;
}
这里需要说明的是varying变量,varying变量是平滑的关键。以直线AB为例,如果顶点A的a_Color是红色,顶点B的a_Color是绿色,那么,从A到B,将会是红色和绿色的混合。越接近顶点A,混合后的颜色显得越红;越接近顶点B,混合后的颜色就显示越绿。至于混合的算法,采用最基本的线性插值就可以完成。
在三角形表面混合时,与直线的线程插值相同,每个颜色在接近它的顶点处都是最强的,向其他顶点就会变暗,用比例确定每种颜色的相对权重,只是这里使用的是面积的比例,而不是线性插值所使用的长度。
回到AirHockeyRenderer中,首先在onSurfaceCreated体现颜色属性。
aPositionLocation = glGetAttribLocation(program, A_POSITION);
aColorLocation = glGetAttribLocation(program, A_COLOR);
// Bind our data, specified by the variable vertexData, to the vertex
// attribute at location A_POSITION_LOCATION.
vertexData.position(0);
glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,
false, STRIDE, vertexData);
glEnableVertexAttribArray(aPositionLocation);
// Bind our data, specified by the variable vertexData, to the vertex
// attribute at location A_COLOR_LOCATION.
vertexData.position(POSITION_COMPONENT_COUNT);
glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT, GL_FLOAT,
false, STRIDE, vertexData);
glEnableVertexAttribArray(aColorLocation);
流程与《Android学习笔记——OpenGL ES的基本用法、绘制流程与着色器编译》中基本一致,只是添加了颜色属性。aColorLocation为颜色属性的位置,STRIDE为跨距,即tableVerticesWithTriangles数组中不仅包含顶点的坐标,还包含了颜色属性,因此在取顶点坐标时,需要跨越颜色属性。
vertexData.position(POSITION_COMPONENT_