OpenGL进阶(十一):GLSL4.x中的数据传递

发表于2017-09-12
评论0 2.1k浏览

in out

       对于 vertex shader,每个顶点都会包含一次,它的主要工作时处理关于定点的数据,然后把结果传递到管线的下个阶段。

       以前版本的GLSL,数据会通过一些内建变量,比如gl_Vertex和gl_Normal,但现在,通常时使用通用顶点属性( generic vertex attributes)来提供,通常和一个Buffer object 想关联。对于程序员来说,现在可以自由去定义一些顶点的属性集来提供输入,只要在开头的时候用in关键字来声明就可以了。

       还有一种方式就是使用uniform variables。这种变量和属性变量的区别:属性变量是指每个顶点shader调用时,都会根据属性的位置从顶点缓冲中装入该顶点的相应属性值,而uniform变量,则对每个draw调用保持不变,这意味着你在draw调用前装入该变量,然后draw中每个顶点shader执行时,都能访问该变量,而且该变量值会保持不变。它可以声明在一个或者多个shader中,如果时声明在多个shader中,变量的类型必须一致。uniform变量常用来存储一些draw执行时候的常量数据,比如光照参数、变化矩阵、纹理对象句柄等等。

       下面是基于GLSL入门的例子的一个修改,通过增加一个uniform的旋转变量,对每个顶点进行旋转一定的角度。

       首先是basic.vert:

  1. #version 400  
  2.   
  3. layout (location = 0) in  vec2 in_Position;  
  4. layout (location = 1) in  vec3 in_Color;  
  5. out vec3 ex_Color;  
  6. uniform mat4 RotationMatrix;  
  7.   
  8. void main(void) {  
  9.    
  10.     gl_Position = RotationMatrix * vec4(in_Position.x, in_Position.y, 0.0, 1.0);  
  11.     ex_Color = in_Color;  
  12. }  

增加了uniform的4维矩阵变量,存储旋转矩阵。

在main.cpp中修改如下:

首先添加一下头文件,因为要用到glm库。

  1. #include   
  2. #include   
  3. using glm::mat4;  
  4. using glm::vec3;  

然后在renderGL中修改代码如下:
  1. glUseProgram(programHandle);  
  2.     float angle = 30;  
  3.     mat4 rotationMatrix = glm::rotate(mat4(1.0f), angle, vec3(0.0f,0.0f,1.0f));  
  4.     GLuint location =glGetUniformLocationg(programHandle,"RotationMatrix");  
  5.       
  6.     if( location >= 0 )  
  7.     {  
  8.         glUniformMatrix4fv(location, 1, GL_FALSE,&rotationMatrix[0][0]);  
  9.     }  
  10.   
  11.     //Draw a square  
  12.     int i;  
  13.     for (i=2; i <=4; i )  
  14.     {  
  15.         /* Make our background black */  
  16.         glClearColor(0.0, 0.0, 0.0, 1.0);  
  17.         glClear(GL_COLOR_BUFFER_BIT);  
  18.         /* Invoke glDrawArrays telling that our data is a line loop and we want to draw 2-4 vertexes */  
  19.         glDrawArrays(GL_TRIANGLE_FAN, 0, i);  
  20.     }  
  21.    
  22.     // Unbind shader  
  23.     glUseProgram(0);  

       修改的部分首先是生成选装的矩阵,glGetUniformLocation用于检测是否存在一个变量,然后通过glUniformMatrix4fv来绑定数值,最后在绘制的时候,shader就可以调用uniform数据了。



使用uniform blocks和uniform buffer object

       UBO,顾名思义,就是一个装载Uniform变量数据的Buffer Object。就概念而言,它跟VBO之类Buffer Object差不多,反正就是显存中一块用于储存特定数据的区域了。在OpenGL端,它的创建、更新、销毁的方式都与其他Buffer Object没什么区别,我们只不过把一个或多个uniform数据交给它,以替代glUniform的方式传递数据而已。这里必须明确一点,这些数据是给到这个UBO,存储于这个UBO上,而不再是交给ShaderProgram,所以它们不会占用这个ShaderProgram自身的uniform存储空间,所以UBO是一种全新的传递数据的方式,从路径到目的地,都跟传统uniform变量的方式不一样。自然,对于这样的数据,在Shader中不能再使用上面代码中的方式来指涉了。随着UBO的引入,GLSL也引入了uniform block这种指涉工具。

       uniform block是Interface block的一种,(layout意义容后再述)在unifom关键字后直接跟随一个block name和大括号,里面是一个或多个uniform变量。一个uniform block可以指涉一个UBO的数据——我们要把block里的uniform变量与OpenGL里的数据建立关联。

      还是基于上面的例子进行修改,我们需要达到下面的效果



