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:

#version 400

layout (location = 0) in  vec2 in_Position;
layout (location = 1) in vec3 in_Color;
out vec3 ex_Color;
uniform mat4 RotationMatrix; void main(void) { gl_Position = RotationMatrix * vec4(in_Position.x, in_Position.y, 0.0, 1.0);
ex_Color = in_Color;
}

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

在main.cpp中修改如下:

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

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using glm::mat4;
using glm::vec3;

然后在renderGL中修改代码如下:

glUseProgram(programHandle);
float angle = 30;
mat4 rotationMatrix = glm::rotate(mat4(1.0f), angle, vec3(0.0f,0.0f,1.0f));
GLuint location =glGetUniformLocationg(programHandle,"RotationMatrix"); if( location >= 0 )
{
glUniformMatrix4fv(location, 1, GL_FALSE,&rotationMatrix[0][0]);
} //Draw a square
int i;
for (i=2; i <=4; i++)
{
/* Make our background black */
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
/* Invoke glDrawArrays telling that our data is a line loop and we want to draw 2-4 vertexes */
glDrawArrays(GL_TRIANGLE_FAN, 0, i);
} // Unbind shader
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

#version 400

in  vec3 texCoord;
layout(location = 0) out vec4 fragColor; uniform blobSettings{
vec4 innerColor;
vec4 outerColor;
float radiusInner;
float radiusOuter;
}; void main(void) {
float dx = abs(texCoord.x) - 0.5;
float dy = texCoord.y -0.5;
float dist = sqrt(dx*dx + dy*dy);
fragColor = mix(innerColor, outerColor, smoothstep(radiusInner, radiusOuter, dist));
}

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

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

layout (location = 0) in  vec3 inPosition;
layout (location = 1) in vec3 vertexTextCoord;
out vec3 texCoord; void main(void) {
texCoord = vertexTextCoord;
gl_Position = vec4(inPosition, 1.0);
}

main.c中需要添加一个函数用于初始化UBO,对Uniform block中的数据进行绑定。

