3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体
我们使用15节学到的知识来绘制14节的立方体。
在第14节我们使用了两次glDrawElements实现了OpenGL实例化,发现这样仍然不太方便,如果需要绘制成千上万的立方体,就需要手写成千上万次的glDrawElements().
而15节我们知道了glDrawElementsInstanced函数可以支持批量绘制。
我们需要绘制的多个立方体唯一不同的只是转换矩阵,为了达到这个目的,我们只需要定义一组不同的变换矩阵,采用glDrawElementsInstanced即可达到目的。
构建矩阵的更简便写法
在进行之前,先介绍一个创建平移和旋转矩阵的更简单的写法:
我们此前构建平移和旋转矩阵都是使用如下的语法:
- glm::translate(glm::mat4(), glm::vec3(0.0f,0.0f,3.0f)); //平移矩阵
- glm::rotate(glm::mat4(), 125.0f, glm::vec3(1.0f, 0.0f,0.0f)); //旋转矩阵
发现第一个参数都需要一个矩阵,这个写法不利于我们本节的实践。
有一个简化的方法:
引入头文件<glm\gtx\transform.hpp>
然后上述两个矩阵的构建都可以省略掉第一个参数。
- glm::translate(glm::vec3(0.0f,0.0f,3.0f)); //平移矩阵
- glm::rotate(125.0f, glm::vec3(1.0f, 0.0f,0.0f)); //旋转矩阵
准备数据
先从修改sendDataToOpenGL()函数开始修改:
- void MyGlWindow::sendDataToOpenGL()
- {
- ShapeData shape = ShapeGenerator::makeCube();
- GLuint vertexBufferID;
- glGenBuffers(, &vertexBufferID);
- glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
- glBufferData(GL_ARRAY_BUFFER, shape.vertexBufferSize(), shape.vertices, GL_STATIC_DRAW);
- glEnableVertexAttribArray();
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , );
- GLuint indexBufferID;
- glGenBuffers(, &indexBufferID);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape.indexBufferSize(), shape.indices, GL_STATIC_DRAW);
- glEnableVertexAttribArray();
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (char*)(sizeof(GLfloat) * ));
- numIndices = shape.numIndices;
- shape.cleanUp();
- //instancing
- glm::mat4 projectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f);
- glm::mat4 fullTransforms[] =
- {
- projectionMatrix * glm::translate(glm::vec3(0.0f, 0.0f, -3.0f)) * glm::rotate(54.0f,glm::vec3(1.0f, 0.0f, 0.0f)),
- projectionMatrix * glm::translate(glm::vec3(2.0f, 0.0f, -4.0f)) * glm::rotate(126.0f, glm::vec3(0.0f, 1.0f, 0.0f))
- };
- GLuint transformMatrixBufferID;
- glGenBuffers(, &transformMatrixBufferID);
- glBindBuffer(GL_ARRAY_BUFFER, transformMatrixBufferID);
- glBufferData(GL_ARRAY_BUFFER, sizeof(fullTransforms), fullTransforms, GL_STATIC_DRAW);
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
- glEnableVertexAttribArray();
- glEnableVertexAttribArray();
- glEnableVertexAttribArray();
- glEnableVertexAttribArray();
- glVertexAttribDivisor(, );
- glVertexAttribDivisor(, );
- glVertexAttribDivisor(, );
- glVertexAttribDivisor(, );
- }
从27行起是新增内容,27行定义了一个projectionMatrix。
29-33行定义了一个“变换矩阵数组”,包含两个元素。它们的形式都是 projectionMatrix * translationMatrix * rotationMatrix。注意这里的tranlationMatrix和rotationMatrix都使用了前面介绍的简化方法去定义。
37行再次绑定transformMatrixBuffer到GL_ARRAY_BUFFER上,可能会有疑问:之前给GL_ARRAY_BUFFER绑定了Vertex Buffer(8行),这里又绑定其他的东西,会把之前绑定的数据破坏掉吗?
答案是不会,只要在这之前使用了glAttribPointer函数(12行),之前绑定的数据就是安全的。
39-50行和上节学习的内容是相似的,但是这里用到了4组命令,原因是一个mat4要被当做四个float对待。
还要修改paintGL():
- void MyGlWindow::paintGL()
- {
- glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
- glViewport(, , width(), height());
- glDrawElementsInstanced(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, , );
- }
我们看到14节中paintGL()中的很多代码都不见了,原因是这些工作都在sendDataToOpenGL()中准备了。这里只需要使用一个glDrawElementsInstanced函数去进行批量绘制即可。
再看修改后的VertexShader:
- #version
- in layout(location=) vec3 position;
- in layout(location=) vec3 color;
- in layout(location=) mat4 fullTransformMatrix;
- out vec3 passingColor;
- void main()
- {
- gl_Position = fullTransformMatrix * vec4(position,);
- passingColor= color;
- }
第五行虽然只有一个location=2,但是对应的sendDataToOpenGL()中却有2,3,4,5 四个通道,原因是我们也要把这里的mat4当做四个float对待,可以想象成这样的情景:
- in layout(location=) vec4 fullTransformMatrix_part1;
- in layout(location=) vec4 fullTransformMatrix_part2;
- in layout(location=) vec4 fullTransformMatrix_part3;
- in layout(location=) vec4 fullTransformMatrix_part4;
编译运行后得到的结果和第14节是一样的。
这样做的好处是扩展起来非常容易,例如我们需要再多绘制几个立方体,只需要向sendDataToOpenGL()函数中的fullTransforms数组中增加元素,并修改paintLG()函数中第5行的最后一个参数即可。
3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体的更多相关文章
- 3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)
大部分OpenGL教程都会在一开始就讲解VAO,但是该教程的作者认为这是很不合理的,因为要理解它的作用需要建立在我们此前学过的知识基础上.因此直到教程已经进行了一大半,作者才引入VAO这个概念.在我看 ...
- 3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced
友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度. OpenGL内置支持Instancing,有专门的函数来处理这件事情. 为了方 ...
- 3D Computer Grapihcs Using OpenGL - 09 Enable Depth Test
启用Depth Test OpenGL是个3D绘图API,也就是说不只有xy坐标轴,还有第三个坐标轴z,z轴的方向是垂直于屏幕,指向屏幕内. 靠近人眼的方向是负方向,标准化设备坐标的最小值是-1, 最 ...
- 3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders
从这里就接触到了可编程图形渲染管线. 下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法. 我们的目标是使用这两个着色器给三角形填充绿色. 添 ...
- 3D Computer Grapihcs Using OpenGL - 03 OpenGL Buffer Data
本节绘制一个三角形,并讲解Buffer Object-缓冲对象 OpenGL的窗口坐标 屏幕中心为坐标原点,横向朝右为x正方向,纵向朝上为y正方向,最大值最小值分别为1,-1. Buffer Obje ...
- 3D Computer Grapihcs Using OpenGL - 14 OpenGL Instancing
如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...
- 3D Computer Grapihcs Using OpenGL - 11 Model View Projection Matrices
本节我们将绘制一个3维物体,立方体. 如果要渲染3D物体,我们需要了解MVP(Model View Projection),它表示三个转换矩阵.实际上这个名字不够明确,更加确切的释义如下: Model ...
- 3D Computer Grapihcs Using OpenGL - 10 Color Buffer
本节我们将尝试利用三角形制作一个“走马灯”效果. 一个三角形如图示方式,从左向右依次移动. 先看一下代码: MyGlWindow.cpp #include <gl\glew.h> #inc ...
- 3D Computer Grapihcs Using OpenGL - 05 EBO
本节将采用两种方法绘制两个三角形. 先看第一种方法的代码 MyGlWindow.cpp #include <gl\glew.h> #include "MyGlWindow.h&q ...
随机推荐
- spring boot-12.Servlet 容器
1.spring boot 默认使用的是嵌入式的Servlet容器,spring-boot-starter-web 依赖了spring-boot-satrter-tomcat就是引入了嵌入式的tomc ...
- Windown Server 2008配置tomcat9虚拟路径
一.用途 用于保存项目运产生的文件 二.步骤 1.修改conf\下的web.xml <!-- 找到listings将false改为true --> <init-para ...
- css样式实例
* { box-sizing: border-box; } /*box-sizing属性允许您以特定的方式定义匹配某个区域的特定元素*/ body { font: Arial; margin:; } ...
- json格式和对象类型的转换20170330
(1)对象的类型转换成字符串类型(或者更确切的说是json类型的) JSONObject jo = JSONObject.fromObject(map);常见的java代码转换成json 比如:后台J ...
- node.js--使用Express中app.use()分模块开发
app.use(path,callback)中的callback既可以是router对象又可以是函数:将一个URL路径与一个函数绑定,第一个参数为访问的路径,如果第一参数为空,则表示任何路径都触发这个 ...
- 单点登录之ajax跨域实现
需求:相同根域名或不同根域名的两个域名,实现单点登录登出 原理: 以b站为例,b站的账号登录域名为passport.bilibili.com.主站为www.bilibili.com,游戏站为www.b ...
- Delphi 基于组件的编程思想
- [转]为什么要引入nullptr?
来源:https://cloud.tencent.com/developer/ask/69685 C++11介绍nullptr,它被称为Null指针常数及其提高类型安全性和解决不明确的情况与现有实现相 ...
- dedecms织梦调用二级和三级分类标签
dedecms调用二级.三级以及调用栏目所有子栏目 <!--频道分类具体内容开始--> <div class="channel_sort"> {dede:c ...
- zabbix的简单操作(查看监控,自定义监控和钉钉监控报警)
zabbix是一种监控软件,我用的是centos7.5版本 一:我已经添加好主机了,接下来就是看看怎么查看监控内容的 1.打开zabbix服务的web网页 2.检测最新数据,要在最新数据中筛选 3.查 ...