【OpenGL】OpenGL绘图的一点理解
来源:互联网 发布:为linux添加引导 编辑:程序博客网 时间:2024/05/18 01:08
主要是在这篇文章的基础上理解的,唉,这东西真麻烦,肖姐姐基本没讲OpenGL的具体工作,其实现在关于OpenGL有了很多新技术,但是按肖姐姐给的库,很多还都是很早的,已经是弃用或者不推荐使用的库,虽然可以说原理都一样,但是……为了实现作业,乱七八糟的搜资料,很多时间都是浪费在这上面,而且各种实现,比如在这个网上它应该是使用了最新的OpenGL3.3+版本,就和之前的不太一样,真是缭乱在风雨中啊,好吧,吐槽完毕……
我就按它源代码的顺序记下自己现在的理解,因为不记下来我自己又会凌乱的。首先声明,这篇文章并没有从头讲怎么使用OpenGL,只是讲了一些对于不懂的地方的理解,如果要具体学习,还请移步该网址。
1.初始化工作。这里都是一些固定的初始化,就不讲了,具体的我还不是很清楚。比较重要的是这里启用了深度测试:
// Enable depth testglEnable(GL_DEPTH_TEST);// Accept fragment if it closer to the camera than the former oneglDepthFunc(GL_LESS);
2.创建一个顶点数组对象,并将它设为当前对象(细节暂不深入)。这里其实只是做了一个初始化的工作,后面在绘图工作中并没有用到这个数据:
GLuint VertexArrayID;// 该顶点数组对象的IDglGenVertexArrays(1, &VertexArrayID);// 真正创建一个数组,返回值传给VertexArrayIDglBindVertexArray(VertexArrayID);// 设置为当前对象
当窗口创建成功后(即OpenGL上下文创建后),马上做这一步工作;必须在任何其他OpenGL调用前完成。
我的理解是,这里是把顶点数据进行分组。比如这个数组里的顶点用来绘一个正方体,那个数组用来绘一个三角形。对于每一个这样的顶点数组对象,想要把它下面的数据绘在屏幕上都需要经过下面的几个步骤。
最后的这句话将告诉下面创建的缓冲区啊什么的,都是这个数组对象的。
3. 载入外部shader。
// 从外部shader中,创建并编译我们的GLSL(OpenGL Shader Language)GLuint programID = LoadShaders( "TransformVertexShader.vertexshader", "ColorFragmentShader.fragmentshader" );// 得到vertaxshader中的“MVP” uniform的句柄GLuint MatrixID = glGetUniformLocation(programID, "MVP");// 透视矩阵 : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 unitsglm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);// 视图矩阵glm::mat4 View = glm::lookAt(glm::vec3(4,3,-3), // Camera is at (4,3,-3), in World Spaceglm::vec3(0,0,0), // and looks at the originglm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down) );// 模型矩阵 : an identity matrix (model will be at the origin)glm::mat4 Model = glm::mat4(1.0f);// 我们的 ModelViewProjection : multiplication of our 3 matrices,在主循环中将会传递给上述的MVP句柄,从而改变视角glm::mat4 MVP = Projection * View * Model; // Remember, matrix multiplication is the other way around
下面就是两个shader的内容。
TransformVertexShader.vertexshader:
#version 330 core// 输入的顶点数据,这里是和下面将要讲的顶点属性一一对应,下面这些值实际上是靠我们将要定义在内存中的数据(如位置,颜色)来初始化的.layout(location = 0) in vec3 vertexPosition_modelspace;layout(location = 1) in vec3 vertexColor;// 输出数据: will be interpolated for each fragment.out vec3 fragmentColor;// Values that stay constant for the whole mesh.uniform mat4 MVP;void main(){// Output position of the vertex, in clip space : MVP * positiongl_Position = MVP * vec4(vertexPosition_modelspace,1);// The color of each vertex will be interpolated// to produce the color of each fragmentfragmentColor = vertexColor;}
ColorFragmentShader.fragmentshader:#version 330 core// 从上面vertaxshader中传入的数据,经过了插值后的值in vec3 fragmentColor;// 输出的数据,这里我没有懂,好像OpenGL直接就使用这个值用于绘制颜色了out vec3 color;void main(){// Output color = 我们在vertex shader中定义的颜色// interpolated between all 3 surrounding verticescolor = fragmentColor;}
4.现在需要告诉OpenGL有哪些内容,换句话就是说,应该往屏幕上画些什么。首先我们要在内存中定义这些顶点:
// 包含了3个向量的数组,表示3个顶点static const GLfloat g_vertex_buffer_data[] = { -1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f};
这里只是在内存里定义了所有点的位置信息,但是还没有跟OpenGL联系起来。所以接下来就要把这个三角形传给OpenGL。我们通过创建一个缓冲区完成:
// 用于标识缓冲区GLuint vertexbuffer;// 生成一个缓冲区,并将返回的标识符传给vertexbufferglGenBuffers(1, &vertexbuffer);// 讲vertexbuffer设置为当前的数组缓冲对象,也就是告诉OpenGL,好了,我下面都是对这个缓冲区进行操作的glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);// 将之前定义的数据传给OpenGLglBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
这只要做一次。它所做的实际上是告诉OpenGL,标识符为vertexbuffer的缓冲区里放了哪些数据,以后你画的时候就要根据这些数据进行绘图。我们可以定义多个缓冲区
因此颜色缓冲区的操作类似。
// One color for each vertex. They were generated randomly.static const GLfloat g_color_buffer_data[] = { 0.583f, 0.771f, 0.014f,0.609f, 0.115f, 0.436f,0.327f, 0.483f, 0.844f,0.822f, 0.569f, 0.201f,0.435f, 0.602f, 0.223f,0.310f, 0.747f, 0.185f,0.597f, 0.770f, 0.761f,0.559f, 0.436f, 0.730f,0.359f, 0.583f, 0.152f,0.483f, 0.596f, 0.789f,0.559f, 0.861f, 0.639f,0.195f, 0.548f, 0.859f,0.014f, 0.184f, 0.576f,0.771f, 0.328f, 0.970f,0.406f, 0.615f, 0.116f,0.676f, 0.977f, 0.133f,0.971f, 0.572f, 0.833f,0.140f, 0.616f, 0.489f,0.997f, 0.513f, 0.064f,0.945f, 0.719f, 0.592f,0.543f, 0.021f, 0.978f,0.279f, 0.317f, 0.505f,0.167f, 0.620f, 0.077f,0.347f, 0.857f, 0.137f,0.055f, 0.953f, 0.042f,0.714f, 0.505f, 0.345f,0.783f, 0.290f, 0.734f,0.722f, 0.645f, 0.174f,0.302f, 0.455f, 0.848f,0.225f, 0.587f, 0.040f,0.517f, 0.713f, 0.338f,0.053f, 0.959f, 0.120f,0.393f, 0.621f, 0.362f,0.673f, 0.211f, 0.457f,0.820f, 0.883f, 0.371f,0.982f, 0.099f, 0.879f};GLuint colorbuffer;glGenBuffers(1, &colorbuffer);glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
总结一下,对于顶点位置信息和顶点颜色信息,实际上都是 在内存中定义数据——为其生成一个缓冲区——用glBindBuffer将刚刚生成的缓冲区设置为当前值——使用定义好的数据填充缓冲区。这样就完成了所有需要的缓冲区的创建工作。
5.准备工作都已经做好了,现在进入我们的主循环中。
在经过了一些屏幕清除工作后,我们要告诉OpenGL,我们要使用自己的shader。
// 使用我们的shaderglUseProgram(programID);// 把我们的变换矩阵传送给当前绑定的shader// 你应该还记得MatrixID是我们得到shader中名字为"MVP" uniform的句柄glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
接下来就是使用缓冲区来绘图了。我们需要把缓冲区中的数据和shader中的各个值进行对应。
// 1rst attribute buffer : vertices 开启了某一个顶点属性后,在渲染时就会使用这个属性。这句话的位置是无所谓的,只要在渲染前开启就可以了。glEnableVertexAttribArray(0);// 这里再次调用了glBindBuffer函数,上一次调用(见上)是为了给这个缓冲区填充数据,现在是为了使用vertexbuffer缓冲区glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); // 这里按我现在的理解是,是将这个缓冲区和属性0绑定了。也就是说,这个缓冲区里的数据用来指定每个点属性0的值glVertexAttribPointer( 0, // 属性 0。没有特别的原因必须使用0,但是一定要与shader中的布局匹配 3, // 大小 GL_FLOAT, // 类型 GL_FALSE, // 是否正则化 0, // 步幅 (void*)0 // 数组缓冲区偏移);
如果还是不懂,往上翻看到vertaxshader,有这样一行代码:
layout(location = 0) in vec3 vertexPosition_modelspace;
实际上就是把vertexbuffer里的数据作为属性0传给了shader,shader中这个值被赋给了名字为vertexPosition_modelspace的变量,并用于gl_Position的赋值,gl_Position是内定的变量,就是点的位置。
颜色数据的对应也是类似的。
// 2nd attribute buffer : colorsglEnableVertexAttribArray(1);glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);glVertexAttribPointer(1, // attribute. No particular reason for 1, but must match the layout in the shader.3, // sizeGL_FLOAT, // typeGL_FALSE, // normalized?0, // stride(void*)0 // array buffer offset);
6. 呼呼,终于做完了。下面就使用shader把图形画在屏幕上!还要记得把之前用到的属性再次disable掉。
// Draw the triangle !glDrawArrays(GL_TRIANGLES, 0, 12*3); // 12*3 indices starting at 0 -> 12 trianglesglDisableVertexAttribArray(0);glDisableVertexAttribArray(1);// Swap buffersglfwSwapBuffers();
思考:
如果要绘制两个图形呢?比如一个正方体和一个三角形。我试了下,简单的话就是直接在定义顶点数据时再加上3个点就行了。但这样是不太科学的,扩展性也不好,又试了下定义两个顶点数据对象(见第2步),然后分别绑定各自的缓冲区,最后在主循环中通过glBindVertexArray(VertexArrayID);来进行切换,也是可以的。
事实上,我们应当使用后面一种方法,也就是通过切换不同的VBO来绘制不同的图形。
- 【OpenGL】OpenGL绘图的一点理解
- OpenGL光照的一点理解
- 写一点opengl的初步入门理解
- OpenGL 绘图的机制
- opengl 基于MFC的OpenGL绘图原理
- opengl绘图
- OpenGL 和 OpenGL ES的一点区别
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- OpenGl 的基本绘图功能
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- 基于MFC的OpenGL绘图
- c语言又一个单链表的实现
- 快排模板
- GE如何测试大型客机引擎
- 修改SQLServer2008express的sa帐号密码
- c语言双向链表的实现
- 【OpenGL】OpenGL绘图的一点理解
- ZOJ-1862 Mine Sweeper
- 最短路径简单应用 畅通工程续 POJ 1874
- LeetCode: Combination Sum
- c语言一个栈的实现
- 可重入函数和不可重入函数
- JAVA不支持C++的模板
- new
- c语言队列的实现