3D Computer Grapihcs Using OpenGL - 20 结合Buffer
在上一节的案例中,我们使用了四个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的更多相关文章
- 3D Computer Grapihcs Using OpenGL - 10 Color Buffer
本节我们将尝试利用三角形制作一个“走马灯”效果. 一个三角形如图示方式,从左向右依次移动. 先看一下代码: MyGlWindow.cpp #include <gl\glew.h> #inc ...
- 3D Computer Grapihcs Using OpenGL - 03 OpenGL Buffer Data
本节绘制一个三角形,并讲解Buffer Object-缓冲对象 OpenGL的窗口坐标 屏幕中心为坐标原点,横向朝右为x正方向,纵向朝上为y正方向,最大值最小值分别为1,-1. Buffer Obje ...
- 3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders
从这里就接触到了可编程图形渲染管线. 下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法. 我们的目标是使用这两个着色器给三角形填充绿色. 添 ...
- 3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体
我们使用15节学到的知识来绘制14节的立方体. 在第14节我们使用了两次glDrawElements实现了OpenGL实例化,发现这样仍然不太方便,如果需要绘制成千上万的立方体,就需要手写成千上万次的 ...
- 3D Computer Grapihcs Using OpenGL - 09 Enable Depth Test
启用Depth Test OpenGL是个3D绘图API,也就是说不只有xy坐标轴,还有第三个坐标轴z,z轴的方向是垂直于屏幕,指向屏幕内. 靠近人眼的方向是负方向,标准化设备坐标的最小值是-1, 最 ...
- 3D Computer Grapihcs Using OpenGL - 05 EBO
本节将采用两种方法绘制两个三角形. 先看第一种方法的代码 MyGlWindow.cpp #include <gl\glew.h> #include "MyGlWindow.h&q ...
- 3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)
大部分OpenGL教程都会在一开始就讲解VAO,但是该教程的作者认为这是很不合理的,因为要理解它的作用需要建立在我们此前学过的知识基础上.因此直到教程已经进行了一大半,作者才引入VAO这个概念.在我看 ...
- 3D Computer Grapihcs Using OpenGL - 17 添加相机(旋转)
在11节我们说过,MVP矩阵中目前只应用了两个矩阵,World to View 矩阵被省略了,这就导致我们的画面没有办法转换视角. 本节我们将添加这一环节,让相机可以旋转. 为了实现这一目的,我们添加 ...
- 3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced
友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度. OpenGL内置支持Instancing,有专门的函数来处理这件事情. 为了方 ...
随机推荐
- 委托、泛型委托、多播委托、匿名函数、lamda表达式、事件
1.为什么要使用委托 将一个方法作为参数传递给另一个方法 2.委托概念 public delegate int 委托名(int a, int b); 声明一个委托类型,可以用访问修饰符修饰,deleg ...
- POJ2387 Til the Cows Come Home (最短路 dijkstra)
AC代码 POJ2387 Til the Cows Come Home Bessie is out in the field and wants to get back to the barn to ...
- numpy-数据格式之 int 与 uint
概念 整型分为 有符号整型 和 无符号整型,其区别在于 无符号整型 可以存放的正数范围 比 有符号整型 大一倍,因为 有符号整型 将最高位存储符号,而 无符号整型 全部存储数字 # 1 111000 ...
- $NOI$ $2019$ 网络同步赛
D2T1考试前测了自己造的“假”极限数据,看了看内存发现是118MB,感觉没问题就交上去了. 赛后用Lemon测了一下:MLE 88->0 对于别的题我已经不想再说什么了. update: 由于 ...
- phpmyadmin导入大容量.sql文件
phpmyadmin导入大容量.sql文件 在phpmyadmin目录文件夹下建立一个文件夹,如importSqlFile 将想要导入的sql文件放入importSqlFile文件夹中 打开confi ...
- git上传文件夹报错: ! [rejected] master -> master (fetch first) error: failed to push some refs to 'https://github.com/taminachen/rjxm.git' hint: Updates were rejected because the remote contains work
使用git上传本地文件夹到远程仓库,使用如下命令:git push -u origin master时报错 原因是在GitHub创建仓库时创建了readme文件,但是本地没有这个文件,造成本地目录与远 ...
- 点亮指路灯led
为什么要使用LED? (bootloader,kernel)开发初期,由于串口等硬件尚未被初始化,因此调试手段相当有限,这时通常会采用LED来做为程序调试的重要手段. LED驱动设计: 1.设置GPI ...
- Dubble 入门
Dubbo 01 架构模型 传统架构 All in One 测试麻烦,微小修改 全都得重新测 单体架构也称之为单体系统或者是单体应用.就是一种把系统中所有的功能.模块耦合在一个应用中的架构方式.其优点 ...
- 使用IL DASM来查看接口内的自动属性
在我的本地地址中 C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\x64下有一个文件 ildas ...
- $.getJSON同步和异步
$.ajaxSettings.async = false; $.getJSON(url, data, function(data){ }); $.getJSON(url, data, function ...