出于同样的原因,我将我的 2D 和 3D 渲染分开用于类似 CAD 的应用程序,尽管在我的情况下,我的 2D 东西不是小部件 - 但它不应该有所作为。这是解决问题的方法:
- 当您的小部件更改将其渲染到 a
QGLFramebufferObject
时,请通过将 FBO 用作您和调用中的QPaintDevice
for a来执行此操作。对您拥有的许多小部件重复此操作,但仅在同一个 FBO 上 - 不要为每个小部件创建一个 FBO……请记住先清除它,就像“正常”帧缓冲区一样。QPainter
QGLWidget::paintEvent(..)
myWidget->render( myQPainter, ...)
QGLFramebufferObject
当您当前的 OpenGL 背景发生变化时,使用标准 OpenGL 调用以相同的方式将其渲染到另一个上。
- 创建一个通过顶点着色器(“相机”将只是一个单位立方体)和一个非常简单的片段着色器,可以将两个纹理叠加在一起。
- 在结束时
QGLWidget::paintEvent(..)
,激活您的着色器程序,将帧缓冲区绑定为它的纹理(myFBO->texture()
获取句柄),并渲染一个单元四边形。因为您的相机是一个单位正方形,并且视口大小定义了 FBO 大小,所以它将完美地填充视口像素。
然而,这是容易的部分......困难的部分是小部件交互。因为您本质上是在渲染一个“代理”,所以您必须中继“真实”和“代理”小部件之间的交互,同时保持“真实”小部件不可见。这是我将如何开始:
- 一些操作系统在渲染小部件而不显示它们时有点奇怪,因此您可能必须在实例化后显示然后隐藏小部件 - 由于 Qt 中聪明的绘制队列,它不太可能真正出现在屏幕上。
- 捕获视口中的所有鼠标事件,计算出光标在哪个“代理”小部件上(如果有),然后将其偏移以获得“真实”隐藏小部件的相对位置 - 该值将取决于哪个父对象'真正的'小部件有,如果有的话。然后在重绘小部件帧缓冲区之前将事件传递给“真实”小部件。
我应该声明我还必须创建一个“标记”系统来很好地处理重绘。您不希望每个小部件事件都触发小部件 FBO 重绘,因为可能有许多同时发生的事件(不要只考虑鼠标) - 但您只想要一个重绘。所以我创建了一个系统,如果应用程序中的任何东西都可以在视觉上改变视口中的任何东西,那么它会将视口标记为“脏”。然后设置一个QTimer
无论您瞄准多少fps(在我的情况下,场景可能会变得非常沉重,所以我还计时了一帧花费了多长时间,然后将该值+10%用作下一次检查的计时器延迟,这样系统就渲染滞后时不会被轰炸)。然后检查脏状态:如果脏,重绘;否则不要。我发现使用两个脏标志让生活变得更轻松,一个用于 3D 东西,一个用于 2D - 但是如果您需要为 OpenGL 绘图保持恒定的绘制速率,则可能不需要两个。
我想我所做的并不是最简单的方法,但它为调优和分析提供了充足的空间——从长远来看,这让生活更轻松。所有的答案肯定不在这篇文章中,但希望它能让你走上策略的道路。