注意:以下讨论假设您了解什么是 sRGB 颜色空间、什么是伽马校正、什么是线性 RGB 颜色空间等等。这主要侧重于该技术的 OpenGL 实现。
如果您想深入讨论这些主题,我建议您查看我的关于HDR/Gamma 校正的教程(以了解线性色彩空间和 gamma),以及关于sRGB 图像以及它们如何处理 gamma 校正的教程。
首先,任何人都可以从我的屏幕截图中确认这是您希望看到的吗?
我不确定我理解你这个问题的意思。如果您应用适当的伽马校正(这是 sRGB 或多或少所做的),您通常会在图像的较暗区域获得更多细节和“更亮”的结果。
但是,正确的思考方式是,在您进行适当的伽马校正之前,您的所有图像都是错误的。您的图像太暗,伽马校正现在使它们具有适当的亮度。你所做的关于事物应该是什么颜色以及灯光应该是多亮的每一个决定都是错误的。
我想问的第二个问题是硬件实际执行的校正是在什么时候?
这是一个与您继续使用封面的“示例”部分非常不同的问题。
sRGB 图像(请记住:纹理包含图像,但帧缓冲区也可以有图像)可以在以下情况下使用:
将数据从用户直接传输到图像(例如,使用 glTexSubImage2D 等)。OpenGL假定您提供的数据已经在 sRGB 颜色空间中。所以上传数据时没有翻译数据。这样做是因为它最有意义:通常,您从艺术家那里获得的任何图像都将位于 sRGB 色彩空间中,除非艺术家竭尽全力将其置于其他色彩空间中。几乎每个图像编辑器都直接在 sRGB 中工作。
通过采样器读取着色器中的值(即:访问纹理)。这也很简单。OpenGL 知道图像中的纹素数据在 sRGB 颜色空间中。OpenGL 假定着色器需要线性RGB 颜色数据。因此,所有从sRGB 图像格式的纹理采样的尝试都将导致 sRGB->lRGB 转换。顺便说一句,这是免费的。
从好的方面来说,如果您拥有支持 GL 3.x+ 的硬件,您几乎肯定会在线性色彩空间中完成过滤,这是有意义的。sRGB 是非线性色彩空间,因此 sRGB 值的线性插值总是错误的。
将从片段着色器输出的值存储到帧缓冲区图像。这是它变得稍微复杂的地方。即使您要渲染的帧缓冲区图像位于 sRGB 颜色空间中,也不足以强制转换。你必须明确glEnable(GL_FRAMEBUFFER_SRGB)
; 这告诉 OpenGL 您从片段着色器写入的值是线性色彩空间值。因此,OpenGL在将它们存储在图像中时需要将它们转换为sRGB
同样,如果您拥有 GL 3.x+ 硬件,您几乎肯定会在线性色彩空间中进行混合。也就是说,OpenGL 将从帧缓冲区读取 sRGB 值,将其转换为线性 RGB 值,将其与传入的线性 RGB 值(您从着色器写入的值)混合,将混合值转换为 sRGB 颜色空间并存储它. 再说一次,这就是你想要的;在 sRGB 颜色空间中混合总是很糟糕。
现在我们明白了,让我们看看你的例子。
例如,我将帧写入 FBO,然后我使用 FBO 颜色缓冲区将屏幕大小的四边形渲染到后台缓冲区(我打算很快切换到延迟着色)。
问题在于你没有问正确的问题。您需要记住的是这个问题,尤其是当您进入延迟渲染时:
这是线性RGB吗?
通常,您应该尽可能长时间地推迟将任何中间数据存储在伽马校正空间中。所以任何中间缓冲区(即:你积累灯光的地方)都不应该是sRGB。
这与转换成本无关;这真的是关于你在做什么。如果你在做延迟渲染,那么你可能也在做 HDR 光照等等。所以你的光积累缓冲区需要是浮点数。并且浮动缓冲区始终是线性的;他们没有理由不是线性的。
如果您想利用免费的伽马校正(并且您确实这样做了),您的最终图像(默认帧缓冲区)必须是 sRGB。如果您在 HDR 浮动缓冲区中完成所有工作,然后将结果色调映射到最终显示,您应该将其写入 sRGB 图像。