glBindAttribLocation GLSL,generic vertex attributes)

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

分类: 计算机图形学  |  作者: silangquan 相关  |  发布日期 : 2012-10-25  |  热度 : 304°

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执行时候的常量数据,比如光照参数、变化矩阵、纹理对象句柄等等。

       下面是基于http://www.aiuxian.com/article/p-234350.html的例子的一个修改,通过增加一个uniform的旋转变量,对每个顶点进行旋转一定的角度。

       首先是basic.vert:

01#version 400
02 
03layout (location = 0) in  vec2 in_Position;
04layout (location = 1) in  vec3 in_Color;
05out vec3 ex_Color;
06uniform mat4 RotationMatrix;
07 
08void main(void) {
09  
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 <glm/glm.hpp>
2#include <glm/gtc/matrix_transform.hpp>
3using glm::mat4;
4using glm::vec3;

然后在renderGL中修改代码如下:
01glUseProgram(programHandle);
02    float angle = 30;
03    mat4 rotationMatrix = glm::rotate(mat4(1.0f), angle, vec3(0.0f,0.0f,1.0f));
04    GLuint location =glGetUniformLocationg(programHandle,"RotationMatrix");
05     
06    if( location >= 0 )
07    {
08        glUniformMatrix4fv(location, 1, GL_FALSE,&rotationMatrix[0][0]);
09    }
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

01#version 400
02  
03in  vec3 texCoord;
04layout(location = 0) out vec4 fragColor;
05 
06uniform blobSettings{
07    vec4 innerColor;
08    vec4 outerColor;
09    float radiusInner;
10    float radiusOuter;
11};
12 
13void 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改变不是很大,增加了一个纹理坐标。

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


main.c中需要添加一个函数用于初始化UBO,对Uniform block中的数据进行绑定。
01void initUniformBlockBuffer()
02{
03    // Get the index of the uniform block
04    GLuint blockIndex = glGetUniformBlockIndex(programHandle, "blobSettings");
05 
06    // Allocate space for the buffer
07    GLint blockSize;
08    glGetActiveUniformBlockiv(programHandle, blockIndex,
09                              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的初始化函数也要进行一些修改:
001void initShader()
002{
003 /* We're going to create a square made from lines */
004 
005 const GLfloat positionData[4][3] = { 
006    {  -1.0,  1.0, 0.0   }, /* Top point */ 
007    {  1.0,  1.0, 0.0  }, /* Right point */ 
008    {  1.0, -1.0, 0.0  }, /* Bottom point */ 
009    { -1.0,  -1.0, 0.0  } }; /* Left point */ 
010     
011    float tcData[] = {
012 
013        0.0f, 0.0f,
014        1.0f, 0.0f,
015        1.0f, 1.0f,
016        1.0f, 0.0f,
017        1.0f, 1.0f,
018        0.0f, 0.0f
019    };
020  
021    /* These pointers will receive the contents of our shader source code files */
022    GLchar *vertexsource, *fragmentsource;
023  
024    /* These are handles used to reference the shaders */
025    GLuint vertexshader, fragmentshader;
026  
027    /* This is a handle to the shader program */
028    GLuint shaderprogram;
029     
030     /* Allocate and assign a Vertex Array Object to our handle */
031    glGenVertexArrays(1, &vao);
032  
033    /* Bind our Vertex Array Object as the current used object */
034    glBindVertexArray(vao);
035  
036    /* Allocate and assign two Vertex Buffer Objects to our handle */
037    glGenBuffers(2, vbo);
038  
039    /* Bind our first VBO as being the active buffer and storing vertex attributes (coordinates) */
040    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
041     
042    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), positionData, GL_STATIC_DRAW);
043     
044      /* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */
045    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
046  
047    /* Enable attribute index 0 as being used */
048    glEnableVertexAttribArray(0);
049  
050    /* Bind our second VBO as being the active buffer and storing vertex attributes (colors) */
051    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
052     
053    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), tcData, GL_STATIC_DRAW);
054  
055    /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
056     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
057  
058    /* Enable attribute index 1 as being used */
059    glEnableVertexAttribArray(1);
060     
061    vShader = glCreateShader( GL_VERTEX_SHADER );
062    fShader = glCreateShader( GL_FRAGMENT_SHADER );
063    printf("Here\n");
064    if(0 == vShader || 0 == fShader)
065    {
066        fprintf(stderr, "Error creating vertex shader.\n");
067        quit(1);
068    }
069     
070    GLchar* vShaderCode = textFileRead("basic.vert");
071    GLchar* fShaderCode = textFileRead("basic.frag");
072    const GLchar* vCodeArray[1] = {vShaderCode};
073    const GLchar* fCodeArray[1] = {fShaderCode};
074    glShaderSource(vShader, 1, vCodeArray, NULL);
075    glShaderSource(fShader, 1, fCodeArray, NULL);
076     
077    glCompileShader(vShader);
078    glCompileShader(fShader);
079    free(vShaderCode);
080    free(fShaderCode);
081 
082 
083    GLint result;
084    glGetShaderiv( vShader, GL_COMPILE_STATUS, &result );
085    if( GL_FALSE == result )
086    {
087        fprintf( stderr, "Vertex shader compilation failed!\n" );
088        GLint logLen;
089        glGetShaderiv( vShader, GL_INFO_LOG_LENGTH, &logLen );
090        if( logLen > 0 )
091        {
092            char * log = (char *)malloc(logLen);
093            GLsizei written;
094            glGetShaderInfoLog(vShader, logLen, &written, log);
095            fprintf(stderr, "Shader log:\n%s", log);
096            free(log);
097        }
098    }
099 
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}

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

编译命令

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

*shader调试的一点小技巧

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

01void main(){
02  float bug=0.0;
03  vec3 tile=texture2D(colMap, coords.st).xyz;
04  vec4 col=vec4(tile, 1.0);
05 
06  if(something) bug=1.0;
07 
08  col.x+=bug;
09 
10  gl_FragColor=col;
11}


 

代码下载

写一个C++的shader类

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

链接地址下载最新的1.10版,解压cd进目录,运行:

make 

sudo make install


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

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



参考

OpenGL 4.0 Shading Language Cookbook

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值