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

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

先看最终代码: MyGlWindow.cpp:

 #include <gl\glew.h>
#include "MyGlWindow.h"
#include <iostream>
#include <fstream>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtx\transform.hpp>
#include <ShapeGenerator.h>
#include <Qt3DInput\qmouseevent.h>
#include <Qt3DInput\qkeyevent.h> GLuint programID; GLuint VertexBufferID;
GLuint IndexBufferID; GLuint cubeVertexArrayObjectID;
//立方体的索引数组长度
GLuint cubeNumIndices; GLuint tetraVertexArrayObjectID;
//四面体的索引数组长度
GLuint tetraNumIndices; GLuint tetraIndexByteOffset; GLuint fullTransformUniformLocation; void MyGlWindow::sendDataToOpenGL()
{
//创建Cube
ShapeData cube = ShapeGenerator::makeCube();
//创建四面体
ShapeData tetra = ShapeGenerator::makeTetrahedron(); //创建和设置VertexBuffer
glGenBuffers(, &VertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
glBufferData(GL_ARRAY_BUFFER, cube.vertexBufferSize() + tetra.vertexBufferSize(),, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, , cube.vertexBufferSize(), cube.vertices);
glBufferSubData(GL_ARRAY_BUFFER, cube.vertexBufferSize(), tetra.vertexBufferSize(), tetra.vertices); //创建和设置IndexBuffer
glGenBuffers(, &IndexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, cube.indexBufferSize() + tetra.indexBufferSize() , , GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, , cube.indexBufferSize(), cube.indices);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, cube.indexBufferSize(), tetra.indexBufferSize(), tetra.indices); cubeNumIndices = cube.numIndices;
tetraNumIndices = tetra.numIndices; //设置绘制Cube的VAO
//生成VAO
glGenVertexArrays(, &cubeVertexArrayObjectID);
//绑定VAO,后续的一系列状态和设置都会存储在这个VAO里。
glBindVertexArray(cubeVertexArrayObjectID);
//开启通道1(位置)
glEnableVertexAttribArray();
//开启通道2(颜色)
glEnableVertexAttribArray();
//绑定顶点数据ID到绑定点
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
//设置通道1如何获取数据
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , );
//设置通道2如何获取数据
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (void*)(sizeof(float) * ));
//绑定索引数据ID到绑定点
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID); //设置绘制四面体的VAO
glGenVertexArrays(, &tetraVertexArrayObjectID);
glBindVertexArray(tetraVertexArrayObjectID);
//开启通道1(位置)
glEnableVertexAttribArray();
//开启通道2(颜色)
glEnableVertexAttribArray();
//绑定顶点数据ID到绑定点
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
//设置通道1如何获取数据
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (void*)(cube.vertexBufferSize()));
//设置通道2如何获取数据
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (void*)(cube.vertexBufferSize() +sizeof(float) * ));
//绑定索引数据ID到绑定点
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID); tetraIndexByteOffset = cube.indexBufferSize(); cube.cleanUp();
tetra.cleanUp();
} void MyGlWindow::installShaders()
{
//...
} void MyGlWindow::initializeGL()
{
glewInit();
glEnable(GL_DEPTH_TEST);
sendDataToOpenGL();
installShaders();
} void MyGlWindow::paintGL()
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(, , width(), height()); //绑定cube的VAO,下面绘制的都是立方体--------------------------------------
glBindVertexArray(cubeVertexArrayObjectID); glm::mat4 fullTransformMatrix;
glm::mat4 viewToProjectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f);
glm::mat4 worldToViewMatrix = camera.getWorldToViewMatrix();
glm::mat4 worldToProjectionMatrix = viewToProjectionMatrix * worldToViewMatrix; //绘制Cube1
glm::mat4 cube1ModelToWorldMatrix =
glm::translate(glm::vec3(-1.0f, 0.0f, -3.0f))*
glm::rotate(36.0f, glm::vec3(1.0f, 0.0f, 0.0f)); fullTransformMatrix = worldToProjectionMatrix * cube1ModelToWorldMatrix;
glUniformMatrix4fv(fullTransformUniformLocation, , GL_FALSE, &fullTransformMatrix[][]);
glDrawElements(GL_TRIANGLES, cubeNumIndices, GL_UNSIGNED_SHORT, ); //绘制Cube2
glm::mat4 cube2ModelToWorldMatrix =
glm::translate(glm::vec3(1.0f, 0.0f, -3.75f))*
glm::rotate(36.0f, glm::vec3(0.0f, 1.0f, 0.0f));
fullTransformMatrix = worldToProjectionMatrix * cube2ModelToWorldMatrix;
glUniformMatrix4fv(fullTransformUniformLocation, , GL_FALSE, &fullTransformMatrix[][]);
glDrawElements(GL_TRIANGLES, cubeNumIndices, GL_UNSIGNED_SHORT, ); //绑定Tetra的VAO,下面绘制的都是四面体--------------------------------------
glBindVertexArray(tetraVertexArrayObjectID); //绘制Tetra1
glm::mat4 tetra1ModelToWorldMatrix =
glm::translate(glm::vec3(1.0f, -2.0f, -3.75f))*
glm::rotate(36.0f, glm::vec3(0.0f, 1.0f, 0.0f));
fullTransformMatrix = worldToProjectionMatrix * tetra1ModelToWorldMatrix;
glUniformMatrix4fv(fullTransformUniformLocation, , GL_FALSE, &fullTransformMatrix[][]);
glDrawElements(GL_TRIANGLES, tetraNumIndices, GL_UNSIGNED_SHORT, (void*)tetraIndexByteOffset); glm::mat4 tetra2ModelToWorldMatrix =
glm::translate(glm::vec3(-3.0f, -2.0f, -3.75f))*
glm::rotate(36.0f, glm::vec3(1.0f, 1.0f, 0.0f));
fullTransformMatrix = worldToProjectionMatrix * tetra2ModelToWorldMatrix;
glUniformMatrix4fv(fullTransformUniformLocation, , GL_FALSE, &fullTransformMatrix[][]);
glDrawElements(GL_TRIANGLES, tetraNumIndices, GL_UNSIGNED_SHORT, (void*)tetraIndexByteOffset);
} std::string MyGlWindow::ReadShaderCode(const char* fileName)
{
//...
} void MyGlWindow::mouseMoveEvent(QMouseEvent * e)
{
//...
} void MyGlWindow::keyPressEvent(QKeyEvent * e)
{
//...
}