void initUniformBlockBuffer()
{
// Get the index of the uniform block
GLuint blockIndex = glGetUniformBlockIndex(programHandle, "blobSettings"); // Allocate space for the buffer
GLint blockSize;
glGetActiveUniformBlockiv(programHandle, blockIndex,
GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
GLubyte * blockBuffer;
blockBuffer = (GLubyte *) malloc(blockSize); // Query for the offsets of each block variable
const GLchar *names[] = { "innerColor", "outerColor",
"radiusInner", "radiusOuter" }; GLuint indices[4];
glGetUniformIndices(programHandle, 4, names, indices); GLint offset[4];
glGetActiveUniformsiv(programHandle, 4, indices, GL_UNIFORM_OFFSET, offset); // Store data within the buffer at the appropriate offsets
GLfloat outerColor[] = {0.0f, 1.0f, 0.0f, 0.0f};
GLfloat innerColor[] = {1.0f, 0.0f, 0.75f, 1.0f};
GLfloat innerRadius = 0.25f, outerRadius = 0.45f; memcpy(blockBuffer + offset[0], innerColor, 4 * sizeof(GLfloat));
memcpy(blockBuffer + offset[1], outerColor, 4 * sizeof(GLfloat));
printf("Initsa VSBO!\n");
memcpy(blockBuffer + offset[2], &innerRadius, sizeof(GLfloat));
memcpy(blockBuffer + offset[3], &outerRadius, sizeof(GLfloat)); // Create the buffer object and copy the data
GLuint uboHandle;
glGenBuffers( 1, &uboHandle );
glBindBuffer( GL_UNIFORM_BUFFER, uboHandle );
glBufferData( GL_UNIFORM_BUFFER, blockSize, blockBuffer, GL_DYNAMIC_DRAW ); // Bind the buffer object to the uniform block
glBindBufferBase( GL_UNIFORM_BUFFER, blockIndex, uboHandle );
}

shader的初始化函数也要进行一些修改:

void initShader()
{
/* We're going to create a square made from lines */ const GLfloat positionData[4][3] = {
{ -1.0, 1.0, 0.0 }, /* Top point */
{ 1.0, 1.0, 0.0 }, /* Right point */
{ 1.0, -1.0, 0.0 }, /* Bottom point */
{ -1.0, -1.0, 0.0 } }; /* Left point */ float tcData[] = { 0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 0.0f
}; /* These pointers will receive the contents of our shader source code files */
GLchar *vertexsource, *fragmentsource; /* These are handles used to reference the shaders */
GLuint vertexshader, fragmentshader; /* This is a handle to the shader program */
GLuint shaderprogram; /* Allocate and assign a Vertex Array Object to our handle */
glGenVertexArrays(1, &vao); /* Bind our Vertex Array Object as the current used object */
glBindVertexArray(vao); /* Allocate and assign two Vertex Buffer Objects to our handle */
glGenBuffers(2, vbo); /* Bind our first VBO as being the active buffer and storing vertex attributes (coordinates) */
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), positionData, GL_STATIC_DRAW); /* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); /* Enable attribute index 0 as being used */
glEnableVertexAttribArray(0); /* Bind our second VBO as being the active buffer and storing vertex attributes (colors) */
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), tcData, GL_STATIC_DRAW); /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); /* Enable attribute index 1 as being used */
glEnableVertexAttribArray(1); vShader = glCreateShader( GL_VERTEX_SHADER );
fShader = glCreateShader( GL_FRAGMENT_SHADER );
printf("Here\n");
if(0 == vShader || 0 == fShader)
{
fprintf(stderr, "Error creating vertex shader.\n");
quit(1);
} GLchar* vShaderCode = textFileRead("basic.vert");
GLchar* fShaderCode = textFileRead("basic.frag");
const GLchar* vCodeArray[1] = {vShaderCode};
const GLchar* fCodeArray[1] = {fShaderCode};
glShaderSource(vShader, 1, vCodeArray, NULL);
glShaderSource(fShader, 1, fCodeArray, NULL); glCompileShader(vShader);
glCompileShader(fShader);
free(vShaderCode);
free(fShaderCode); GLint result;
glGetShaderiv( vShader, GL_COMPILE_STATUS, &result );
if( GL_FALSE == result )
{
fprintf( stderr, "Vertex shader compilation failed!\n" );
GLint logLen;
glGetShaderiv( vShader, GL_INFO_LOG_LENGTH, &logLen );
if( logLen > 0 )
{
char * log = (char *)malloc(logLen);
GLsizei written;
glGetShaderInfoLog(vShader, logLen, &written, log);
fprintf(stderr, "Shader log:\n%s", log);
free(log);
}
} programHandle = glCreateProgram();
if(0 == programHandle)
{
fprintf(stderr, "Error creating programHandle.\n");
quit(1);
} glAttachShader(programHandle, vShader);
glAttachShader(programHandle, fShader); glBindAttribLocation(programHandle, 0, "in_Position");
glBindAttribLocation(programHandle, 1, "in_Color"); glLinkProgram(programHandle);
}

渲染的时候直接画一个正方形就可以了。

glUseProgram(programHandle);
glDrawArrays(GL_QUADS,0,4);
glUseProgram(0);

编译命令

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

*shader调试的一点小技巧

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

void main(){
float bug=0.0;
vec3 tile=texture2D(colMap, coords.st).xyz;
vec4 col=vec4(tile, 1.0); if(something) bug=1.0; col.x+=bug; gl_FragColor=col;
}

代码下载

写一个C++的shader类

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

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

参考

OpenGL/GLSL数据传递小记(3.x) - http://www.zwqxin.com/archives/shaderglsl/communication-between-opengl-glsl-2.html

OpenGL 4.0 Shading Language Cookbook

