Sending data to a shader using vertex attributes and vertex buffer object

上次我们说到着色器的编译和连接,后面的事情没有做过多的阐述,所以我们今天继续上次的。

今天所讲的也是OpenGL着色器编程中非常重要的一个环节,叫做:数据传递

它主要承担的任务呢就是把opengl主程序中的数据传递到着色器中,上一节,我们已经把着色器准备好了,本节,我们将把主程序中的数据传递到着色器中进行处理。

说到数据传递,它其实分两种:

1.使用顶点属性和顶点缓冲对象(VBO)进行数据传递

2.使用统一变量(uniform)进行数据传递

上述两者均为非常重要的内容,我们今天先阐述第一种。

Getting Ready

为了方便,我们把上次的代码贴过来

顶点着色器 vertShader

#version 

layout (location=) in vec3 VertexPosition;
layout (location=) in vec3 VertexColor; out vec3 Color; void main()
{
Color = VertexColor; gl_Position = vec4(VertexPosition,1.0);
}

片元着色器 fragShader

#version 

in vec3 Color;

out vec4 FragColor;

void main()
{
FragColor = vec4(Color, 1.0);
}

opengl 主程序(应用程序)

 //配置代码
#if _MSC_VER>=1900
#include "stdio.h"
_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#ifdef __cplusplus
extern "C"
#endif
FILE* __cdecl __iob_func(unsigned i) {
return __acrt_iob_func(i);
}
#endif /* _MSC_VER>=1900 */ //code-list
//using namespace std;
#include <iostream>
#include <fstream>
using namespace std;
#include <vgl.h> GLint vertShader, fragShader;
GLuint vaoHandle; float positionDate[] =
{
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
0.0f,0.8f,0.0f,
};
float colorDate[] =
{
1.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,
0.0f,0.0f,1.0f,
}; void init();
void Display();
void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName); //编译着色器
void _Link_Shader_(); //链接着色器
bool readFile(const char*, string&); //读取文件内容的函数 int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(, );
glutInitWindowPosition(, );
glutCreateWindow("test"); if (glewInit())
{
cout << "Error!" << glGetString(glewInit()) << endl;
exit(EXIT_FAILURE);
} cout << "GL version:" << glGetString(GL_VERSION) << endl; //查询本机OpenGL版本 init(); _Compiling_Shader_(vertShader, GL_VERTEX_SHADER,"basic.vert");
_Compiling_Shader_(fragShader, GL_FRAGMENT_SHADER, "basic.frag"); _Link_Shader_(); glutDisplayFunc(Display); glutMainLoop();
} void init()
{
GLuint vboHandles[]; glGenBuffers(, vboHandles); GLuint postionBufferHandle = vboHandles[];
GLuint colorBufferHanle = vboHandles[]; glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
glBufferData(GL_ARRAY_BUFFER, * sizeof(float), positionDate, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
glBufferData(GL_ARRAY_BUFFER, * sizeof(float), colorDate, GL_STATIC_DRAW); glGenVertexArrays(, &vaoHandle);
glBindVertexArray(vaoHandle); glEnableVertexAttribArray();
glEnableVertexAttribArray(); glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , nullptr); glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , nullptr);
} void Display()
{
glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(vaoHandle);
glDrawArrays(GL_TRIANGLES, , );
glutSwapBuffers();
} bool readFile(const char* filename, string& content)
{
ifstream infile;
infile.open(filename);
if (!infile.is_open())return false; char ch;
infile >> noskipws;
while (!infile.eof())
{
infile >> ch;
content += ch;
}
infile.close();
content += '\0';
return true;
} void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName)
{
shaderHandle = glCreateShader(GL_Shader_type);
//检查编译情况
if ( == shaderHandle)
{
fprintf(stderr, "Error creating shader.\n");
exit(EXIT_FAILURE);
}
string ShaderCode;
if (!readFile(shaderName, ShaderCode))
{
cout << "readFile Error!" << endl;
exit(EXIT_FAILURE);
}
const GLchar* shaderSource = ShaderCode.c_str();
const GLchar* codeArray[] = { shaderSource };
glShaderSource(shaderHandle, , codeArray, NULL); glCompileShader(shaderHandle); GLint result;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &result);
if (GL_FALSE == result)
{
fprintf(stderr, "shader compilation failed!\n"); GLint logLen;
glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen); if (logLen > )
{
char* log = new char[logLen]; GLsizei written;
glGetShaderInfoLog(shaderHandle, logLen, &written, log); fprintf(stderr, "Shader log:\n%s", log);
delete[] log;
}
}
}
void _Link_Shader_()
{
GLuint programHandle = glCreateProgram();
if ( == programHandle)
{
fprintf(stderr, "Error creating program object.\n");
exit();
} glAttachShader(programHandle, vertShader);
glAttachShader(programHandle, fragShader); glLinkProgram(programHandle); GLint status;
glGetProgramiv(programHandle, GL_LINK_STATUS, &status);
if (GL_FALSE == status)
{
fprintf(stderr, "Failed to link shader program!\n"); GLint logLen;
glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);
if (logLen > )
{
char* log = new char[logLen]; GLsizei written;
glGetShaderInfoLog(programHandle, logLen, &written, log); fprintf(stderr, "Program log:\n%s", log);
delete[] log;
}
}
else
glUseProgram(programHandle);
}

