上次我们说到着色器的编译和连接,后面的事情没有做过多的阐述,所以我们今天继续上次的。
Getting Ready
为了方便,我们把上次的代码贴过来
顶点着色器 vertShader
#version 430 layout (location=0) in vec3 VertexPosition; layout (location=1) in vec3 VertexColor; out vec3 Color; void main() { Color = VertexColor; gl_Position = vec4(VertexPosition,1.0); }
片元着色器 fragShader
#version 430 in vec3 Color; out vec4 FragColor; void main() { FragColor = vec4(Color, 1.0); }
opengl 主程序(应用程序)
1 //配置代码 2 #if _MSC_VER>=1900 3 #include "stdio.h" 4 _ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned); 5 #ifdef __cplusplus 6 extern "C" 7 #endif 8 FILE* __cdecl __iob_func(unsigned i) { 9 return __acrt_iob_func(i); 10 } 11 #endif /* _MSC_VER>=1900 */ 12 13 //code-list 14 //using namespace std; 15 #include <iostream> 16 #include <fstream> 17 using namespace std; 18 #include <vgl.h> 19 20 GLint vertShader, fragShader; 21 GLuint vaoHandle; 22 23 float positionDate[] = 24 { 25 -0.8f,-0.8f,0.0f, 26 0.8f,-0.8f,0.0f, 27 0.0f,0.8f,0.0f, 28 }; 29 float colorDate[] = 30 { 31 1.0f,0.0f,0.0f, 32 0.0f,1.0f,0.0f, 33 0.0f,0.0f,1.0f, 34 }; 35 36 void init(); 37 void Display(); 38 void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName); //编译着色器 39 void _Link_Shader_(); //链接着色器 40 bool readFile(const char*, string&); //读取文件内容的函数 41 42 int main(int argc, char** argv) 43 { 44 glutInit(&argc, argv); 45 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); 46 glutInitWindowSize(1024, 768); 47 glutInitWindowPosition(20, 20); 48 glutCreateWindow("test"); 49 50 if (glewInit()) 51 { 52 cout << "Error!" << glGetString(glewInit()) << endl; 53 exit(EXIT_FAILURE); 54 } 55 56 cout << "GL version:" << glGetString(GL_VERSION) << endl; //查询本机OpenGL版本 57 58 init(); 59 60 61 _Compiling_Shader_(vertShader, GL_VERTEX_SHADER,"basic.vert"); 62 _Compiling_Shader_(fragShader, GL_FRAGMENT_SHADER, "basic.frag"); 63 64 _Link_Shader_(); 65 66 glutDisplayFunc(Display); 67 68 glutMainLoop(); 69 } 70 71 72 void init() 73 { 74 GLuint vboHandles[2]; 75 76 glGenBuffers(2, vboHandles); 77 78 GLuint postionBufferHandle = vboHandles[0]; 79 GLuint colorBufferHanle = vboHandles[1]; 80 81 glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle); 82 glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), positionDate, GL_STATIC_DRAW); 83 84 glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle); 85 glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), colorDate, GL_STATIC_DRAW); 86 87 glGenVertexArrays(1, &vaoHandle); 88 glBindVertexArray(vaoHandle); 89 90 glEnableVertexAttribArray(0); 91 glEnableVertexAttribArray(1); 92 93 glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle); 94 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); 95 96 glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle); 97 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); 98 } 99 100 void Display() 101 { 102 glClear(GL_COLOR_BUFFER_BIT); 103 104 glBindVertexArray(vaoHandle); 105 glDrawArrays(GL_TRIANGLES, 0, 3); 106 glutSwapBuffers(); 107 } 108 109 bool readFile(const char* filename, string& content) 110 { 111 ifstream infile; 112 infile.open(filename); 113 if (!infile.is_open())return false; 114 115 char ch; 116 infile >> noskipws; 117 while (!infile.eof()) 118 { 119 infile >> ch; 120 content += ch; 121 } 122 infile.close(); 123 content += ‘\0‘; 124 return true; 125 } 126 127 void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName) 128 { 129 shaderHandle = glCreateShader(GL_Shader_type); 130 //检查编译情况 131 if (0 == shaderHandle) 132 { 133 fprintf(stderr, "Error creating shader.\n"); 134 exit(EXIT_FAILURE); 135 } 136 string ShaderCode; 137 if (!readFile(shaderName, ShaderCode)) 138 { 139 cout << "readFile Error!" << endl; 140 exit(EXIT_FAILURE); 141 } 142 const GLchar* shaderSource = ShaderCode.c_str(); 143 const GLchar* codeArray[] = { shaderSource }; 144 glShaderSource(shaderHandle, 1, codeArray, NULL); 145 146 glCompileShader(shaderHandle); 147 148 GLint result; 149 glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &result); 150 if (GL_FALSE == result) 151 { 152 fprintf(stderr, "shader compilation failed!\n"); 153 154 GLint logLen; 155 glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen); 156 157 if (logLen > 0) 158 { 159 char* log = new char[logLen]; 160 161 GLsizei written; 162 glGetShaderInfoLog(shaderHandle, logLen, &written, log); 163 164 fprintf(stderr, "Shader log:\n%s", log); 165 delete[] log; 166 } 167 } 168 } 169 void _Link_Shader_() 170 { 171 GLuint programHandle = glCreateProgram(); 172 if (0 == programHandle) 173 { 174 fprintf(stderr, "Error creating program object.\n"); 175 exit(1); 176 } 177 178 glAttachShader(programHandle, vertShader); 179 glAttachShader(programHandle, fragShader); 180 181 glLinkProgram(programHandle); 182 183 GLint status; 184 glGetProgramiv(programHandle, GL_LINK_STATUS, &status); 185 if (GL_FALSE == status) 186 { 187 fprintf(stderr, "Failed to link shader program!\n"); 188 189 GLint logLen; 190 glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen); 191 if (logLen > 0) 192 { 193 char* log = new char[logLen]; 194 195 GLsizei written; 196 glGetShaderInfoLog(programHandle, logLen, &written, log); 197 198 fprintf(stderr, "Program log:\n%s", log); 199 delete[] log; 200 } 201 } 202 else 203 glUseProgram(programHandle); 204 }
How it works...
1.glsl 相关
关于in、out 等简单的关键字上次已经做过阐述。
location 关键字用于设置glsl 变量的下标,以便于在opengl 主程序对其进行访问。
设定glsl 变量下标(index)还可以在opengl主程序中进行:
//假定着色器程序(glsl程序)的句柄额为programHandle glBindAttribLocation(programHandle, 0,"VertexPosition"); glBindAttribLocation(programHandle, 1, "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(1, &vaoHandle); glBindVertexArray(vaoHandle); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); //glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle); //glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindVertexBuffer(0, postionBufferHandle, 0, sizeof(GLfloat) * 3); glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); glVertexAttribBinding(0, 0); //glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle); //glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindVertexBuffer(1, colorBufferHandle, 0, sizeof(GLfloat) * 3); glVertexAttribFormat(1, 3, GL_FLOAT, GL_FALSE, 0); glVertexAttribBinding(1, 1);
这样就很清楚了
从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 等等,之后测试完成在介绍给大家,今天就到这里啦。
原文:https://www.cnblogs.com/lv-anchoret/p/9523191.html