分类: 计算机图形学 |
作者: 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:
03 | layout (location = 0) in vec2 in_Position; |
04 | layout (location = 1) in vec3 in_Color; |
06 | uniform mat4 RotationMatrix; |
10 | gl_Position = RotationMatrix * vec4(in_Position.x, in_Position.y, 0.0, 1.0); |
增加了uniform的4维矩阵变量,存储旋转矩阵。
在main.cpp中修改如下:
首先添加一下头文件,因为要用到glm库。
2 | #include <glm/gtc/matrix_transform.hpp> |
然后在renderGL中修改代码如下:
01 | glUseProgram(programHandle); |
03 | mat4 rotationMatrix = glm::rotate(mat4(1.0f), angle, vec3(0.0f,0.0f,1.0f)); |
04 | GLuint location =glGetUniformLocationg(programHandle, "RotationMatrix" ); |
08 | glUniformMatrix4fv(location, 1, GL_FALSE,&rotationMatrix[0][0]); |
16 | glClearColor(0.0, 0.0, 0.0, 1.0); |
17 | glClear(GL_COLOR_BUFFER_BIT); |
19 | glDrawArrays(GL_TRIANGLE_FAN, 0, i); |
修改的部分首先是生成选装的矩阵,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
04 | layout(location = 0) out vec4 fragColor; |
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)); |
首先定义texCoord作为从vertex shader的输如,然后fragColor作为输出,对图形对像素进行挨个着色。
basic.vert改变不是很大,增加了一个纹理坐标。
1 | layout (location = 0) in vec3 inPosition; |
2 | layout (location = 1) in vec3 vertexTextCoord; |
6 | texCoord = vertexTextCoord; |
7 | gl_Position = vec4(inPosition, 1.0); |
main.c中需要添加一个函数用于初始化UBO,对Uniform block中的数据进行绑定。
01 | void initUniformBlockBuffer() |
04 | GLuint blockIndex = glGetUniformBlockIndex(programHandle, "blobSettings" ); |
08 | glGetActiveUniformBlockiv(programHandle, blockIndex, |
09 | GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); |
10 | GLubyte * blockBuffer; |
11 | blockBuffer = (GLubyte *) malloc (blockSize); |
14 | const GLchar *names[] = { "innerColor" , "outerColor" , |
15 | "radiusInner" , "radiusOuter" }; |
19 | glGetUniformIndices(programHandle, 4, names, indices); |
22 | glGetActiveUniformsiv(programHandle, 4, indices, GL_UNIFORM_OFFSET, offset); |
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; |
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)); |
37 | glGenBuffers( 1, &uboHandle ); |
38 | glBindBuffer( GL_UNIFORM_BUFFER, uboHandle ); |
39 | glBufferData( GL_UNIFORM_BUFFER, blockSize, blockBuffer, GL_DYNAMIC_DRAW ); |
42 | glBindBufferBase( GL_UNIFORM_BUFFER, blockIndex, uboHandle ); |
shader的初始化函数也要进行一些修改:
005 | const GLfloat positionData[4][3] = { |
009 | { -1.0, -1.0, 0.0 } }; |
022 | GLchar *vertexsource, *fragmentsource; |
025 | GLuint vertexshader, fragmentshader; |
028 | GLuint shaderprogram; |
031 | glGenVertexArrays(1, &vao); |
034 | glBindVertexArray(vao); |
037 | glGenBuffers(2, vbo); |
040 | glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); |
042 | glBufferData(GL_ARRAY_BUFFER, 12 * sizeof (GLfloat), positionData, GL_STATIC_DRAW); |
045 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); |
048 | glEnableVertexAttribArray(0); |
051 | glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); |
053 | glBufferData(GL_ARRAY_BUFFER, 12 * sizeof (GLfloat), tcData, GL_STATIC_DRAW); |
056 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); |
059 | glEnableVertexAttribArray(1); |
061 | vShader = glCreateShader( GL_VERTEX_SHADER ); |
062 | fShader = glCreateShader( GL_FRAGMENT_SHADER ); |
064 | if (0 == vShader || 0 == fShader) |
066 | fprintf (stderr, "Error creating vertex shader.\n" ); |
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); |
077 | glCompileShader(vShader); |
078 | glCompileShader(fShader); |
084 | glGetShaderiv( vShader, GL_COMPILE_STATUS, &result ); |
085 | if ( GL_FALSE == result ) |
087 | fprintf ( stderr, "Vertex shader compilation failed!\n" ); |
089 | glGetShaderiv( vShader, GL_INFO_LOG_LENGTH, &logLen ); |
092 | char * log = ( char *) malloc (logLen); |
094 | glGetShaderInfoLog(vShader, logLen, &written, log ); |
095 | fprintf (stderr, "Shader log:\n%s" , log ); |
100 | programHandle = glCreateProgram(); |
101 | if (0 == programHandle) |
103 | fprintf (stderr, "Error creating programHandle.\n" ); |
107 | glAttachShader(programHandle, vShader); |
108 | glAttachShader(programHandle, fShader); |
111 | glBindAttribLocation(programHandle, 0, "in_Position" ); |
112 | glBindAttribLocation(programHandle, 1, "in_Color" ); |
114 | glLinkProgram(programHandle); |
渲染的时候直接画一个正方形就可以了。
1 | glUseProgram(programHandle); |
2 | glDrawArrays(GL_QUADS,0,4); |
编译命令
g++ main.c -o main -l SDL -lGL -lGLU -lglut -lGLEW
*shader调试的一点小技巧
由于没办法在shader使用打印语句,所以shader调试起来会有点麻烦,我们可以用glGet方法来获取一些状态变量来判断shder的状态,更常用的是改变shader的代码,然后利用渲染的结果来进行调试。比如:
03 | vec3 tile=texture2D(colMap, coords.st).xyz; |
04 | vec4 col=vec4(tile, 1.0); |
06 | if (something) bug=1.0; |
代码下载
写一个C++的shader类
首先需要升级一下系统的glew库,老版本的glew4.x的很多特性都不支持。
去链接地址下载最新的1.10版,解压cd进目录,运行:
make
sudo make install
GLSL的基本的知识到现在已经接触得差不多了,接下来为了更方便的学习,现在把shader封装成一个class, 加入到之前的框架。
代码就不贴了,点我去下载。
参考
OpenGL 4.0 Shading Language Cookbook