Android OpenGL ES 2.0 手把手教学(4)- 片段着色器 fragment shader

3,822 阅读2分钟

大家好,下面和大学一起学习如何使用片段着色器fragment shader来渲染丰富多彩的颜色,在我的github上有一个项目OpenGLES2.0SamplesForAndroid,我会不断地编写学习样例,文章和代码同步更新,欢迎关注,链接:github.com/kenneycode/…

在上一篇文章中,我们介绍了渲染管线,下面来回顾一下fragment shader在渲染管线中的位置:

fragment shader会在光栅化后,对每个像素执行一次,先看一下我们之前的例子中使用的fragment shader:

precision mediump float;
void main() {
    gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
}

在一开始声明了float的精度为中精度,接着和vertex shader一样有一个main()方法作为入口,fragment shader中有一个内置变量gl_FragColor表示fragment shader的输出,在之前的例子我们给gl_FragColor设置了一个固定值vec4(0.0, 0.0, 1.0, 1.0),这是一个RGBA的颜色值,因此我们之前看到的三角形是一个纯蓝色的,现在,我们来实现一个彩色的三角形,这需要vertex shaderfragment shader同时修改,来看看我们修改后的shader

// vertex shader
precision mediump float;
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
    v_Color = a_Color;
    gl_Position = a_Position;
}
// fragment shader
precision mediump float;
varying vec4 v_Color;
void main() {
    gl_FragColor = v_Color;
}

修改后,gl_FragColor不再是一个固定值,而是一个类型为varying的颜色值v_Colorvarying类型变量是一种经过插值后得到的变量,我们可以看到vertex shader中也声明了同样的varying vec4 v_Color,然后将a_Color赋给v_Colorfragment shader中的v_Color就是通过vertex shader传递过来的,而a_Colora_Position一样是attribute类型。

上篇文章提到过,vertex shader会对每个顶点都执行一次,而fragment shader会对每个像素执行一次,那么fragment shader中拿到的v_Color究竟是什么值?它是经过插值的值,这个如何理解呢?先来看一下我们这个例子中传递的值:

// 颜色数据
// The color data
private val colorData = floatArrayOf(
                                    1.0f, 0.0f, 0.0f, 1.0f,
                                    0.0f, 1.0f, 0.0f, 1.0f,
                                    0.0f, 0.0f, 1.0f, 1.0f)
// 每个颜色的成份数(RGBA)
// The num of components of per color(RGBA)
private val COLOR_COMPONENT_COUNT = 4
    
// 将三角形顶点数据放入buffer中
// Put the triangle vertex data into the buffer
val colorDataBuffer = ByteBuffer.allocateDirect(colorData.size * java.lang.Float.SIZE)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
colorDataBuffer.put(colorData)
colorDataBuffer.position(0)

// 获取字段a_Color在shader中的位置
// Get the location of a_Color in the shader
val aColorLocation = GLES20.glGetAttribLocation(programId, "a_Color")

// 启动对应位置的参数
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(aColorLocation)

// 指定a_Color所使用的顶点数据
// Specify the vertex data of a_Color
GLES20.glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT, GLES20.GL_FLOAT, false,0, colorDataBuffer)

我们以与传递三角形顶点给a_Position相同的方式,将三个颜色值(1.0f, 0.0f, 0.0f, 1.0f)(0.0f, 1.0f, 0.0f, 1.0f)(0.0f, 0.0f, 1.0f, 1.0f)传递给a_Color,这时在vertex shader执行时会同时取到顶点和颜色数据的同一位置的数据,将这三个颜色值传递给v_Color后,在fragment shader中的v_Color就是经过这三个颜色值插值后的值,例如越靠近颜色(1.0f, 0.0f, 0.0f, 1.0f)对应的点的地方,颜色就越接近(1.0f, 0.0f, 0.0f, 1.0f),我们来看下效果:

代码在我github的OpenGLES2.0SamplesForAndroid项目中,本文对应的是SampleFragmentShader,项目链接:github.com/kenneycode/…

感谢阅读!