首先我们重新写一个basic.frag

  1. #version 400  
  2.    
  3. in  vec3 texCoord;  
  4. layout(location = 0) out vec4 fragColor;  
  5.   
  6. uniform blobSettings{  
  7.     vec4 innerColor;  
  8.     vec4 outerColor;  
  9.     float radiusInner;  
  10.     float radiusOuter;  
  11. };  
  12.   
  13. void main(void) {  
  14.     float dx = abs(texCoord.x) - 0.5;  
  15.     float dy = texCoord.y -0.5;  
  16.     float dist = sqrt(dx*dx   dy*dy);  
  17.     fragColor = mix(innerColor, outerColor, smoothstep(radiusInner, radiusOuter, dist));  
  18. }  

首先定义texCoord作为从vertex shader的输如,然后fragColor作为输出,对图形对像素进行挨个着色。

basic.vert改变不是很大,增加了一个纹理坐标。

  1. layout (location = 0) in  vec3 inPosition;  
  2. layout (location = 1) in  vec3 vertexTextCoord;  
  3. out vec3 texCoord;  
  4.   
  5. void main(void) {  
  6.     texCoord = vertexTextCoord;  
  7.     gl_Position = vec4(inPosition, 1.0);  
  8. }  


main.c中需要添加一个函数用于初始化UBO,对Uniform block中的数据进行绑定。
  1. void initUniformBlockBuffer()  
  2. {  
  3.     // Get the index of the uniform block  
  4.     GLuint blockIndex = glGetUniformBlockIndex(programHandle, "blobSettings");  
  5.   
  6.     // Allocate space for the buffer  
  7.     GLint blockSize;  
  8.     glGetActiveUniformBlockiv(programHandle, blockIndex,  
  9.                               GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);  
  10.     GLubyte * blockBuffer;  
  11.     blockBuffer = (GLubyte *) malloc(blockSize);  
  12.   
  13.     // Query for the offsets of each block variable  
  14.     const GLchar *names[] = { "innerColor""outerColor",  
  15.                               "radiusInner""radiusOuter" };  
  16.   
  17.       
  18.     GLuint indices[4];  
  19.     glGetUniformIndices(programHandle, 4, names, indices);  
  20.   
  21.     GLint offset[4];  
  22.     glGetActiveUniformsiv(programHandle, 4, indices, GL_UNIFORM_OFFSET, offset);  
  23.   
  24.     // Store data within the buffer at the appropriate offsets  
  25.     GLfloat outerColor[] = {0.0f, 1.0f, 0.0f, 0.0f};  
  26.     GLfloat innerColor[] = {1.0f, 0.0f, 0.75f, 1.0f};  
  27.     GLfloat innerRadius = 0.25f, outerRadius = 0.45f;  
  28.   
  29.     memcpy(blockBuffer   offset[0], innerColor, 4 * sizeof(GLfloat));  
  30.     memcpy(blockBuffer   offset[1], outerColor, 4 * sizeof(GLfloat));  
  31.     printf("Initsa VSBO!\n");  
  32.     memcpy(blockBuffer   offset[2], &innerRadius, sizeof(GLfloat));  
  33.     memcpy(blockBuffer   offset[3], &outerRadius, sizeof(GLfloat));  
  34.   
  35.     // Create the buffer object and copy the data  
  36.     GLuint uboHandle;  
  37.     glGenBuffers( 1, &uboHandle );  
  38.     glBindBuffer( GL_UNIFORM_BUFFER, uboHandle );  
  39.     glBufferData( GL_UNIFORM_BUFFER, blockSize, blockBuffer, GL_DYNAMIC_DRAW );  
  40.   
  41.     // Bind the buffer object to the uniform block  
  42.     glBindBufferBase( GL_UNIFORM_BUFFER, blockIndex, uboHandle );  
  43. }  