How it works...

1.glsl 相关

关于in、out 等简单的关键字上次已经做过阐述。

location 关键字用于设置glsl 变量的下标,以便于在opengl 主程序对其进行访问。

设定glsl 变量下标(index)还可以在opengl主程序中进行:

//假定着色器程序(glsl程序)的句柄额为programHandle
glBindAttribLocation(programHandle, ,"VertexPosition");
glBindAttribLocation(programHandle, , "VertexColor");

当然这个index也可以不设置,系统会默认进行,我们进行明确设定一下方便后面的数据传递。

gl_Position 是opengl内置变量,我们只需要重置其值即可,至于后面的扩展维度,属于CG中的知识,是在后面的三维变换中用到的,我们这里设置为1.0,即限定于普通二维平面。

流程:

·vertShader从opengl程序中接收读入VertexPosition和VertexColor,重置刷新gl_Positin ,然后将VertexColor 以 unchanged copy 的方式传递给Color。

·fragShader接收来自vertShader中的out数据,产生像素信息,输出

2.opengl 相关

流程:

1)创建一个VAO(vertex array object)    line:21(代码第21行)

2)创建属性数据信息                                 line:23、29

3)创建VBO(vertex buffer object)         line:74~79

4)用创建好的属性数据填充VBO缓冲区     line:81~85

5)激活VAO               line:87、88

6)激活glsl 顶点属性数据                           line:90、91

7)VBO与glsl 顶点属性量作映射         line:93~97

8)激活VAO进行渲染绘制                           line:104~105

那么,我们来分析一下每一步的工作

我们做之前一定是要准备好坐标以及颜色等属性信息的,这个就不多说了

3)创建VBO来存储我们的属性信息,为了方便,我们将vboHandle数组中的每一个量都重新赋值是方便后面的代码书写,没有引用是因为我们的句柄只是数字,数字即可识别,不需要地址。

4)我们需要将VBO与opengl的枚举量GL_ARRAY_BUFFER创建映射,然后通过它进行数据载入。

那个枚举量还有其他,我们将其称为binding point(暂时不知道该如何翻译,就先这样叫着吧)。

我们通过GLBindBuffer将VBO对象与一个binding point建立映射,然后通过GLBufferData将创建好的属性数据填充到VBO缓冲中,函数的第三个参数是告诉opengl应该如何使用该数据,在我们的例子中,数据被指定一次,不会被修改,并且将被多次用于绘制操作,因此这种使用模式最好对应于值GL_STATIC_DRAW。

既然我们都建立好了我们的缓冲区对象了,我们就把它们联系起来,然后记录在VAO中。

于是就有了第5)步,VAO记录的是我们的缓冲区数据和glsl 顶点属性量之间的关联

然后第6),我们通过下标index值(location设定的值)访问glsl 顶点属性量,进行激活。

这样做的目的是:打开属性量的通道,代表着该属性量将被用于之后的渲染

7)void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer);   顶点属性指针函数

第一个参数为glsl 属性量的index(或是location值);

第二个参数为该属性量的维度数,比如,position数据就是x,y,z组成的,是三维数据;

第三个参数为数据类型;

第四个参数为需要规格化,即将数据范围限定在【-1,1】;

第五个是相邻连续的属性量之间的字节距离;

最后一个参数是一个指针,但不被视为一个指针! 相反,它的值被解释为从缓冲区的开头到缓冲区中的第一个属性的字节偏移量。 在这种情况下,在第一个元素之前的任一缓冲区中都没有附加数据,因此我们使用零值

