如何在函数调用之间跟踪OpenGL状态?

8
由于OpenGL是一个状态机,我在程序中不断地使用glEnable()和glDisable()。有一些调用只在开始时进行(例如glClearColor),但大多数情况下我会根据需要开启或关闭它们(例如光照,取决于我是渲染模型、3D文本还是GUI)。
您如何跟踪这些状态呢?每次函数调用前都重置它们吗?那不是很多不必要的开销吗?
例如,在编写新函数时,有时我知道在调用函数时状态的位置,我就会在函数顶部省略glEnable或glDisable等相关状态切换调用。其他时候,我事先编写函数并添加这些内容。因此,我的函数变得非常混乱,有些修改OpenGL状态,而其他一些则只做出假设(后来被打破,然后我必须回去弄清楚为什么某个东西变成了黄色,或者为什么另一个东西颠倒了等等)。
在面向对象的环境中,您如何跟踪OpenGL跨函数的使用呢?
此问题也与如何知道何时使用push和pop以及何时只需设置值有关。
例如,假设您有一个程序,绘制一些3D内容,然后绘制一些2D内容。显然,在每种情况下投影矩阵都不同。那么您会:
1.设置一个3D投影矩阵,绘制3D;设置一个2D投影矩阵,绘制2D,循环
2.在程序中设置一个3D投影矩阵;然后绘制3D,push矩阵,绘制2D,pop矩阵,循环
您会选择哪个方法?为什么?
3个回答

2

我认为最简单的方法是编写自己的渲染类,包装所有正在使用的OpenGL状态操作函数,并进行状态跟踪。需要考虑更改屏幕分辨率或切换全屏模式会使当前的OpenGL渲染上下文状态和数据无效,这意味着在此类事件后,您将不得不设置所有状态,重新上传所有纹理和着色器程序等。


2

很好的问题。想一想纹理是如何工作的。OpenGL有着数量惊人的纹理需要切换,每绘制完一个对象后你需要启用/禁用纹理。通常情况下,你会尝试通过一次性地绘制所有使用相同纹理的对象来优化,但是这仍然需要进行大量状态切换。因此,我尽量在同一状态下绘制所有东西,但我不确定以你所思考的方式进行优化是否非常重要。

至于在3D和2D投影模式之间推送和弹出,推送和弹出旨在用于分层建模。如果你需要让你的2D投影相对于3D对象处于某个位置,请推动矩阵并切换到2D投影模式,完成后再弹回来。然而,如果你正在编写一个具有恒定位置的2D GUI叠加层,我不认为推送和弹出很重要。


我不确定你的第一段是否真正解决了问题,虽然我相信这主要是因为我没有表达清楚。当我确切地知道自己在问什么时,我需要回到这个问题上来。不过,谢谢你对push/pop的见解,我很喜欢那个解释。 - Ricket

2
您可能对阅读更多关于场景图库的信息感兴趣。它们旨在从更高级别管理您的图形。它们并不能很好地处理所有事情,但是它们擅长组织几何体以实现优化渲染。它们还可以用于基于OpenGL状态对场景进行排序,尽管许多库不会这样做。通过按状态排序,您可以按照减少状态转换的顺序渲染几何体。以下是有关场景图库的良好概述: http://www.realityprime.com/articles/scenegraphs-past-present-and-future

是的,我对它们非常熟悉。我正在使用Java,所以有Xith3D和jMonkeyEngine。问题是我过于沉迷于这些引擎,却没有创造出任何有价值的东西。现在我只写纯OpenGL,实际上做了一些有意义的事情。还是谢谢你。 - Ricket
似乎你有些在重新发明轮子。你所完成的工作有多少是由场景图库管理的? - Eric
1
.ms3d文件加载器,包围盒计算。大概就是这样了。使用场景图库,至少很难实现我特殊的高度图(计划中的动态加载)和自定义的GUI功能。所以,是一个权衡选择,对我来说更容易的做法是先编写场景图库能处理的事情,然后再做它不能处理的其他事情,而不是使用一个场景图库并尝试找到解决方法和技巧,只为了在屏幕上绘制2D形状之类的东西。 - Ricket

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接