shader的初始化函数也要进行一些修改:
  1. void initShader()  
  2. {  
  3.  /* We're going to create a square made from lines */  
  4.   
  5.  const GLfloat positionData[4][3] = {    
  6.     {  -1.0,  1.0, 0.0   }, /* Top point */    
  7.     {  1.0,  1.0, 0.0  }, /* Right point */    
  8.     {  1.0, -1.0, 0.0  }, /* Bottom point */    
  9.     { -1.0,  -1.0, 0.0  } }; /* Left point */    
  10.       
  11.     float tcData[] = {  
  12.   
  13.         0.0f, 0.0f,  
  14.         1.0f, 0.0f,  
  15.         1.0f, 1.0f,  
  16.         1.0f, 0.0f,  
  17.         1.0f, 1.0f,  
  18.         0.0f, 0.0f  
  19.     };  
  20.    
  21.     /* These pointers will receive the contents of our shader source code files */  
  22.     GLchar *vertexsource, *fragmentsource;  
  23.    
  24.     /* These are handles used to reference the shaders */  
  25.     GLuint vertexshader, fragmentshader;  
  26.    
  27.     /* This is a handle to the shader program */  
  28.     GLuint shaderprogram;  
  29.       
  30.      /* Allocate and assign a Vertex Array Object to our handle */  
  31.     glGenVertexArrays(1, &vao);  
  32.    
  33.     /* Bind our Vertex Array Object as the current used object */  
  34.     glBindVertexArray(vao);  
  35.    
  36.     /* Allocate and assign two Vertex Buffer Objects to our handle */  
  37.     glGenBuffers(2, vbo);  
  38.    
  39.     /* Bind our first VBO as being the active buffer and storing vertex attributes (coordinates) */  
  40.     glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);  
  41.       
  42.     glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), positionData, GL_STATIC_DRAW);  
  43.       
  44.       /* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */  
  45.     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);  
  46.    
  47.     /* Enable attribute index 0 as being used */  
  48.     glEnableVertexAttribArray(0);  
  49.    
  50.     /* Bind our second VBO as being the active buffer and storing vertex attributes (colors) */  
  51.     glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);  
  52.       
  53.     glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), tcData, GL_STATIC_DRAW);  
  54.    
  55.     /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */  
  56.      glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);  
  57.    
  58.     /* Enable attribute index 1 as being used */  
  59.     glEnableVertexAttribArray(1);  
  60.       
  61.     vShader = glCreateShader( GL_VERTEX_SHADER );  
  62.     fShader = glCreateShader( GL_FRAGMENT_SHADER );  
  63.     printf("Here\n");  
  64.     if(0 == vShader || 0 == fShader)  
  65.     {  
  66.         fprintf(stderr, "Error creating vertex shader.\n");  
  67.         quit(1);  
  68.     }  
  69.       
  70.     GLchar* vShaderCode = textFileRead("basic.vert");  
  71.     GLchar* fShaderCode = textFileRead("basic.frag");  
  72.     const GLchar* vCodeArray[1] = {vShaderCode};  
  73.     const GLchar* fCodeArray[1] = {fShaderCode};  
  74.     glShaderSource(vShader, 1, vCodeArray, NULL);  
  75.     glShaderSource(fShader, 1, fCodeArray, NULL);  
  76.       
  77.     glCompileShader(vShader);  
  78.     glCompileShader(fShader);  
  79.     free(vShaderCode);  
  80.     free(fShaderCode);  
  81.   
  82.   
  83.     GLint result;  
  84.     glGetShaderiv( vShader, GL_COMPILE_STATUS, &result );  
  85.     if( GL_FALSE == result )  
  86.     {  
  87.         fprintf( stderr, "Vertex shader compilation failed!\n" );  
  88.         GLint logLen;  
  89.         glGetShaderiv( vShader, GL_INFO_LOG_LENGTH, &logLen );  
  90.         if( logLen > 0 )  
  91.         {  
  92.             char * log = (char *)malloc(logLen);  
  93.             GLsizei written;  
  94.             glGetShaderInfoLog(vShader, logLen, &written, log);  
  95.             fprintf(stderr, "Shader log:\n%s", log);  
  96.             free(log);  
  97.         }  
  98.     }  
  99.   
  100.     programHandle = glCreateProgram();  
  101.     if(0 == programHandle)  
  102.     {  
  103.         fprintf(stderr, "Error creating programHandle.\n");  
  104.         quit(1);  
  105.     }  
  106.       
  107.     glAttachShader(programHandle, vShader);  
  108.     glAttachShader(programHandle, fShader);  
  109.       
  110.       
  111.     glBindAttribLocation(programHandle, 0, "in_Position");  
  112.     glBindAttribLocation(programHandle, 1, "in_Color");  
  113.       
  114.     glLinkProgram(programHandle);  
  115. }  

渲染的时候直接画一个正方形就可以了。
  1. glUseProgram(programHandle);  
  2. glDrawArrays(GL_QUADS,0,4);  
  3. glUseProgram(0);   

编译命令

g main.c -o main -l SDL -lGL -lGLU -lglut -lGLEW

*shader调试的一点小技巧

由于没办法在shader使用打印语句,所以shader调试起来会有点麻烦,我们可以用glGet方法来获取一些状态变量来判断shder的状态,更常用的是改变shader的代码,然后利用渲染的结果来进行调试。比如:

  1. void main(){  
  2.   float bug=0.0;  
  3.   vec3 tile=texture2D(colMap, coords.st).xyz;  
  4.   vec4 col=vec4(tile, 1.0);  
  5.   
  6.   if(something) bug=1.0;  
  7.   
  8.   col.x =bug;  
  9.   
  10.   gl_FragColor=col;  
  11. }  


 

代码下载

写一个C 的shader类

首先需要升级一下系统的glew库,老版本的glew4.x的很多特性都不支持。

http://glew.sourceforge.net/下载最新的1.10版,解压cd进目录,运行:

make 

sudo make install


GLSL的基本的知识到现在已经接触得差不多了,接下来为了更方便的学习,现在把shader封装成一个class, 加入到之前的框架。

代码就不贴了,点我去下载

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引