glVertexAttribPointer函数存储(在VAO的状态下)指向当前绑定到GL_ARRAY_BUFFER绑定点的缓冲区的指针。 当另一个缓冲区绑定到该绑定点(binding point)时,它不会更改指针的值。

More

如果上面的glVertexAttribPointer没看懂可以看下面这个详细版(下面的第一块)。

Separate attribute format    分离属性格式  技术

在opengl 4.3中,我们有着更好的方式去指定VAO状态(属性格式,属性激活与否,vbo缓冲区)。

在之前的例子中,glVertexAttribPointer干了两件事。

1.它间接指定了含有属性数据信息的缓冲区与binding point 之间的关联

2.它直接指定了数据的格式(类型,偏移量,间隔,等等)

我们可以将其划分为各自的函数进行设定,这样,我们将会看的更加清晰一点。

我们可以这样做:程序可以正常执行

//以前的87行处

glGenVertexArrays(, &vaoHandle);
glBindVertexArray(vaoHandle); glEnableVertexAttribArray();
glEnableVertexAttribArray(); //glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindVertexBuffer(, postionBufferHandle, , sizeof(GLfloat) * );
glVertexAttribFormat(, , GL_FLOAT, GL_FALSE, );
glVertexAttribBinding(, ); //glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindVertexBuffer(1, colorBufferHandle, , sizeof(GLfloat) * );
glVertexAttribFormat(, , GL_FLOAT, GL_FALSE, );
glVertexAttribBinding(, );

这样就很清楚了

从glBindVertexBuffer开始讲,我们之前提到过,同一个binding point可以绑定多个buffer缓冲区,比如,colorBufferHandle和positionBUfferHandle都与同一个binding point——GL_ARRAY_BUFFER建立映射(绑定)。其实呢,在opengl中binding point也有多个下标(通常标号为0~15)即16个,所以我们在这里通过binding index来建立VBO 缓冲区与binding point之间的映射。

注意:这里的binding index是binding point的下标,比如GL_ARRAY_BUFFER一般有16个位置可供绑定,以支持同一个binding point 与多个buffer建立映射

之前的location 设定的属性index与之不同,它代表的是glsl 属性量的访问代号

所以,该函数第一个参数为binding index;第二个参数为VBO buffer(缓冲区),这里用VBO句柄代替;第三个参数为buffer起始位置与第一个数据之间的字节距离;

第四个参数为连续两个数据元素之间的字节距离,这里的值不是0,而glVertexAttribPointer中的该参数为0,是因为:例如,position数据中,每一个数据元素为一个三维的坐标,我们让opengl去解析这个数据,后者提供了足够的信息,包括每个数据元素的维度,以及数据类型等共6个参数,而前者函数并没有提供数据元素的维度数,即没有说明每个数据元素有多大,所以需要提供每两个数据元素中间的字节距离,即3个float。

这个函数就非常明确的实现了之前提到的第一件事,它指定了binding index,buffer,buffer中第一个数据的初始位置,以及每两个数据元素中间相隔的字节距离(或者可以认为是每个数据元素占多大,即如何解析数据),可以说是非常明确了

glVertexAttribFormat,第一个参数为location值,即属性量的index,第二个为数据元素的维度,第三个为数据类型,第四个指是否需要规格化,第五个参数为相对偏移量

glVertexAttribBinding,绑定binding index与属性量的index

另请注意,顶点缓冲区绑定点(binding point)(由glBindVertexBuffer指定)的缓冲区绑定是VAO状态的一部分,与GL_ARRAY_BUFFER的绑定不同,后者不是。
这个版本可以说更清晰易懂。 它消除了我们对VAO中管理的“不可见”指针的疑惑,并且glVertexAttribBinding使属性和缓冲区之间的关系更加清晰。 此外,它分离了真正不需要组合的问题。

后面还有一些其他的技术,比如:绘制方面用到的glDrawElements 等等,之后测试完成在介绍给大家,今天就到这里啦。

