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