我们使用15节学到的知识来绘制14节的立方体。

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

而15节我们知道了glDrawElementsInstanced函数可以支持批量绘制。

我们需要绘制的多个立方体唯一不同的只是转换矩阵,为了达到这个目的,我们只需要定义一组不同的变换矩阵,采用glDrawElementsInstanced即可达到目的。

构建矩阵的更简便写法

在进行之前,先介绍一个创建平移和旋转矩阵的更简单的写法:

我们此前构建平移和旋转矩阵都是使用如下的语法:

  1. glm::translate(glm::mat4(), glm::vec3(0.0f,0.0f,3.0f)); //平移矩阵
  2.  
  3. glm::rotate(glm::mat4(), 125.0f, glm::vec3(1.0f, 0.0f,0.0f)); //旋转矩阵

发现第一个参数都需要一个矩阵,这个写法不利于我们本节的实践。

有一个简化的方法:

引入头文件<glm\gtx\transform.hpp>

然后上述两个矩阵的构建都可以省略掉第一个参数。

  1. glm::translate(glm::vec3(0.0f,0.0f,3.0f)); //平移矩阵
  2.  
  3. glm::rotate(125.0f, glm::vec3(1.0f, 0.0f,0.0f)); //旋转矩阵

准备数据

先从修改sendDataToOpenGL()函数开始修改:

  1. void MyGlWindow::sendDataToOpenGL()
  2. {
  3.  
  4. ShapeData shape = ShapeGenerator::makeCube();
  5.  
  6. GLuint vertexBufferID;
  7. glGenBuffers(, &vertexBufferID);
  8. glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
  9. glBufferData(GL_ARRAY_BUFFER, shape.vertexBufferSize(), shape.vertices, GL_STATIC_DRAW);
  10.  
  11. glEnableVertexAttribArray();
  12. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , );
  13.  
  14. GLuint indexBufferID;
  15. glGenBuffers(, &indexBufferID);
  16. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
  17. glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape.indexBufferSize(), shape.indices, GL_STATIC_DRAW);
  18.  
  19. glEnableVertexAttribArray();
  20. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (char*)(sizeof(GLfloat) * ));
  21.  
  22. numIndices = shape.numIndices;
  23. shape.cleanUp();
  24.  
  25. //instancing
  26. glm::mat4 projectionMatrix = glm::perspective(30.0f, ((float)width()) / height(), 0.1f, 10.0f);
  27.  
  28. glm::mat4 fullTransforms[] =
  29. {
  30. projectionMatrix * glm::translate(glm::vec3(0.0f, 0.0f, -3.0f)) * glm::rotate(54.0f,glm::vec3(1.0f, 0.0f, 0.0f)),
  31. projectionMatrix * glm::translate(glm::vec3(2.0f, 0.0f, -4.0f)) * glm::rotate(126.0f, glm::vec3(0.0f, 1.0f, 0.0f))
  32. };
  33.  
  34. GLuint transformMatrixBufferID;
  35. glGenBuffers(, &transformMatrixBufferID);
  36. glBindBuffer(GL_ARRAY_BUFFER, transformMatrixBufferID);
  37. glBufferData(GL_ARRAY_BUFFER, sizeof(fullTransforms), fullTransforms, GL_STATIC_DRAW);
  38. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
  39. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
  40. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
  41. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * ));
  42. glEnableVertexAttribArray();
  43. glEnableVertexAttribArray();
  44. glEnableVertexAttribArray();
  45. glEnableVertexAttribArray();
  46. glVertexAttribDivisor(, );
  47. glVertexAttribDivisor(, );
  48. glVertexAttribDivisor(, );
  49. glVertexAttribDivisor(, );
  50. }

从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():

  1. void MyGlWindow::paintGL()
  2. {
  3. glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
  4. glViewport(, , width(), height());
  5. glDrawElementsInstanced(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, , );
  6. }

我们看到14节中paintGL()中的很多代码都不见了,原因是这些工作都在sendDataToOpenGL()中准备了。这里只需要使用一个glDrawElementsInstanced函数去进行批量绘制即可。

