3

这是我的问题,当我加载纹理时,我将它们作为 SRGB 加载以将它们转换为线性空间。当我写入窗口系统提供的默认帧缓冲区时,我启用了 GL_FRAMEBUFFER_SRGB 以便写入帧缓冲区会将颜色从线性空间转换为 SRGB 空间。

现在的问题是我正在渲染到屏幕外 FBO,然后将其放入默认帧缓冲区。这样做时,打开或关闭 GL_FRAMEBUFFER_SRGB 无效。我尝试使用默认帧缓冲区绑定和屏幕外 FBO 绑定启用 GL_FRAMEBUFFER_SRGB,但它们不起作用。

但是,如果我将 GL_SRGB 指定为我绑定为屏幕外 FBO 的颜色缓冲区的纹理的内部格式,那么会起作用。我假设在这种情况下,当启用 GL_FRAMEBUFFER_SRGB 时,从片段着色器到纹理的写入将其从线性空间转换为 SRGB 空间。

我想知道,似乎从 FBO 到默认帧缓冲区的 GL_FRAMEBUFFER_SRGB 转换没有得到应用。由于我想在线性空间中工作,直到最终传输到默认帧缓冲区的后缓冲区,当我将屏幕外 FBO 传送到默认帧缓冲区时,如何应用转换?我猜想将屏幕外 FBO 作为纹理进行采样并在默认帧缓冲区上渲染一个四边形会应用 SRGB 转换,但是没有办法使用帧缓冲区进行这种转换吗?

4

1 回答 1

7

这样做时打开GL_FRAMEBUFFER_SRGB或关闭没有效果。我尝试GL_FRAMEBUFFER_SRGB使用默认的帧缓冲区绑定和屏幕外 FBO 绑定都启用它们,但它们不起作用。

GL_FRAMEBUFFER_SRGB不像您认为的那样:它不会任何帧缓冲区格式从 RGB 转换为 sRGB。 GL_FRAMEBUFFER_SRGB只有当已经指定了帧缓冲区的格式时才会有任何效果sRGB,例如通过将带有internalFormatofGL_SRGB8GL_SRGB8_ALPHA8作为颜色缓冲区的纹理附加到 FBO。如果您想为默认帧缓冲区进行 sRGB 转换,则必须使用窗口系统特定的 API 来显式地创建一个 PixelFormat/Visual/FBConfig/whatever 支持 sRGB。查看有关如何执行此操作的GL / GLX / WGL _ARB_FRAMEBUFFER_SRGB扩展规范。

但是,如果我GL_SRGB将绑定为屏幕外 FBO 的颜色缓冲区的纹理的内部格式指定为有效的方法。我假设在这种情况下,当GL_FRAMEBUFFER_SRGB启用从片段着色器到纹理的写入时,会将其从线性空间转换为 SRGB 空间。

是的,这正是它应该如何工作的。GL_FRAMEBUFFER_SRGB启用位只是禁用通常会执行 SRGB 转换的格式的一种方法,而不是相反。

我想知道,似乎从 FBO 到默认帧缓冲区的 GL_FRAMEBUFFER_SRGB 转换没有得到应用。

让我们看看迄今为止最新的 OpenGL 规范:OpenGL 4.6 core profile specification,第 18.3.1 节“Blitting Pixel Rectangles”(强调我的):

当从读取缓冲区中获取值时,如果 FRAMEBUFFER_SRGB启用并且 FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING对应于读取缓冲区的帧缓冲区附件的值为 SRGB(参见第 9.2.3 节),则红色、绿色和蓝色分量从非线性 sRGB 转换而来根据公式 8.17 的颜色空间。

当值被写入绘图缓冲区时,blit 操作会绕过大部分片段管道。唯一影响 blit 的片段操作是像素所有权测试、剪刀测试和 sRGB 转换(参见第 17.3.7 节)。颜色、深度和模板蒙版(参见第 17.4.2 节)被忽略

第 17.3.7 节“sRGB 转换”说明:

如果FRAMEBUFFER_SRGB启用并且 FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING目标缓冲区对应的帧缓冲区附件的值为 SRGB(参见第 9.2.3 节),则混合后的 R、G 和 B 值通过 [公式如下转换为非线性 sRGB 颜色空间。 ..]

GL_FRAMEBUFFER_SRGB请注意,第 17.3.7 节将明确在写入颜色缓冲区时,仅在启用时才会应用线性到 sRGB 的转换。

所以这给我们留下了以下可能性:

  1. 源颜色缓冲区和目标颜色缓冲区都有标准的 NON-sRGB 格式:blit 将复制像素值,无论如何GL_FRAMEBUFFER_SRGB设置,都不会发生 sRGB 转换。

  2. 源缓冲区具有 sRGB 格式,目标缓冲区具有 NON-sRGB 格式。当且仅当 GL_FRAMEBUFFER_SRGB启用时,才会完成从 sRGB 到线性的转换。

  3. 源缓冲区具有 NON-sRGB 格式,目标缓冲区具有 sRGB 格式。当且仅当 GL_FRAMEBUFFER_SRGB启用时,才会完成从线性到 sRGB 的转换。

  4. 源和目标都具有 sRGB 格式。当且仅当 GL_FRAMEBUFFER_SRGB启用时,才会完成从 sRGB 到线性和线性到 sRGB 的转换。请注意,转换在这里可能归结为无操作。这也包括双线性过滤的情况:GL 规范不要求在 sRGB 源上的双线性过滤在转换后应用于线性空间,它也可以在之前应用。

现在,故事可能到此为止了。但事实并非如此。具有 sRGB 格式的帧缓冲区 blit 的行为在 GL 历史中经历了许多变化。

第 18.3.1 节OpenGL 4.3 核心配置文件规范确实指出:

当从读缓冲区中取值时,如果 FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING对应于读缓冲区的帧缓冲区附件的值为 SRGB(见 9.2.3 节),则红、绿、蓝分量根据非线性 sRGB 颜色空间转换而来方程

[第二段与 4.6 中的前一段相同]

这意味着直到 GL 4.3,缓冲区的转换总是在它具有 sRGB 格式时完成,无论GL_FRAMEBUFFER_SRGB启用的设置如何。但是,对于目标缓冲区,此设置仍然是相关的。

现在在OpenGL 3.3中,没有提到使用 sRGB 格式进行 blitting 的行为。相关部分是 4.3.2 “复制像素”,仅说明:

Blit 操作绕过片段管道。唯一影响 blit 的片段操作是像素所有权测试和剪刀。

这意味着它还将绕过目标缓冲区的线性到 sRGB 转换,并且根本不声明源转换。

也不是说驱动程序在历史上也忽略了规范,并在 sRGB 转换方面做了他们认为最好的事情。例如,请参阅文章“2015 年 3 月 OpenGL 驱动程序状态和 FB sRGB 转换”小猪 OpenGL 测试套件也有一个不错的补丁,它讨论了一些问题并提出了一个有点悲伤的结论:

我认为简短的总结是: OpenGL 中的 sRGB 几乎已经损坏了。:( 至少,我曾经与之交谈过的每个游戏开发者都这样告诉我。呃。

但是,根据我的经验,大多数当前的 GL >= 4.4 驱动程序都会按照指定的方式进行转换,因此情况不再那么糟糕了。但我也不会赌上我的生命。

于 2017-09-08T13:34:40.327 回答