OpenGL进阶(十一) - GLSL4.x中的数据传递的更多相关文章

  1. web开发-前端到服务器Controller中的数据传递

    一, ajax方式 1. ajax获取页面中的数据,包括表单中的数据, 然后封装成对象,数组, 字符串, 或其他基本类型的数据. 2. 将封装得到的数据通过ajax传递到controller中(注:在 ...

  2. 将Controller中的数据传递到View中显示

    如何将Controller 中的数据传送到View 步骤: (1)要有数据,如果要用到对象可以在Model 中定义对应的类 (2)要有装数据的容器: System.Text.StringBuilder ...

  3. react-redux中的数据传递

    1.connect connect用于连接React组件与 Redux store,其使用方法如下 connect([mapStateToProps], [mapDispatchToProps], [ ...

  4. 后端list集合中的数据传递到前台HTML中显示(表格形式)

    关键字:web项目中前后台数据传递问题 在学习web项目的过程中,我们肯定会遇到前后台数据交换问题.这个问题我也思考了很久,今天借此总结一下.由于博主水平有限,如有不当之处,还请大家多多指正,,废话不 ...

  5. 剖析 Rails 3 MVC 中的数据传递

    引用链接:https://www.ibm.com/developerworks/cn/web/1108_linhx_rails3mvc/ 如果读者已经开发过基于 Rails 的应用,但对其 MVC 间 ...

  6. Android笔记——Activity中的数据传递案例(用户注冊)

    1.创建程序activity_main: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andro ...

  7. Android笔记(四十一) Android中的数据存储——SQLite(三)select

    SQLite 通过query实现查询,它通过一系列参数来定义查询条件. 各参数说明: query()方法参数 对应sql部分 描述 table from table_name 表名称 colums s ...

  8. web开发-服务器Controller到前端中的数据传递

    一, ajax方式 (一)controller中 1. 定义AjaxResponse类 成员有: status , message, data.  其中 status是成功或失败状态, message ...

  9. springmvc中的数据传递

    import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; impo ...

随机推荐

  1. Uva227.Puzzle

    题目连接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  2. linux指定动态运行库的位置

    动态运行库在windows.linux下均广泛使用.windows下通常为dll文件,linux下为so文件.不过,对于部署程序,这两个系统查找依赖的运行库文件时却不一样.对于windows而言,优先 ...

  3. 主题简介 ASP .NET

    由控件的外观.样式组成的集合,由一个文件组构成,存放在App_Themes文件夹下. 主题包括:皮肤文件(.Skin).CSS文件(.CSS).图片.其它资源等. 主题的作用:统一设置Web页面的外观 ...

  4. 一个简单的面试题 很多人也会懵 i++ 和++i的区别

    以下分别输出i的值分别为多少 NSInteger i = 0 ; NSLog(@"%ld",i++); NSLog(@"%ld",i++); NSLog(@&q ...

  5. R6010 -abort() has been called错误分析及其解决方法

    近期使用vs2010编程出现下面问题.在网上收集了大家的意见之后,整理了一下 导致出现这种原因有: 1.非法指针訪问和内存泄漏 2.大家再查查吧.一定是指针出现故障了.设置的指针范围跟你执行的不正确 ...

  6. 某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法

    在某APK中,通过如下方法动态注册了一个BroadcastReceiver,代码参考如下: @Override protected void onAttachedToWindow() { super. ...

  7. 微软提供了三个核心服务:Windows+Office 365+Azure

    微软提供了三个核心服务:Windows+Office 365+Azure 英语新闻来源:http://techcrunch.com/2014/11/10/microsofts-ceo-breaks-d ...

  8. 动态修改UINavigationBar的背景色

    这是我们最终想要得到的效果: 思路 在UISrollView的delegate方法 1  - (void)scrollViewDidScroll:(UIScrollView *)scrollView ...

  9. Qt使用异或进行加密解密

          在加密,解密中,异或运算应该时比较简单的一种.下面的代码,采用异或运算进行加密,解密: 点击(此处)折叠或打开 #include <QtCore/QCoreApplication&g ...

  10. Java中遍历Map对象的方法

    方法一: 在for-each循环中使用entries来遍历 这是最常见的遍历方式,在需要获取key和value时使用. Map<Integer, Integer> map = new Ha ...