3D Computer Grapihcs Using OpenGL - 20 结合Buffer

在上一节的案例中,我们使用了四个Buffer Object,立方体的VertexBuffer,立方体的索引Buffer,四面体的VertexBuffer,四面体的索引Buffer。

我们这节尝试把两个图形的Vertex Buffer结合,两个图形的索引Buffer结合,形成两个Buffer,让程序更加简化。

先看最终代码: MyGlWindow.cpp:

  1 #include <gl\glew.h>
  2 #include "MyGlWindow.h"
  3 #include <iostream>
  4 #include <fstream>
  5 #include <glm\gtc\matrix_transform.hpp>
  6 #include <glm\gtx\transform.hpp>
  7 #include <ShapeGenerator.h>
  8 #include <Qt3DInput\qmouseevent.h>
  9 #include <Qt3DInput\qkeyevent.h>
 10 
 11 
 12 GLuint programID;
 13 
 14 
 15 GLuint VertexBufferID;
 16 GLuint IndexBufferID;
 17 
 18 GLuint cubeVertexArrayObjectID;
 19 //立方体的索引数组长度
 20 GLuint cubeNumIndices;
 21 
 22 GLuint tetraVertexArrayObjectID;
 23 //四面体的索引数组长度
 24 GLuint tetraNumIndices;
 25 
 26 GLuint tetraIndexByteOffset;
 27 
 28 GLuint fullTransformUniformLocation;
 29 
 30 
 31 void MyGlWindow::sendDataToOpenGL()
 32 {
 33     //创建Cube
 34     ShapeData cube = ShapeGenerator::makeCube();
 35     //创建四面体
 36     ShapeData tetra = ShapeGenerator::makeTetrahedron();
 37 
 38     //创建和设置VertexBuffer
 39     glGenBuffers(1, &VertexBufferID);
 40     glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
 41     glBufferData(GL_ARRAY_BUFFER, cube.vertexBufferSize() + tetra.vertexBufferSize(),0, GL_STATIC_DRAW);
 42     glBufferSubData(GL_ARRAY_BUFFER, 0, cube.vertexBufferSize(), cube.vertices);
 43     glBufferSubData(GL_ARRAY_BUFFER, cube.vertexBufferSize(), tetra.vertexBufferSize(), tetra.vertices);
 44 
 45     //创建和设置IndexBuffer
 46     glGenBuffers(1, &IndexBufferID);
 47     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
 48     glBufferData(GL_ELEMENT_ARRAY_BUFFER, cube.indexBufferSize() + tetra.indexBufferSize() , 0, GL_STATIC_DRAW);
 49     glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, cube.indexBufferSize(), cube.indices);
 50     glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, cube.indexBufferSize(), tetra.indexBufferSize(), tetra.indices);
 51 
 52 
 53     cubeNumIndices = cube.numIndices;
 54     tetraNumIndices = tetra.numIndices;
 55 
 56 
 57 
 58     //设置绘制Cube的VAO
 59     //生成VAO
 60     glGenVertexArrays(1, &cubeVertexArrayObjectID);
 61     //绑定VAO,后续的一系列状态和设置都会存储在这个VAO里。
 62     glBindVertexArray(cubeVertexArrayObjectID);
 63     //开启通道1(位置)
 64     glEnableVertexAttribArray(0);
 65     //开启通道2(颜色)
 66     glEnableVertexAttribArray(1);
 67     //绑定顶点数据ID到绑定点
 68     glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
 69     //设置通道1如何获取数据
 70     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
 71     //设置通道2如何获取数据
 72     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)(sizeof(float) * 3));    
 73     //绑定索引数据ID到绑定点
 74     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
 75 
 76 
 77     //设置绘制四面体的VAO
 78     glGenVertexArrays(1, &tetraVertexArrayObjectID);
 79     glBindVertexArray(tetraVertexArrayObjectID);
 80     //开启通道1(位置)
 81     glEnableVertexAttribArray(0);
 82     //开启通道2(颜色)
 83     glEnableVertexAttribArray(1);
 84     //绑定顶点数据ID到绑定点
 85     glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
 86     //设置通道1如何获取数据
 87     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)(cube.vertexBufferSize()));
 88     //设置通道2如何获取数据
 89     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (void*)(cube.vertexBufferSize() +sizeof(float) * 3));
 90     //绑定索引数据ID到绑定点
 91     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
 92 
 93     tetraIndexByteOffset = cube.indexBufferSize();
 94 
 95     cube.cleanUp();
 96     tetra.cleanUp();
 97 }
 98 
 99 void MyGlWindow::installShaders()
