热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

3DComputerGrapihcsUsingOpenGL16使用DrawElementsInstanced绘制立方体

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

我们使用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::translate(glm::vec3(0.0f,0.0f,3.0f)); //平移矩阵

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

从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(0, 0, width(), height());
5 glDrawElementsInstanced(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0, 2);
6 }

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

再看修改后的VertexShader:

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

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

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

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

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

 

转:https://www.cnblogs.com/AnKen/p/8378283.html



推荐阅读
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 如何在HTML中获取鼠标的当前位置
    本文介绍了在HTML中获取鼠标当前位置的三种方法,分别是相对于屏幕的位置、相对于窗口的位置以及考虑了页面滚动因素的位置。通过这些方法可以准确获取鼠标的坐标信息。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 解决github访问慢的问题的方法集锦
    本文总结了国内用户在访问github网站时可能遇到的加载慢的问题,并提供了解决方法,其中包括修改hosts文件来加速访问。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • 本文介绍了使用数据库管理员用户执行onstat -l命令来监控GBase8s数据库的物理日志和逻辑日志的使用情况,并强调了对已使用的逻辑日志是否及时备份的重要性。同时提供了监控方法和注意事项。 ... [详细]
  • 本文介绍了解决mysql 5.1启动问题的方法,通过修改my.ini文件中的相关配置,包括innodb_data_home_dir和skip-innodb等,可以解决启动问题。同时还介绍了如何调整内存池来存储metadata信息。 ... [详细]
  • php缓存ri,浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
    thinkPHP的F方法只能用于缓存简单数据类型,不支持有效期和缓存对象。S()缓存方法支持有效期,又称动态缓存方法。本文是小编日常整理有关thinkp ... [详细]
  • 使用freemaker生成Java代码的步骤及示例代码
    本文介绍了使用freemaker这个jar包生成Java代码的步骤,通过提前编辑好的模板,可以避免写重复代码。首先需要在springboot的pom.xml文件中加入freemaker的依赖包。然后编写模板,定义要生成的Java类的属性和方法。最后编写生成代码的类,通过加载模板文件和数据模型,生成Java代码文件。本文提供了示例代码,并展示了文件目录结构。 ... [详细]
  • 流数据流和IO流的使用及应用
    本文介绍了流数据流和IO流的基本概念和用法,包括输入流、输出流、字节流、字符流、缓冲区等。同时还介绍了异常处理和常用的流类,如FileReader、FileWriter、FileInputStream、FileOutputStream、OutputStreamWriter、InputStreamReader、BufferedReader、BufferedWriter等。此外,还介绍了系统流和标准流的使用。 ... [详细]
author-avatar
世妖娆灬
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有