每次绑定具有不同分辨率的帧缓冲区时是否都必须调用 glViewport?

Do you have to call glViewport every time you bind a frame buffer with a different resolution?

我有一个程序包含大约 3 个不同大小的帧缓冲区。我在一开始就对它们进行初始化,为它们提供适当的渲染目标并更改每个渲染目标的视口大小。

我原以为您只需要在初始化帧缓冲区时调用 glViewport,但这会在我的程序中产生问题,所以我认为这是错误的?因为它们的分辨率都不同,所以现在当我在每个帧中渲染时,我绑定第一个帧缓冲区,更改视口大小以适合该帧缓冲区,绑定第二个帧缓冲区,更改视口大小以适合第二个帧缓冲区的分辨率,绑定第三个帧缓冲区,更改视口大小以适应它,然后绑定 window 帧缓冲区并将视口大小更改为 window.

的分辨率

这是必要的,还是应该归咎于程序中的其他内容?这是每一帧都完成的,所以我担心如果我不必这样做会产生一些不必要的开销。

I originally thought that you only had to call glViewPort only when you initialise the framebuffer, however this creates problems in my program so I assume that's wrong?

是的,这是一个错误的假设(可能是因为无数错放了 glViewport 的错误教程)。

glViewport always属于绘图代码。在即将绘制某些内容到帧缓冲区之前,您始终会使用正确的参数调用 glViewport。 glViewport设置的参数是在transformation pipeline中使用的,所以你应该想到glViewport是一个类似于glTransform(在固定功能pipeline中)或者glUniform的命令。

在开始绘制到具有不同大小的帧缓冲区之前,您始终需要调用 glViewport()。这是必要的,因为视口不是帧缓冲区状态的一部分。

例如,如果您查看 OpenGL 3.3 规范,第 6.2 节,标题为 "State Tables",从第 278 页开始,包含 table 个完整状态,显示每个部分的范围状态:

  • Table 第 299 页的 6.23 列出了 "state per framebuffer object"。列出的唯一状态是绘制缓冲区和读取缓冲区。如果视口是帧缓冲区状态的一部分,它将在此处列出。
  • 视口在 table 6.8 "transformation state" 中列出。这是全局状态,与任何 object.
  • 无关

OpenGL 4.1 引入了多个视口。但他们仍然是全球转型状态的一部分。

如果您想知道为什么会这样,唯一真正的答案就是它是这样定义的。查看图形管道,它确实有意义。虽然 glViewport() 调用看起来像是在帧缓冲区内指定了一个要渲染到的矩形,但该调用实际上定义了一个转换,该转换作为顶点着色器之间的固定功能块的一部分应用(或几何着色器,如果有的话)和片段着色器。视口设置决定了 NDC(标准化设备坐标)如何映射到 Window 坐标。

另一方面,帧缓冲区状态决定了片段着色器输出如何写入帧缓冲区。所以它控制着管道的一个完全不同的部分。

从应用程序通常使用视口的方式来看,我认为将视口作为帧缓冲区状态的一部分会更有意义。但 OpenGL 实际上是一个 API 旨在作为图形硬件的抽象,从这个角度来看,视口独立于帧缓冲区状态。