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()函数开始修改:

 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值