前言属性是每个顶点位置、表面法现和纹理坐标等都需要的而统一值则用于为整个图元批次向保持不变的统一uniform的着色器传递数据。任何着色器变量都可以指定为一个统一值而统一值可以在3个着色器阶段中的任何一个阶段中当前只讨论顶点着色器和片段着色器。声明统一值创建一个统一值非常简单只需在变量声明开始时放置一个uniform关键词。uniformfloatfTime;uniformintiIndex;uniform vec4 vColorValue;uniform mat4 mvpMatrix;统一值不能被标记为in或out它们也不能在着色器阶段之间进行插值虽然可以将它们复制到经过插值的变量中并且它们总是只读的。寻找统一值在一个着色器进行编译和连接之后我们必须在着色器中寻找统一值位置。可以使用glGetUniformLocation函数来定位着色器代码中的统一值位置GLintglGetUniformLocation(GLuint shaderID,constGLchar*varName);这个函数返回一个有符号的整数代表在shaderID指定的着色器中由varName命名的变量的位置。例如为了获取一个名为vColorValue的统一值的位置我们应该进行如下操作GLint iLocationglGetUniformLocation(myShader,“vColorValue”);着色器变量名是区分大小写的如果glGetUniformLocation的返回值是-1就说明统一值的名称在着色器中不能被定位。我们应当牢记即使着色器编译正确如果不是在着色器中直接使用那么一个统一值名称仍然可能在这个着色器中“消失”。如果声明了一个统一值而不去使用它那么编译器将会丢弃它。设置统一值在通过glGetUniformLocation得到着色器中统一值的位置后就可以针对统一值的类型设置各种数据值。设置标量和向量统一值一个单独的标量和向量数据类型可以在glUniform函数中使用下面的变量进行设置。voidglUniform1f(GLint location,GLfloat v0);voidglUniform2f(GLint location,Glfloat v0,GLfloat v1);voidglUniform3f(GLint location,GLfloat v0,GLfloat v1,GLfloat v2);voidglUniform4f(GLint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3);voidglUniform1i(GLint location,GLint v0);voidglUniform2i(GLint location,GLint v0,GLint v1);voidglUniform3i(GLint location,GLint v0,GLint v1,GLint v2);voidglUniform4i(GLint location,GLint v0,GLint v1,GLint v2,GLint v3);例如考虑在一个着色器中声明的4个变量如下所示。uniformfloatfTime;uniformintiIndex;uniform vec4 vColorValue;uniformboolbSomeFlag;为了在着色器中寻找并设置这些值我们的C/C代码可能会像下面这样。GLint locTime,locIndex,locColor,locFlag;locTimeglGetUniformLocation(myShader,“fTime”);locIndexglGetUniformLocation(myShader,“iIndex”);locColorglGetUniformLocation(myShader,“vColorValue”);locFlagglGetUniformLocation(myShader,“bSomeFlag”);…… ……glUseProgram(myShader);glUniform1f(locTime,45.2f);glUniform1i(locIndex,42);glUniform4f(locColor,1.0f,0.0f,0.0f,1.0f);glUniform1i(locFlag,GL_FALSE);注意我们是在使用一个整数版本的glUniform来传递一个bool值。布尔值也可以作为浮点值进行传递0.0代表假1.0则代表真。设置统一数组glUniform函数还接受一个指针假定指向一个数值数组。voidglUniform1fv(GLint location,GLuint count,GLfloat*v);voidglUniform2fv(GLint location,GLuint count,Glfloat*v);voidglUniform3fv(GLint location,GLuint count,GLfloat*v);voidglUniform4fv(GLint location,GLuint count,GLfloat*v);voidglUniform1iv(GLint location,GLuint count,GLint*v);voidglUniform2iv(GLint location,GLuint count,GLint*v);voidglUniform3iv(GLint location,GLuint count,GLint*v);voidglUniform4iv(GLint location,GLuint count,GLint*v);在这里count值代表每个含有x个分量的数组中有多少个元素其中x是位于函数名末尾的数字。例如如果我们有一个4分量的统一值。uniform vec4 vColor;在C/C中我们可以将它表示为一个浮点数组。GLfloat vColor[4]{1.0f,1.0f,1.0f,1.0f};但是这是一个包含4个值的单个数组所以会像下面这样将它传递到着色器中。glUniform4fv(iColorLocation,1,vColor);如果我们在着色器中有一个颜色值数组uniform vec4vColors[2];那么在C/C中我们可以像下面这样表示和传递一个数组。GLfloat vColors[2][4]{{1.0f,1.0f,1.0f,1.0f},{1.0f,0.0f,0.0f,1.0f}};……glUniform4fv(iColorLocation,2,vColors);在最简单的情况下我们可以像下面这样设置一个单个浮点统一值。GLfloat fValue45.2f;glUniform1fv(iLocation,1,fValue);设置统一矩阵最后我们来看一看如何设置一个矩阵统一值。着色器矩阵数据类型只有浮点类型这样变量就减少了很多。下面的函数分别用来加载一个2 x 2、3 x 3和4 x 4矩阵。glUniformMatrix2fv(GLint location,GLuint count,GLboolean transpose,constGLfloat*m);glUniformMatrix3fv(GLint location,GLuint count,GLboolean transpose,constGLfloat*m);glUniformMatrix4fv(GLint location,GLuint count,GLboolean transpose,constGLfloat*m);变量count代表指针参数m中存储的矩阵数量没错我们可以使用矩阵数组。如果矩阵已经按照列优先排序OpenGL推荐的方式进行存储布尔值标记transpose将被设为GL_TRUE。将这个值设置为GL_FALSE会导致这个矩阵在复制到着色器中时发生变换。例如在着色器中声明统一矩阵变量uniform mat4 mvpMatrix;uniform mat4 mvMatrix;uniform mat3 normalMatrix;在C/C代码中获取变量位置并设置矩阵值locMVPglGetUniformLocation(ADSTextureShader,mvpMatrix);locMVglGetUniformLocation(ADSTextureShader,mvMatrix);locNMglGetUniformLocation(ADSTextureShader,normalMatrix);// ...glUniformMatrix4fv(locMVP,1,GL_FALSE,transformPipeline.GetModelViewProjectionMatrix());glUniformMatrix4fv(locMV,1,GL_FALSE,transformPipeline.GetModelViewMatrix());glUniformMatrix3fv(locNM,1,GL_FALSE,transformPipeline.GetNormalMatrix());平面着色器现在让我们来看一个使用统一值的示例着色器。在存储着色器中有一个平面着色器它的功能就是将几何图形进行变换并将其设置为一个单色。它使用的顶点属性只有顶点位置。它还需要使用两个统一值、一个变换矩阵和一个颜值。顶点着色器代码如下#version130uniform mat4 mvpMatrix;in vec4 vVertex;voidmain(){gl_PositionmvpMatrix*vVertex;}可以看到在顶点着色器代码中声明了一个统一值变量变化矩阵然后在主函数中应用变换。片段着色器代码如下#version130uniform vec4 vColorValue;out vec4 vFragColor;voidmain(){vFragColorvColorValue;}和之前一样没有多大变化只是简单的颜色传递。C/C代码如下#includeflatshader/flatshader.h#includeGLTools.h#includeGLMatrixStack.h#includeGLFrame.h#includeGLFrustum.h#includeGLGeometryTransform.h#includeStopWatch.h#includemath.h#ifndefFREEGLUT_STATIC#defineFREEGLUT_STATIC#endif#includeGL/glut.hnamespaceflatshader{GLFrame viewFrame;GLFrustum viewFrustum;GLTriangleBatch torusBatch;GLMatrixStack modelViewMatrix;GLMatrixStack projectionMatrix;GLGeometryTransform transformPipeline;GLuint flatShader;// 自定义的平面着色器GLint locMVP;// 模型视图投影矩阵常量的位置GLint locColor;// 颜色值均匀分布的位置voidSetupRC(){glClearColor(0.0f,0.0f,0.0f,1.0f);glEnable(GL_DEPTH_TEST);glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);viewFrame.MoveForward(4.0f);gltMakeTorus(torusBatch,0.80f,0.25f,52,26);flatShadergltLoadShaderPairWithAttributes(./src/chapter_6/rewrite/flatshader/flatshader.vp,./src/chapter_6/rewrite/flatshader/flatshader.fp,1,GLT_ATTRIBUTE_VERTEX,vVertex);locMVPglGetUniformLocation(flatShader,mvpMatrix);locColorglGetUniformLocation(flatShader,vColorValue);}voidShutdownRC(){}voidRenderScene(){staticCStopWatch rotTimer;glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);modelViewMatrix.PushMatrix(viewFrame);{modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds()*10.f,0.0f,1.0f,0.0f);GLfloat vColor[]{0.1f,0.1f,1.0f,1.0f};glUseProgram(flatShader);glUniform4fv(locColor,1,vColor);glUniformMatrix4fv(locMVP,1,GL_FALSE,transformPipeline.GetModelViewProjectionMatrix());}torusBatch.Draw();modelViewMatrix.PopMatrix();glutSwapBuffers();glutPostRedisplay();}voidChangeSize(intw,inth){if(h0)h1;glViewport(0,0,w,h);viewFrustum.SetPerspective(35.0f,float(w)/float(h),1.0f,1000.0f);projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());transformPipeline.SetMatrixStacks(modelViewMatrix,projectionMatrix);}intflatshader_test(intargc,char*argv[]){gltSetWorkingDirectory(argv[0]);glutInit(argc,argv);glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);glutInitWindowSize(800,600);glutCreateWindow(Simple Transformed Geometry);glutReshapeFunc(ChangeSize);glutDisplayFunc(RenderScene);GLenum errglewInit();if(GLEW_OK!err){fprintf(stderr,GLEW Error: %s\n,glewGetErrorString(err));return1;}SetupRC();glutMainLoop();ShutdownRC();return0;}}在RenderScene中图形颜色的统一值和变换矩阵的统一值将在花托对象上调用Draw函数之前进行设置。最终效果如下