OpenGL 笔记<3> 数据传递 一的更多相关文章

  1. OpenGL笔记<4> 数据传递二

    Sending data to a shader using uniform Preface 上一节我们介绍了通过顶点属性量进行数据传递,今天我们介绍一下通过uniform变量来进行数据传递的方法. ...

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

    in out 对于 vertex shader,每个顶点都会包含一次,它的主要工作时处理关于定点的数据,然后把结果传递到管线的下个阶段. 以前版本的GLSL,数据会通过一些内建变量,比如gl_Vert ...

  3. OpenGL/GLSL数据传递小记(3.x)(转)

    OpenGL/GLSL规范在不断演进着,我们渐渐走进可编程管道的时代的同时,崭新的功能接口也让我们有点缭乱的感觉.本文再次从OpenGL和GLSL之间数据的传递这一点,记录和介绍基于OpenGL3.x ...

  4. OpenGL/GLSL数据传递小记(2.x)(转)

    本篇记录一下关于OpenGL程序中绑定各种GLSL变量的一些注意问题(有些是近期编写代码感受强烈的).以供参考.——ZwqXin.com 本文来源于 ZwqXin (http://www.zwqxin ...

  5. 【整理】解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function

    解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function https://www.cnblogs.com/jaso ...

  6. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  7. Android(java)学习笔记220:开发一个多界面的应用程序之界面间数据传递

    1.界面跳转的数据传递 (1)intent.setData() --> intent.getData():     传递的数据比较简单,一般是文本类型的数据String:倘若我们传递的数据比较复 ...

  8. ASP.NET MVC 学习笔记-5.Controller与View的数据传递

    ViewData属性 ViewData属性是System.Web.Mvc.ControllerBase中的一个属性,它相当于一个数据字典.Controller中向该字典写入数据,ViewData[“K ...

  9. 解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function

    Vue的项目中,如果项目简单, 父子组件之间的数据传递可以使用  props 或者 $emit 等方式 进行传递 但是如果是大中型项目中,很多时候都需要在不相关的平行组件之间传递数据,并且很多数据需要 ...

随机推荐

  1. 解决IE6中 PNG图片透明的终极方案-八种方案!

    “珍惜生命,远离IE6”,IE6中的bug令很多Web前端开发人员实为头疼,因此不知道烧了多少脑细胞,在众多的Bug中最令人抓狂的就是IE对png图片的不支持,导致设计师和重构师放弃了很多很炫的效果, ...

  2. 转 -- OK6410 tftp下载内核、文件系统以及nand flash地址相关整理、总结

    转载地址:http://emouse.cnblogs.com/ 飞凌官方提供了一键下载烧写linux的方式,相对来说比较方便,但是对于开发来说不够灵活,因此这篇文章把tftp相关的点介绍一下,整理下其 ...

  3. Spring Aop、拦截器、过滤器的区别

    Filter过滤器:拦截web访问url地址.Interceptor拦截器:拦截以 .action结尾的url,拦截Action的访问.Spring AOP拦截器:只能拦截Spring管理Bean的访 ...

  4. windows程序设计.窗口.

    第一个windows窗口 #include <windows.h> /* Displays "Hello, World!" in client area */ LRES ...

  5. JS设计模式——8.桥接模式

    桥接模式的用途 在实现API的时候,桥接模式非常有用. 在设计一个JavaScript API的时候,可以用这个模式来弱化它与使用它的类和对象之间的耦合. 示例:事件监听器 桥接模式最常见和实际的应用 ...

  6. 2016.08.02 math(leetcode) 小结

    math(leetcode) 小结 在leetcode中有些知识点(套路) 判断一个数是不是能被某些数整除,可以用 n%x == 0,循环除的话,就将while(n%x == 0)的循环条件设置判断整 ...

  7. 玩玩 Nginx 1----- Nginx + ngx_lua安装测试【CentOs下】

          最近打算搞搞nginx,扒着各位先驱的文章自己进行测试下,中间过程也是错误不断,记录一下,以备使用.       nginx的安装挺简单的,主要还是研究下一些第三方的模块,首先想试下初始化 ...

  8. Flask:文件配置方式实践及其中的各种问题记录

    Windows 10家庭中文版,Python 3.6.4,Flask 1.0.2, 提示: 1.请查看本文后面的“18-07-17  11:18重大纠正” ! 2.flask run命令运行时传入参数 ...

  9. java基础76 web服务器之Tomcat服务器

    (注:本文是以“压缩版Tomcat”为例,展开描述的) 一.Tomcat服务器的介绍 1.服务器 1.1.服务器的种类 从物理上讲:服务器就是一台pc机器.至少8核/8G以上.内存至少用T来计算.宽带 ...

  10. OKR.2019

    转眼又一年过去了,回顾审视一年的得失,规划下一年的奋斗目标.Review And Planning,让全新的2019迎来全新的自己. O1 学习软件开发技术知识 KR1.1 阅读<CLR via ...