在第三章开始,每个源码示例几乎都使用了一些预建的存储着色器,这些存执着色器执行一些例行程序和典型的渲染操作。本章,将开始学习如何编写自己的着色器,即服务器端的着色器应用:着色器编程和着色语言。
我们至少需要两个着色器:一个顶点着色器、一个片段着色器。还有一种可选的着色器成为几何着色器,在第11章学习。我们可以以3种方式中选择一种向顶点着色器传递数据:一是参数,是对每个顶点而言的;二是统一值,是针对整个顶点数据批次的常亮(所以是一致的);最后是加载和使用纹理数据。
将顶点属性发送到片段着色器毫无意义,因为片段着色器只是用来在图元进行光栅化后对片段(最基本的是像素)进行填充。但是,每个顶点数据都可以通过顶点程序传递到片段着色器。但是在这种情况下,这些数据可能是常亮(每个片段的都是同样的值),或者这些值也可以用不同的方式在图元表面进行插值。
可用的数据类型只有4种:整数(包括有符号和无符号整数)、浮点型(单精度float)和布尔型(bool)。没有指针、字符串或字符。函数可以返回任何数据类型中的一种,但是不允许是void指针。其应用与他们在C/C++中一样。
OpenGL着色语言的另一个独特的性质是对一个向量的独立元素进行寻址的方式。与C/C++中的union非常类似。
使用点号来确定多达4个向量元素的地址,但也可以使用下列3组标识符中的任意一组:xyzw、rgba或stpq。
vVertexPos.x = 3.0f; vVertexPos.xy = vec2(3.0f,5.0f);vVertexPos.xyz = vNewPos.xyz;
vColor.r = 1.0f;vColor.rgba = vec4(1.0f,1.0f,0.5f,1.0f);
vTexCoord.st = vec2(1.0f,0.0f);.........
实际上在OpenGL着色语言中,一个矩阵就是一个由向量组成的数组——列向量。例如:
mModelView[3]=vec4(0.0f,0.0f,0.0f,1.0f);
vec4 vTranslation = mModelView[3];或者vec4 vTranslation = mModelView[3].xyz;
矩阵类型也有自己的构造函数,例如下面是创建单位矩阵:
vFragColor = vVaryingColor;//对片段进行颜色插值
} vFragColor是最终输出的颜色。每个着色器的第一行非命令行都是指定版本。#version 330,指定这个着色器要求的OpenGL着色语言的最低版本是3.3。
加载和初始化着色器的过程概述:
1)指定属性,就是附件参数(....)的值。 2)设置源代码:创建两个着色器对象。 3)编译着色器,编译器由硬件提供提供。 4)进行连接和绑定。5)连接着色器
例如: gltLoadShaderPairWithAttributs(“vertexProg.vp”,“fragmentPrg.vp”,2,GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");前两个char类型指针指向顶点和片段程序的文件,2代表顶点程序包含属性的数量;之后是一一对应的属性索引和属性名;函数详细学习可以查看GLTools源码。在后面学习源码示例ShaderTriangle.cpp时再理解。
这样就将着色器设置为活动的,现在顶点着色器和片段着色器会处理所有提交的几何图形。在提交顶点属性之前,要对Uniform值和纹理进行设置。
属性是每个顶点位置、表面法线和纹理坐标等都需要的,而统一值则用于为整个图元批次向保持不变的(统一(uniform)的)着色器传递数据。现在我们要开始编写自己的着色器,就需要能够设置自己的统一值,而不只是矩阵值了。例如:
uinform float fTime; uniform int iIndex; uinform vec4 vColorValue; uinform mat4 mvpMatrix;
统一值不能被标记为in或out,它们也不能在着色器阶段之间进行插值,而且它们总是只读的。
在一个着色器进行编译和连接之后,我们必须在着色器中寻找统一值位置。
可以从加载的着色器中获取一个名为vColorValue的统一值的位置:
着色器变量名是区分大小写的,如果glGetUniformLocation的返回值是-1.就说明统一值的名称在着色器中不能被定为。
通常寻找统一值和设置统一值一起使用,例如:
location是寻找的统一值变量,count值代表每个含有x个分量的数组有多少个元素,v是指向数组的指针,x是位于函数名末尾的数字
例如:
1)GLFloat vColor[4]={1.0f,1.0f,1.0f,1.0f}是一个包含4个值的单个数组,调用:
glUniform4fv(iColorLocation,1,vColors);
2)GLFloat vColor[2][4]={{1.0f,1.0f,1.0f,1.0f},{1.0f,1.0f,1.0f,1.0f}};代表含有4分量的数组有2个元素
glUniform4fv(iColorLocation,2,vColors);
3)GLFloat fValue = 45.2f;
glUniform1fv(iColorLocation,1,&fValue);
变量count代表指针参数m中存储的矩阵数量。如果矩阵已经按照列优先排序进行存储,布尔值标记transpose将被设置为GL_TURE。将这个值设置为GL_FALSE会导致这个矩阵在复制到着色器中时发生变换。
在学习了着色器的基本概念、渲染方式和一些基本函数之后,后面通过源码示例进一步理解着色器和着色语言。我们要编写自己的顶点和片段程序,加载程序片段渲染绘制的图形,所以这节的学习作为后面源码学习的基础。后面要学习顶点和片段着色器即服务端的程序,也要巩固学习客户端的应用程序代码(C/C++),两者结合起来。