GLSL的Hello World
这一节中包含一个最基本的shader,它提供如下功能:顶点变换然后使用单一的颜色渲染图元。
顶点shader
前面已经说过,顶点shader负责完成顶点变换。这里将按照固定功能的方程完成顶点变换。
固定功能流水线中一个顶点通过模型视图矩阵以及投影矩阵进行变换,使用如下公式:
view plain copy to clipboard print ?- vTrans
= projection * modelview *incomingVertex
vTrans = projection * modelview *incomingVertex首先GLSL需要访问OpenGL状态,获得公式中的前两个矩阵。前面讲过,GLSL可以获取某些OpenGL状态信息的,这两个矩阵当然包括在内。可以通过预先定义的一致变量来获取它们:
view plain copy to clipboard print ?- uniform
mat4 gl_ModelViewMatrix; - uniform
mat4 gl_ProjectionMatrix;
uniform mat4 gl_ModelViewMatrix; uniform mat4 gl_ProjectionMatrix;接下来需要得到输入的顶点。通过预先定义的属性变量,所有的顶点将可以一个个传入顶点shader中。
view plain copy to clipboard print ?- attribute
vec4 gl_Vertex;
attribute vec4 gl_Vertex;为了输出变换后的顶点,shader必须写入预先定义的vec4型变量gl_Position中,注意这个变量没有修饰符。
现在我们可以写一个仅仅进行顶点变换的顶点shader了。注意所有其他功能都将丧失,比如没有光照计算。顶点shader必须有一个main函数,如下面的代码所示:
view plain copy to clipboard print ?- void
main() - {
-
gl_Position =gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; - }
void main(){ gl_Position =gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; }上面代码中变换每个顶点时,投影矩阵都将乘上模型视图矩阵,这显然非常浪费时间,因为这些矩阵不是随每个顶点变化的。注意这些矩阵是一致变量。
GLSL提供一些派生的矩阵,也就是说gl_ModelViewProjectionMatri
- void
main() - {
-
gl_Position =gl_ModelViewProjectionMatri x * gl_Vertex; - }
void main(){ gl_Position =gl_ModelViewProjectionMatri
- vec4
ftransform(void);
vec4 ftransform(void);使用这个函数的另一个原因是float数据类型的精度限制。由于数据精度的限制,当使用不同的顺序计算时,可能得到不同的结果,因此GLSL提供这个函数保证获得最佳性能的同时,还能得到与固定功能流水线相同的结果。
这个函数按照与固定功能相同的步骤对输入顶点进行变换,然后返回变换后的顶点。所以shader可以重新写成如下形式:
view plain copy to clipboard print ?- void
main() - {
-
gl_Position =ftransform(); - }
void main(){ gl_Position =ftransform(); }片断shader
片断shader也有预先定义的变量gl_FragColor,可以向其中写入片断的颜色值。下面的代码就是一个片断shader,将所有片断绘制成淡蓝色:
view plain copy to clipboard print ?- void
main() - {
-
gl_FragColor =vec4(0.4,0.4,0.8,1.0); - }
void main(){ gl_FragColor =vec4(0.4,0.4,0.8,1.0); }可以在此获得本节例子的源码:
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl5_2.0.zip
颜色shader
GLSL可以读取一些OpenGL状态,在本节我们将学习如何访问在OpenGL中设置的glColor变量。
GLSL有一个属性变量记录当前颜色,也提供易变变量从顶点shader向片断shader传递颜色值。
view plain copy to clipboard print ?- attribute
vec4 gl_Color; - varying
vec4 gl_FrontColor; // writable onthe vertex shader - varying
vec4 gl_BackColor; // writable onthe vertex shader - varying
vec4 gl_Color; // readable on thefragment shader
attribute vec4 gl_Color; varying vec4 gl_FrontColor; // writable onthe vertex shader varying vec4 gl_BackColor; // writable onthe vertex shader varying vec4 gl_Color; // readable on thefragment shader变量使用思想如下:
1、OpenGL程序通过glColor传送颜色信息。
2、顶点shader通过属性gl_Color接收颜色值。
3、顶点shader计算正面和反面的颜色,然后分别保存在gl_FrontColor和gl_BackColor中。
4、片断shader接收易变变量gl_Color中存储的插值产生的颜色,由当前图元的方向决定颜色是gl_FrontColor还是gl_BackColor插值产生的。
5、片断shader根据易变变量gl_Color设置gl_FragColor。
前 面说过顶点shader和片断shader中传递的易变变量要有相同的名字,但这里是个例外,顶点shader中的gl_FrontColor和 gl_BackColor会根据图元的方向,自动转变为片断shader中的gl_Color。还要注意属性变量gl_Color和易变变量 gl_Color没有冲突,因为前者只存在于顶点shader,后者只存在于片断shader。
下面是顶点shader的例子,只计算了正面颜色:
view plain copy to clipboard print ?- void
main() - {
-
gl_FrontColor =gl_Color; -
gl_Position =ftransform(); - }
void main(){ gl_FrontColor =gl_Color; gl_Position =ftransform();}
片断shader更加简单:
- void
main() - {
-
gl_FragColor = gl_Color; - }
void main(){ gl_FragColor = gl_Color; }基于GLEW的源代码:
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/colorglut_2.0.zip
扁平shader(Flatten Shader)
着色器编程让我们可以探索一些新效果,本节的例子展示了用奇怪的方法操作顶点得到的效果。
首先我们要得到一个扁平的3D模型,只需要在应用模型视图变换时将模型顶点的z坐标设为0就行了。下面是顶点shader的代码:
view plain copy to clipboard print ?- void
main(void) - {
-
vec4 v = vec4(gl_Vertex); -
v.z = 0.0; -
-
gl_Position =gl_ModelViewProjectionMatri x * v; - }
void main(void){ vec4 v = vec4(gl_Vertex); v.z = 0.0; gl_Position =gl_ModelViewProjectionMatri
片断shader与“GLSL中的Hello World”一节相同,就只用设置一种颜色。
一个扁平的茶壶效果如下:
更进一步,我们需要在z坐标上使用一个正弦函数。将z坐标作为x坐标的函数,这样茶杯将呈现波浪的效果:
view plain copy to clipboard print ?- void
main(void) - {
-
vec4 v =vec4(gl_Vertex); -
v.z = sin(5.0*v.x)*0.25; -
gl_Position =gl_ModelViewProjectionMatri x * v; - }
void main(void){ vec4 v =vec4(gl_Vertex); v.z = sin(5.0*v.x)*0.25; gl_Position =gl_ModelViewProjectionMatri
顶点shader的代码如下:
view plain copy to clipboard print ?- uniform
float time; -
- void
main(void) - {
-
vec4 v =vec4(gl_Vertex); -
v.z = sin(5.0*v.x +time*0.01)*0.25; -
gl_Position =gl_ModelViewProjectionMatri x * v; - }
uniform float time; void main(void){ vec4 v =vec4(gl_Vertex); v.z = sin(5.0*v.x +time*0.01)*0.25; gl_Position =gl_ModelViewProjectionMatri
?setup: 获取一致变量的存储位置
?render: 更新一致变量
设置(setup)步骤只有一条语句:
view plain copy to clipboard print ?- loc
=glGetUniformLocation(p,"time");
loc =glGetUniformLocation(p,"time");这里p是程序的句柄,time与顶点shader中定义的一致变量名称相同。变量loc是Glint类型的,必须定义在下面的渲染(render)函数也可以访问到的地方。渲染函数如下所示:
view plain copy to clipboard print ?- void
renderScene(void) - {
-
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); -
glLoadIdentity(); -
gluLookAt(0.0,0.0,5.0, -
0.0,0.0,0.0, -
0.0f,1.0f,0.0f); -
glUniform1f(loc, time); -
-
glutSolidTeapot(1); -
time+=0.01; -
glutSwapBuffers(); - }
void renderScene(void){ glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(0.0,0.0,5.0, 0.0,0.0,0.0, 0.0f,1.0f,0.0f); glUniform1f(loc, time); glutSolidTeapot(1); time+=0.01; glutSwapBuffers(); }函数中的变量time在程序一开始初始化,然后每帧都会进行自增运算。
本节的GLEW源代码:
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/flatten_2.0.zip