100 {
101     //...
102 }
103 
104 void MyGlWindow::initializeGL()
105 {
106     glewInit();
107     glEnable(GL_DEPTH_TEST);
108     sendDataToOpenGL();
109     installShaders();
110 }
111 
112 void MyGlWindow::paintGL()
113 {
114     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
115     glViewport(0, 0, width(), height());
116 
117     //绑定cube的VAO,下面绘制的都是立方体--------------------------------------
118     glBindVertexArray(cubeVertexArrayObjectID);
119 
120     glm::mat4 fullTransformMatrix;
121     glm::mat4 viewToProjectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f);
122     glm::mat4 worldToViewMatrix = camera.getWorldToViewMatrix();
123     glm::mat4 worldToProjectionMatrix = viewToProjectionMatrix * worldToViewMatrix;
124 
125     //绘制Cube1
126     glm::mat4 cube1ModelToWorldMatrix =
127         glm::translate(glm::vec3(-1.0f, 0.0f, -3.0f))*
128         glm::rotate(36.0f, glm::vec3(1.0f, 0.0f, 0.0f));
129 
130     fullTransformMatrix = worldToProjectionMatrix * cube1ModelToWorldMatrix;
131     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
132     glDrawElements(GL_TRIANGLES, cubeNumIndices, GL_UNSIGNED_SHORT, 0);
133 
134     //绘制Cube2
135     glm::mat4 cube2ModelToWorldMatrix = 
136         glm::translate(glm::vec3(1.0f, 0.0f, -3.75f))*
137         glm::rotate(36.0f, glm::vec3(0.0f, 1.0f, 0.0f));
138     fullTransformMatrix = worldToProjectionMatrix * cube2ModelToWorldMatrix;
139     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
140     glDrawElements(GL_TRIANGLES, cubeNumIndices, GL_UNSIGNED_SHORT, 0);
141 
142     //绑定Tetra的VAO,下面绘制的都是四面体--------------------------------------
143     glBindVertexArray(tetraVertexArrayObjectID);
144 
145     //绘制Tetra1
146     glm::mat4 tetra1ModelToWorldMatrix =
147         glm::translate(glm::vec3(1.0f, -2.0f, -3.75f))*
148         glm::rotate(36.0f, glm::vec3(0.0f, 1.0f, 0.0f));
149     fullTransformMatrix = worldToProjectionMatrix * tetra1ModelToWorldMatrix;
150     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
151     glDrawElements(GL_TRIANGLES, tetraNumIndices, GL_UNSIGNED_SHORT, (void*)tetraIndexByteOffset);
152 
153     glm::mat4 tetra2ModelToWorldMatrix =
154         glm::translate(glm::vec3(-3.0f, -2.0f, -3.75f))*
155         glm::rotate(36.0f, glm::vec3(1.0f, 1.0f, 0.0f));
156     fullTransformMatrix = worldToProjectionMatrix * tetra2ModelToWorldMatrix;
157     glUniformMatrix4fv(fullTransformUniformLocation, 1, GL_FALSE, &fullTransformMatrix[0][0]);
158     glDrawElements(GL_TRIANGLES, tetraNumIndices, GL_UNSIGNED_SHORT, (void*)tetraIndexByteOffset);
159 }
160 
161 
162 std::string MyGlWindow::ReadShaderCode(const char* fileName)
163 {
164     //...
165 }
166 
167 void MyGlWindow::mouseMoveEvent(QMouseEvent * e)
168 {
169     //...
170 }
171 
172 void MyGlWindow::keyPressEvent(QKeyEvent * e)
173 {
174     //...
175 }

注意:为了方便起见,我们把原先setupVertexArray函数的内容全部移动到sendDataToOpenGL函数中了。

下面详解改动的过程:

  • 首先合并了两个顶点数组和两个索引数组, cubeVertexBufferID + tetraVertexBufferID -> VertexBufferID, cubeIndexBufferID + tetraIndexBufferID -> IndexBufferID。
  • 在sendDataToOpenGL()函数的一开始,我们先定义好两个图形。
  • 合并了顶点数组和索引数组以后,在初始化时候就要初始化足够的长度,在41行我们指定VertexBuffer的长度为: cube.vertexBufferSize() + tetra.vertexBufferSize(),正好容纳两个顶点数组,而数组内容暂时为空(第三个参数为0)。
  • 然后使用两个glBufferSubData函数分块给顶点数组填充内容,42,43行分别给VertexBuffer的第一部分和第二部分填充了数据,注意第二个和第三个参数,分别指定了开始的偏移值和数组的长度。这也是为什么42行的第二个参数为0,而43行的第二个参数为cube.vertexBuffer()的原因。
  • 顶点数组设定好以后,下面就设定索引数组(46-50),其设定方法和顶点数组类似,就不赘述了。
  • 我们把sendDataToOpenGL函数和setupVertexArray函数合并,并且把cube.cleanUp()和tetra.cleanUp()放在了最后,原因是后面还需要使用到cube对象的相关属性
  • 58-91行中只改动了87和89行的最后一个参数。这里的改动主要是针对VertexBuffer的,因为VertexBuffer发生了变化,所以四面体的位置和颜色信息在数组中都要往后整体偏移cube.vertexBufferSize()个位置。
  • 93行我们定义了一个tetraIndexByteOffset成员,用来记录立方体的索引Buffer的长度(绘制的时候要用)
  • 在绘制函数中,内容同样基本没变,只是改变了151行和158行的最后一个参数,这里是针对IndexBuffer的,因为glDrawElements()函数的最后一个参数是说明索引Buffer数组的偏移值的(顶点Buffer在前面已经用glVertexAttribPointer函数设置过了),因此,在绘制四面体时候需要向后偏移上一步我们记录的tetraIndexByteOffset个单位。绘制立方体则不需要偏移,因为它们仍处在数组的开头。

修改完成后,运行,结果和上节一样,但是逻辑上我们已经把四个数组简化成2个数组了。

PS:我们在这里需要对之前的代码加个“补丁”,我们之前创建的Buffers都没有删除,这会造成内存泄露,所以我们给MyGlWindow类加一个析构函数.

1 MyGlWindow::~MyGlWindow()
2 {
3     glDeleteBuffers(1, &VertexBufferID);
4     glDeleteBuffers(1, &IndexBufferID);
5     glUseProgram(0);
6     glDeleteProgram(programID);
7 }

把之前的VertexBuffer,IndexBuffer都删除掉。另外也要删除掉Program,但是主要在删除Program 之前需要glUseProgram(0)。

 

posted @ 2018-02-05 12:00  Ken_An  阅读(230)  评论(0编辑  收藏  举报