OpenGL矩阵堆栈

前言

回顾OpenGL在同一场景绘制不同模型的过程。

同一场景不同模型

在单个场景中绘制不同的简单模型,可以为每个模型分别使用单独的缓冲区,如果有需要(如涉及复杂光照或者对象由不同图元组成等情况),还可以设置不同的着色器程序进行渲染。这些简单模型之间没有内在联系,因此只需要通过模型矩阵改变其在场景中的位置,然后按“流程”输出即可。
一种更加复杂的情况是分层模型。分层模型由多个简单模型组装而成,且这些简单模型之间存在一定的内在联系。行星系统一个典型的例子——地球围着太阳转,月球围着地球转。分层模型中较难解决的部分是如何跟踪所有子模型的模型—视图矩阵并完美地协调它们。我们可以使用矩阵堆栈来处理此操作。

矩阵堆栈

矩阵堆栈:一堆变换的矩阵。在旧的固定功能管线中,可以使用OpenGL内置矩阵堆栈。新的功能管线中,可以使用stack来进行模拟。

矩阵堆栈中的第一个矩阵通常是视图矩阵。之后的矩阵是建立在其之上的,复杂程度越来越高的模型—视图矩阵,应用了更多的模型变换,可以直接应用,也可以先结合其它矩阵。

//以简单太阳系统模拟示例:太阳——地球——月球
……
glm::mat4 pMat,vMat,mMat;	
stack<glm::mat4>mvStack;	
……
mvStack.push(vMat);		//基础视图矩阵
//--------------金字塔 == “太阳”,1号缓冲区--------------------
mvStack.push(mvStack.top());
mvStack.top() *= translate(mat4(1.0f), pyrLoc);	//“太阳”位置
mvStack.push(mvStack.top());
mvStack.top() *= rotate(mat4(1.0f), (float)currentTime, vec3(0.0f, 1.0f, 0.0f));	//“太阳”自转
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, value_ptr(mvStack.top()));	//将“太阳”模型-视图矩阵传递给着色器
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, 18);	//绘制“太阳”
mvStack.pop();

//--------------立方体 == “地球”,0号缓冲区-------------------------
mvStack.push(mvStack.top());
mvStack.top() *= translate(mat4(1.0f), vec3(sin((float)currentTime)*4.0f, 0.0f, cos((float)currentTime)*4.0f));	//地球公转
mvStack.push(mvStack.top());
mvStack.top() *= rotate(mat4(1.0f), (float)currentTime, vec3(0.0f, 1.0f, 0.0f));	//地球自转
mvStack.top() *= scale(mat4(1.0f), vec3(0.5f));
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, value_ptr(mvStack.top()));
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, 36);	//绘制“地球”
mvStack.pop();

//--------------小立方体 == "月球",0号缓冲区------------------------
mvStack.push(mvStack.top());
mvStack.top() *= translate(mat4(1.0f), vec3(0.0f, sin((float)currentTime)*2.0f, cos((float)currentTime)*2.0f));	//“月球”公转
mvStack.push(mvStack.top());
mvStack.top() *= rotate(mat4(1.0f), (float)currentTime, vec3(0.0f, 0.0f, 1.0f));	//“月球”自转
mvStack.top() *= scale(mat4(1.0f), vec3(0.25f));	
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, value_ptr(mvStack.top()));
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, 36);	//绘制“月球”
mvStack.pop();

//从堆栈中移除月球缩放、旋转、位置矩阵,行星位置矩阵,太阳位置矩阵和视图矩阵
mvStack.pop();mvStack.pop();mvStack.pop();mvStack.pop();

上述代码中,矩阵堆栈数据流动情况如下:

进栈记录栈中数据流动mvStack
1视图矩阵进栈 1
2 "太阳"模型—视图矩阵进栈,添加模型平移(自身位置) 1-2
3 “太阳”模型—视图矩阵进栈,添加模型旋转(自转) 1-2-3
pop “太阳”模型—视图矩阵出栈 1-2
注意:此时栈顶是"太阳"的模型—视图矩阵
4 “地球”模型—视图矩阵进栈,添加模型平移(相对于“太阳”的位置,公转) 1-2-4
5 “地球”模型—视图矩阵进栈,添加模型旋转、缩放(自转) 1-2-4-5
pop “地球”模型—视图矩阵出栈 1-2-4
注意:此时栈顶是"地球"的模型—视图矩阵
6 “月球”模型—视图矩阵进栈,添加模型平移(相对于“地球“的位置,公转) 1-2-4-6
7 “月球”模型—视图矩阵进栈,添加模型旋转、缩放(自转) 1-2-4-6-7
pop“月球”模型—视图矩阵出栈 1-2-4-6
注意:此时栈中分别是"月球"、“地球”和太阳的模型—视图矩阵以及基础视图矩阵
pop 依次出栈,直到栈空 empty

练习截图

“太阳系统模拟1”“太阳系统模拟2”

优化性能

随着场景复杂性的增加,我们必须关注性能,从而提高程序的运行速度。

  • 尽量减少动态内存空间的分配
  • 预先计算透视矩阵
  • 启用背面剔除功能
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值