注意:为了方便起见,我们把原先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类加一个析构函数.

 MyGlWindow::~MyGlWindow()
{
glDeleteBuffers(, &VertexBufferID);
glDeleteBuffers(, &IndexBufferID);
glUseProgram();
glDeleteProgram(programID);
}

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

3D Computer Grapihcs Using OpenGL - 20 结合Buffer的更多相关文章

  1. 3D Computer Grapihcs Using OpenGL - 10 Color Buffer

    本节我们将尝试利用三角形制作一个“走马灯”效果. 一个三角形如图示方式,从左向右依次移动. 先看一下代码: MyGlWindow.cpp #include <gl\glew.h> #inc ...

  2. 3D Computer Grapihcs Using OpenGL - 03 OpenGL Buffer Data

    本节绘制一个三角形,并讲解Buffer Object-缓冲对象 OpenGL的窗口坐标 屏幕中心为坐标原点,横向朝右为x正方向,纵向朝上为y正方向,最大值最小值分别为1,-1. Buffer Obje ...

  3. 3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders

    从这里就接触到了可编程图形渲染管线. 下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法. 我们的目标是使用这两个着色器给三角形填充绿色. 添 ...

  4. 3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体

    我们使用15节学到的知识来绘制14节的立方体. 在第14节我们使用了两次glDrawElements实现了OpenGL实例化,发现这样仍然不太方便,如果需要绘制成千上万的立方体,就需要手写成千上万次的 ...

  5. 3D Computer Grapihcs Using OpenGL - 09 Enable Depth Test

    启用Depth Test OpenGL是个3D绘图API,也就是说不只有xy坐标轴,还有第三个坐标轴z,z轴的方向是垂直于屏幕,指向屏幕内. 靠近人眼的方向是负方向,标准化设备坐标的最小值是-1, 最 ...

  6. 3D Computer Grapihcs Using OpenGL - 05 EBO

    本节将采用两种方法绘制两个三角形. 先看第一种方法的代码 MyGlWindow.cpp #include <gl\glew.h> #include "MyGlWindow.h&q ...

  7. 3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)

    大部分OpenGL教程都会在一开始就讲解VAO,但是该教程的作者认为这是很不合理的,因为要理解它的作用需要建立在我们此前学过的知识基础上.因此直到教程已经进行了一大半,作者才引入VAO这个概念.在我看 ...

  8. 3D Computer Grapihcs Using OpenGL - 17 添加相机(旋转)

    在11节我们说过,MVP矩阵中目前只应用了两个矩阵,World to View 矩阵被省略了,这就导致我们的画面没有办法转换视角. 本节我们将添加这一环节,让相机可以旋转. 为了实现这一目的,我们添加 ...

  9. 3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced

    友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度. OpenGL内置支持Instancing,有专门的函数来处理这件事情. 为了方 ...

随机推荐

  1. Dubbo从入门到精通

    1.在Dubbo中注解的使用 2.Dubbo启动时qos-server can not bind localhost:22222错误解决 3.Dubbo配置方式详解

  2. start-all.sh启动HDFS,datanode没有启动

    第一次格式化dfs后,启动并使用hadoop,之后如果再次重新执行了格式化(hdfs namenode -format) start-all.sh启动时就会遇到datanode无法启动的问题,通常情况 ...

  3. Java static基本认知

    一. static的用途 在Java编程思想中有这么一句话:“static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来是可以的.而且可以在没有创建任何对象的前提下,仅仅 ...

  4. http://www.pythontutor.com/visualize.html#mode=edit python在线检测代码

    http://www.pythontutor.com/visualize.html#mode=edit

  5. javaweb:关于HttpServletRequest介绍 (转)

    出处:https://www.cnblogs.com/xdp-gacl/p/3798347.html 一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的 ...

  6. C++11随机数的正确打开方式

    C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...

  7. synchronized锁住的是代码还是对象,以及synchronized底层实现原理

    synchronized (this)原理:涉及两条指令:monitorenter,monitorexit:再说同步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令monitorenter和 ...

  8. vue-Ie下踩坑20190820

    开发的vue项目终于告一段落,终于是可以部署在测试服务器给客户看了,期间也是遇到各式各样的坑. 1.有些bug在谷歌是在没问题的,在ie上就会暴露出来,因为其中一个模块设计多个类型资源的列表,所以写纯 ...

  9. 日语能力考试N2必备训读动词

    日语能力考试N2必备训读动词 ア合う——あう——「自」合一.合到一起.准确味わう——あじわう——「他」品味.品尝預かる——あずかる——「他」照顾.保管.承担預ける——あずける——「他」寄存.处理难以了 ...

  10. Linux上安装ElasticSearch及遇到的问题

    在Linux上安装ElasticSearch 1. 安装前环境准备 安装JDK环境,并配置环境变量,这里可以参考我以前写过的博客 https://www.cnblogs.com/ywb-article ...