一.平滑着色
我们已经知道,在OpenGL中,我们只能画点,直线和三角形,并且所有物体都是以他们为基础构建的。既然受限于这三个基本图元,那么我们如何用许多不同的颜色和着色表达更复杂的场景呢?我们能使用的一个方法就是使用上百万个小三角形,每个三角形的颜色都不同,这样就可以看到一副美丽,复杂,有丰富颜色变化的场景。尽管,这在技术上是可行的,但性能和内存的开销是十分庞大的。所以,OpenGL提供了另外一种技术,平滑着色。举例来说,就是有一个三角形,每个顶点的颜色都是不同的,我们可以在三角形表面混合这些颜色,最终得到一个平滑着色的三角形。我们要使用这种类型的着色让桌子中央更加明亮,而桌子的边缘显得比较暗淡。
在实现这个效果前,我们先得更新桌子的结构,我们需要在桌子中间加入一个点,这样就可以在桌子的中间和边缘之间混合颜色,更新后的桌子结构如下图所示:
为了更好的理解平滑着色,我这里举个例子:比如,有一条直线,一个顶点A是红色的,另一个顶点B是绿色的,那么使用平滑着色后的效果就是,越靠近顶点A颜色越红,越靠近顶点B颜色越绿,也就是呈现出从红色从绿色逐渐过渡的效果。
二.引入三角形扇
想要画出这个结构,我们需要引入三角形扇结构。一个三角形扇会以一个中心顶点作为开始,使用相邻的两个顶点创建第一个三角形,接下来每加入一个顶点,就会创建一个新的三角形,围绕中心的顶点按扇形展开,为了使这个扇形闭合,我们只需要在最后重复第二个点。三角形扇结构如下图所示:
接下来,我们需要修改代码,在上一篇文章的基础上。我们首先需要更新顶点数组结构,修改如下:
val tableVertices=floatArrayOf(
//Triangle fan
0f,0f,
-0.5f,-0.5f,
0.5f,-0.5f,
0.5f,0.5f,
-0.5f,0.5f,
-0.5f,-0.5f,
//Mid Line
-0.5f,0f,
0.5f,0f,
//Mallets
0f,-0.25f,
0f,0.25f,
)
然后,在onDrawFrame函数中,将绘制矩形的代码修改成:glDrawArrays(GL_TRIANGLE_FAN,0,6)
运行程序,看看效果。
三.增加一个新的颜色属性
通过给桌子加入一个中心点,我们已经更新了桌子的结构。接下来,我们给每个顶点加入第二个属性,颜色属性,代码修改如下:
val tableVertices=floatArrayOf(
//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,
//Mid Line
-0.5f,0f,1f,0f,0f,
0.5f,0f,1f,0f,0f,
//Mallets
0f,-0.25f,0f,0f,1f,
0f,0.25f,1f,0f,0f
)
接下来,我们需要修改顶点着色器代码,修改后的代码如下所示:
#version 300 es
layout(location=0) in vec4 a_Position;
layout(location=1) in vec4 a_Color;
out vec4 v_Color;
void main() {
gl_Position=a_Position;
v_Color=a_Color;
gl_PointSize=10.0;
}
片段着色器也需要修改,修改后的代码如下:
#version 300 es
precision mediump float;
in vec4 v_Color;
out vec4 fragColor;
void main() {
fragColor=v_Color;
}
我们在顶点着色器中加入了一个a_Color变量用于接收顶点的颜色属性,然后再借助out关键字将数据从顶点着色器传输到片段着色器。
接下来,我们需要更新一些常量,首先加入一个常量用于记录颜色属性的分量个数,然后再加入一个变量用于存储一个顶点的位置分量和颜色分量总共占据的字节数,因为我们现在有两个属性,知道占据的总字节数,我们就能找到第二个顶点的首地址。修改如下:
companion object{
private val POSITION_COMPONENT_COUNT=2//记录一个顶点有两个分量
private val COLOR_COMPONENT_COUNT=3//记录颜色用三个分量表示
private val BYTES_PER_FLOAT=4//每个浮点数4个字节
private val STRIDE=(POSITION_COMPONENT_COUNT+ COLOR_COMPONENT_COUNT)* BYTES_PER_FLOAT//记录一个顶点的位置属性和颜色属性占有的总字节数
}
更新onSurfaceCreated()函数,加入STRIDE这个参数,让OpenGL知道下一个顶点的位置该怎么找,主要修改下面这行代码:
//关联属性和顶点数据的数组
glVertexAttribPointer(0, POSITION_COMPONENT_COUNT, GL_FLOAT,false,STRIDE,vertexData)
然后,加入代码,将顶点数组中的颜色属性传给顶点着色器中的a_Color变量,在onSurfaceCreated函数末尾加入如下代码:
//给顶点着色器传入颜色属性
vertexData.position(POSITION_COMPONENT_COUNT)
glVertexAttribPointer(1, COLOR_COMPONENT_COUNT, GL_FLOAT,false,STRIDE,vertexData)
glEnableVertexAttribArray(1)
最后,修改onDrawFrame()函数中的代码,将给uniform变量传输颜色属性的代码都删除,修改后的代码如下:
override fun onDrawFrame(p0: GL10?) {
glClear(GL_COLOR_BUFFER_BIT)//清除帧缓冲区内容,和glClearColor一起使用
//绘制矩形
glDrawArrays(GL_TRIANGLE_FAN,0,6)
//绘制分割线
glDrawArrays(GL_LINES,6,2)
//绘制木槌,用点表示
glDrawArrays(GL_POINTS,8,1)
glDrawArrays(GL_POINTS,9,1)
}
四.运行程序,查看最终效果
可以看到,由于我们定义了中心点的颜色为纯白,所以越靠近中心点越显白,边缘位置显得暗淡些。