再看修改后的VertexShader:

  1. #version
  2.  
  3. in layout(location=) vec3 position;
  4. in layout(location=) vec3 color;
  5. in layout(location=) mat4 fullTransformMatrix;
  6.  
  7. out vec3 passingColor;
  8.  
  9. void main()
  10. {
  11. gl_Position = fullTransformMatrix * vec4(position,);
  12. passingColor= color;
  13. }

第五行虽然只有一个location=2,但是对应的sendDataToOpenGL()中却有2,3,4,5 四个通道,原因是我们也要把这里的mat4当做四个float对待,可以想象成这样的情景:

  1. in layout(location=) vec4 fullTransformMatrix_part1;
  2. in layout(location=) vec4 fullTransformMatrix_part2;
  3. in layout(location=) vec4 fullTransformMatrix_part3;
  4. in layout(location=) vec4 fullTransformMatrix_part4;

编译运行后得到的结果和第14节是一样的。

这样做的好处是扩展起来非常容易,例如我们需要再多绘制几个立方体,只需要向sendDataToOpenGL()函数中的fullTransforms数组中增加元素,并修改paintLG()函数中第5行的最后一个参数即可。

3D Computer Grapihcs Using OpenGL - 16 使用DrawElementsInstanced绘制立方体的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. 3D Computer Grapihcs Using OpenGL - 14 OpenGL Instancing

    如果我们需要绘制两个(或者多个)一样的立方体(或者物体),只是位置.缩放.旋转不一样,那么我们可以不需要多次将这个物体的顶点信息.颜色信息等发送到显卡,而是发送一次,绘制多次,仅仅是每次绘制之前应用不 ...

  7. 3D Computer Grapihcs Using OpenGL - 11 Model View Projection Matrices

    本节我们将绘制一个3维物体,立方体. 如果要渲染3D物体,我们需要了解MVP(Model View Projection),它表示三个转换矩阵.实际上这个名字不够明确,更加确切的释义如下: Model ...

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

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

  9. 3D Computer Grapihcs Using OpenGL - 05 EBO

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

随机推荐

  1. spring boot-12.Servlet 容器

    1.spring boot 默认使用的是嵌入式的Servlet容器,spring-boot-starter-web 依赖了spring-boot-satrter-tomcat就是引入了嵌入式的tomc ...

  2. Windown Server 2008配置tomcat9虚拟路径

    一.用途 用于保存项目运产生的文件 二.步骤 1.修改conf\下的web.xml <!-- 找到listings将false改为true -->        <init-para ...

  3. css样式实例

    * { box-sizing: border-box; } /*box-sizing属性允许您以特定的方式定义匹配某个区域的特定元素*/ body { font: Arial; margin:; } ...

  4. json格式和对象类型的转换20170330

    (1)对象的类型转换成字符串类型(或者更确切的说是json类型的) JSONObject jo = JSONObject.fromObject(map);常见的java代码转换成json 比如:后台J ...

  5. node.js--使用Express中app.use()分模块开发

    app.use(path,callback)中的callback既可以是router对象又可以是函数:将一个URL路径与一个函数绑定,第一个参数为访问的路径,如果第一参数为空,则表示任何路径都触发这个 ...

  6. 单点登录之ajax跨域实现

    需求:相同根域名或不同根域名的两个域名,实现单点登录登出 原理: 以b站为例,b站的账号登录域名为passport.bilibili.com.主站为www.bilibili.com,游戏站为www.b ...

  7. Delphi 基于组件的编程思想

  8. [转]为什么要引入nullptr?

    来源:https://cloud.tencent.com/developer/ask/69685 C++11介绍nullptr,它被称为Null指针常数及其提高类型安全性和解决不明确的情况与现有实现相 ...

  9. dedecms织梦调用二级和三级分类标签

    dedecms调用二级.三级以及调用栏目所有子栏目 <!--频道分类具体内容开始--> <div class="channel_sort"> {dede:c ...

  10. zabbix的简单操作(查看监控,自定义监控和钉钉监控报警)

    zabbix是一种监控软件,我用的是centos7.5版本 一:我已经添加好主机了,接下来就是看看怎么查看监控内容的 1.打开zabbix服务的web网页 2.检测最新数据,要在最新数据中筛选 3.查 ...