上次教程关于光照,这次内容会比较复杂,关于着色器(Shader),GLSL(OpenGL Shading Language)。对于初次接触图形学的童鞋,可能比较晦涩。并且用Mac开发的同学注意,本教程中有关Shader内容,环境均为win下VS,但这并不代表Mac不支持Shader,只是需要配置一些环境,Google可以百度一下。
- 着色器(Shader)
6.1 着色器分类
还记得第一讲中的管线图吗?有个词叫光栅化,记得吗?在光栅化之前都是对顶点进行变换,顶点光照和投影变换等,这些都是对顶点进行操作,其对应的着色器也叫做顶点着色器(Vertex Shader)。光栅化之后,进行片元处理,纹理映射等都是对片元的操作,对应的着色器也叫片元着色器(Fragment Shader)。当然也会有别的着色器,比如细分着色器,计算着色器等,有兴趣可以了解。
6.2 GLSL概述
首先介绍一下GLSL的变量类型。GLSL中,有四种类型参数:顶点属性(attribute),一致变量(uniform),还有一种有人称为易变变量(varying),但是这里我们将它称为out/in变量。varing是很早之前的版本,现在大部分都改为out/in了。最后一个是常数变量(const)。
顶点属性(attribute),是只能在vertex shader中使用的变量,用来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。
一致变量(uniform),是外部application程序传递给(vertex和fragment)shader的变量,可以理解为全局变量,并且是只读的属性,不能通过shader来改变其值。
易变变量(out/in),被从一个着色器传递至下一个着色器,相当于模块之间的接口。在我们上述提到的情况里,out变量来自于vertex shader,然后到fragment shader变为in变量。当然,顶点属性(attribute)变量可以当做vertex shader的in变量。
常数变量(const),这个就不用多做介绍了吧,和你理解的常量是一个意思。
了解了变量种类,来看看GLSL语言的风格。其实GLSL某种意义上更像是C语言为图形学的一种扩展语言。比如也有 int, ivec2, ivec3, ivec4,float, vec2, vec3, vec4 ,bool, bvec2, bvec3, bvec4等类型。vector的元素可以通过"."来获取,比如c1.rgba= c2.abgr 。当然也有不一样的部分,比如没有string类型,没有枚举enums,当然更没有指针,并且数组也只能用一维数组。
这里介绍一个GLSL的操作discard,片元丢弃。这是用在片元着色器进行丢弃当前片元的,效果如下:
我们刚刚提到还有其他类型的shader,比如细分着色器(Tessellation Shader),几何着色器(Geometry Shader)等,但我们的材料只会提及顶点和片元着色器,其他的不会涉及,之后有时间可能会做一组专门关于shader的教程,那时候会设计这些着色器。我们看下shaders的流程图吧:
接下里进入正题,首先从顶点着色器开始。
6.3 着色器示例代码
顶点着色器(Vertex Shader),会进行顶点变换,法线变换,法线归一化,计算顶点光照,以及得到顶点的纹理坐标并将其传到片元着色器。
内置的Vertex Shader变量主要用到的有:
vec4 gl_Vertex
vec3 gl_Normal
vec4 gl_Color
vec4 gl_MultiTexCoord0
mat4 gl_ModelViewMatrix
mat4 gl_ProjectionMatrix
mat4 gl_ModelViewProjectionMatrix
mat4 gl_NormalMatrix (this is the transpose of the inverse of the MV matrix)
vec4 gl_Position
片元着色器(Fragment Shader),会进行颜色的计算,纹理映射,处理片元光照操作,还有颜色的融合以及片元丢弃等操作。
内置的Fragment Shader变量主要用到的有:
vec4 gl_FragColor
这样就可以写出一个最简单的vertex shader和fragment shader:
在vertex shader中有gl_ModelViewProjectionMatrix gl_Vertex这么一句话,其实可以分开,写成gl_ProjectionMatrix gl_ModelViewMatrix gl_Vertex,但是变换每个顶点时,投影矩阵都将乘上模型视图矩阵,这显然非常浪费时间,因为这些矩阵不是随每个顶点变化的。所以GLSL提供一些派生的矩阵,也就是说gl_ModelViewProjectionMatrix是上面两个矩阵的乘积,所以便写成gl_ModelViewProjectionMatrix gl_Vertex。