我一直以为我的伽马校正管道应该如下:
- 对于所有加载的纹理都使用sRGB格式(
GL_SRGB8_ALPHA8
),因为所有绘画程序在文件预伽马校正之前。当在着色器中从GL_SRGB8_ALPHA8
纹理中采样时,OpenGL会自动转换为线性空间。 - 所有光照计算、后期处理等都在线性空间中进行。
- 将最终颜色转换回sRGB空间,以便在屏幕上显示。
请注意,在我的情况下,最终颜色写入涉及从FBO(线性RGB纹理)写入到后备缓冲区。
我的假设受到了挑战,因为如果我在最后一步进行伽马校正,则我的颜色比它们应该亮。我设置一个固定的颜色{ 255, 106, 0 },但是当我渲染时,我得到的是{ 255, 171, 0 }(通过打印屏幕并选取颜色确定)。我得到了黄色而不是橙色。如果我在最后一步不进行伽马校正,我得到的值恰好是{ 255, 106, 0 }。
根据某些资源,现代液晶屏幕模拟CRT伽马。它们总是这样吗?如果不是,我如何确定是否应该进行伽马校正?我在其他地方做错了吗?
编辑1
我现在注意到,即使使用颜色正确的灯光写入的颜色,使用来自纹理的颜色的地方也不正确(比我预期的要暗得多,没有伽马校正)。我不知道这种差异来自哪里。
编辑2
在将我的纹理从GL_SRGB8_ALPHA8
更改为GL_RGBA8
之后,一切看起来都很完美,即使在使用纹理值进行光照计算时也是如此(如果我减半光线的强度,则输出的颜色值也会减半)。
我的代码不再考虑伽马校正,我的输出看起来正确。
这让我更加困惑,伽马校正是否不再需要/使用?
编辑3-回答datenwolf的答案
经过更多的实验,我对以下几点感到困惑。
1-大多数图像格式都以非线性方式(在sRGB空间中)存储
我加载了一些图像(在我的情况下是.png和.bmp图像),并检查了原始二进制数据。在我的程序中,如果我使用图像编辑程序的值与字节数组进行比较,则似乎这些图像实际上位于RGB颜色空间中,并且它们完全匹配。由于我的图像编辑器正在提供RGB值,这表明存储在RGB颜色空间中。我使用stb_image.h/.c加载我的图像,并一直跟踪加载.png文件的过程,但没有看到任何地方在加载时进行伽马校正。我还在十六进制编辑器中检查了.bmp文件,发现磁盘上的值也完全匹配。
如果这些图像实际上是在线性RGB空间中存储在磁盘上,那么我应该如何在编程时知道何时指定图像为sRGB空间?是否有某种方式可以查询此信息,更高级的图像加载器可能会提供?还是由图像创建者保存其图像为伽马校正(或非伽马校正) - 这意味着建立一个约定并遵循特定项目。我问了几位艺术家,他们都不知道什么是伽马校正。
如果我指定我的图像是sRGB,则它们太暗,除非我最后进行伽马校正(如果监视器使用sRGB,则这是可以理解的,但请参见第2点)。
“在大多数计算机上,有效的扫描LUT是线性的!这是什么意思?” 我不确定能否找到您的回答中该想法的结束位置。
从我做实验得出的结果来看,我测试过的所有显示器输出线性值。如果我用着色器的硬编码值绘制全屏四边形并给其着色,而且没有伽马校正,监视器将显示我指定的正确值。
我引用了您在回答中提到的那句话和我的结果,这让我相信现代显示器输出线性值(即不模拟CRT伽马)。
我们应用程序的目标平台是PC。对于此平台(不包括使用CRT或非常老的显示器的人),按照1的响应方式进行操作,然后对于2,不要进行伽马校正(即不进行最终的RGB->sRGB转换 - 手动或使用GL_FRAMEBUFFER_SRGB)是否是合理的?
如果是这样,那么GL_FRAMEBUFFER_SRGB适用于哪些平台(或在今天使用它将是有效的),还是说采用线性RGB的显示器真的很新(考虑到GL_FRAMEBUFFER_SRGB是2008年推出的)?
我在学校与其他几个图形开发人员交谈后,听起来他们都没有考虑到伽马校正,并且他们没有注意到任何不正确的地方(有些人甚至没有意识到它)。特别是一个开发人员说,当考虑伽马时,他得到了错误的结果,因此他决定不担心伽马。给出了矛盾的网络信息/我的项目看到的情况,我不确定在我的目标平台上该做什么。
编辑4 - 回应 datenwolf的更新答案
没错,如果信号链的某个位置应用了非线性转换,但从图像到显示器的所有像素值都未被修改,那么该非线性已经在图像像素值上预先应用。这意味着图像已经处于非线性颜色空间中。
如果我正在我的显示器上查看图像,则您的回复对我有意义。为了确保我清楚,当我说我正在查看图像的字节数组时,我是指查看纹理在内存中的数值,而不是屏幕上的图像输出(对于第二点,我确实这样做了)。在我看来,唯一的可能是如果图像编辑器使用sRGB空间为我提供值。
还要注意,我确实尝试在监视器上检查输出,以及修改纹理颜色(例如,将其除以一半或加倍),并且输出似乎是正确的(使用我下面描述的方法进行测量)。
你如何测量信号响应?
不幸的是,我的测量方法远不如你的精密。当我说我在我的监视器上进行了实验时,我意味着输出一个固定颜色的全屏四边形,其颜色在着色器中硬编码为纯OpenGL帧缓冲区(写入时不执行任何颜色空间转换)。当我输出白色、75%灰色、50%灰色、25%灰色和黑色时,正确的颜色被显示。现在我的正确颜色的解释可能是错误的。我截屏,然后使用图像编辑程序查看像素值(以及视觉评估以确保值合理)。如果我正确理解,如果我的监视器是非线性的,我需要在呈现给显示设备之前执行RGB->sRGB转换才能使它们正确。
我不会撒谎,我感觉我有点深入了。我正在考虑为我的第二个困惑点(最终的RGB->sRGB转换)寻找的解决方案将是一个可调节的亮度设置,并将其默认设置为在我的设备上看起来正确的值(没有伽马校正)。
gamma = 1/2.2
进行 gamma 矫正,然后为了制作灰度图像,我对所有颜色通道应用反向矫正,使用gamma=2.2
,然后使用线性变换 'gray = 0.2126 * r + 0.7152 * g + 0.0722 * b',最后将灰度值使用gamma = 1/2.2
进行转换,再保存为 bmp。令人惊讶的是,没有进行 gamma 矫正时,我得到了更好的灰度图像... - John Smith