4

我想对我的 OpenGL 照明实施伽马校正,但应用伽马校正后,我的结果似乎根本不是线性的。

我还发现OpenGL: Gamma Corrected image doesn't appear linear这与我的问题非常相似,但还没有收到答案,也没有讨论过实际的漫射光。

作为说明,我在线性空间中定义了以下 4 种浅色:

glm::vec3 lightColors[] = {
    glm::vec3(0.25),
    glm::vec3(0.50),
    glm::vec3(0.75),
    glm::vec3(1.00)
};

将每个光源分开并将基本线性衰减应用于漫反射照明方程,我得到以下结果:

错误的伽玛

这是片段着色器:

void main()
{           
    vec3 lighting = vec3(0.0);
    for(int i = 0; i < 4; ++i)
        lighting += Diffuse(normalize(fs_in.Normal), fs_in.FragPos, lightPositions[i], lightColors[i]);
    // lighting = pow(lighting, vec3(1.0/2.2));
    FragColor = vec4(lighting, 1.0f);
}

我不仅几乎看不到伽马校正灯的亮度差异,而且衰减也因伽马校正而失真。据我了解,所有计算(包括衰减)都是在线性空间中完成的,并且通过校正伽马,监视器应该正确显示它(因为它再次将其作为输出不校正)。仅基于照明颜色,最右边的圆圈应该是左边圆圈的 4 倍,是第二个圆圈的 2 倍,但似乎并非如此。

只是我不够敏感,无法感知正确的亮度差异还是有什么问题?

我尝试的其他方法是简单地将精确的浅色输出到默认帧缓冲区,无需和使用伽马校正。

伽马输出

左边是未校正的,右边是带有伽马校正的;红色数字表示来自 Photoshop 颜色选择器的 RGB 强度。我知道 Photoshop RGB 值并不代表最终输出图像(因为 Photoshop 不会将 RGB 值读取为显示器输出)。左边的图像直观地看起来更好,但基于 RGB 强度值,我想说最右边的图像确实被我的片段着色器正确地进行了伽马校正;因为这些强度中的每一个都会通过显示器并以正确的强度进入我的眼睛。例如,当 0.88 伽马校正后的 0.75 强度变为 0.88^2.2 = 0.75 作为监视器的输出。

正确的图像真的正确吗?而且,与其他图像相比,实际照明为何如此偏暗?

4

1 回答 1

7

你得到的结果是预期的。

您似乎将显示器产生的物理辐射辐射与人类感知的亮度混淆了。后者是非线性的,简单的伽马模型是一种近似的方法。基本上,显示器正在反转人眼的非线性变换,因此标准(非线性)RGB 空间被线性感知 - 使用 RGB 强度 0.5 被感知为亮度大约为 1.0 的一半,依此类推。

如果您在显示 gamma 校正灰度级别时将色度计或分光光度计放在您的显示器上,您实际上会看到 0.73 步长将显示大约 50% 的白色级别亮度(以坎德拉/m^2 为单位)(假设您显示与 sRGB 模型并没有太大的偏差,顺便说一句。不使用 gamma 2.2,而是与 gamma 2.4 结合的线性段,2.2 只是另一个近似值)。

现在的问题是:你到底想达到什么目标?如果您想要进行物理上准确的计算,通常需要在线性色彩空间中工作。但是这样一来,一个亮度为另一个亮度50%的光源对人来说并没有一半的亮度,你得到的结果基本上是正确的。

如果您只想拥有一个感知亮度呈线性的色彩空间,您可以完全跳过伽马校正,因为 sRGB 已经在尝试提供该校正。如果您想要准确的结果,您可能只需要一些颜色校准或小的伽马调整来纠正显示器引入的偏差。

于 2015-04-22T19:12:42.817 回答