校正的目的
保证所有的输入都转换到线性空间,并在线性空间下做各种光照计算(线性空间进行操作),最后输出通过gamma校正后进行显示
输入转至线性空间
对非线性输入纹理进行线性校正,让其处于线性空间:
方法1
在shader中:
float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
方法2
加载纹理贴图时:
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
如果还打算在纹理中引入alpha元素,必须将纹理的内部格式指定为GL_SRGB_ALPHA。
因为不是所有纹理都是在sRGB空间中的,所以当我们把纹理指定为sRGB纹理时要格外小心。比如diffuse纹理,这种为物体上色的纹理几乎都是在sRGB空间中的。而为了获取光照参数的纹理,像specular贴图和法线贴图几乎都在线性空间中,所以如果把它们也配置为sRGB纹理的话,光照就坏掉了。指定sRGB纹理时要当心。将diffuse纹理定义为sRGB纹理之后,我们将获得我们所期望的视觉输出,这次每个物体都会只进行一次gamma校正。
输出前进行校正
方法1
在shader中:
在每个相关像素着色器运行的最后应用gamma校正,所以在发送到帧缓冲前,颜色就被校正了。
void main()
{
[...]
float gamma = 2.2;
fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
}
方法2
在帧缓冲中,开启GL_FRAMEBUFFER_SRGB以后,每次像素着色器运行后续帧缓冲,OpenGL将自动执行gamma校正,包括默认帧缓冲
glEnable(GL_FRAMEBUFFER_SRGB);
gamma校正将把线性颜色空间转变为非线性空间,所以在最后一步进行gamma校正是极其重要的。如果你在最后输出之前就进行gamma校正,所有的后续操作都是在操作不正确的颜色值。
衰减
在使用了gamma校正之后,另一个不同之处是光照衰减(Attenuation)。真实的物理世界中,光照的衰减和光源的距离的平方成反比。
float attenuation = 1.0 / (distance * distance);
然而使用这个衰减公式的时候,衰减效果总是过于强烈,光只能照亮一小圈,看起来并不真实。
出于这个原因,我们使用在基本光照教程中所讨论的那种衰减方程,它给了我们更大的控制权,此外我们还可以使用双曲线函数:
float attenuation = 1.0 / distance;
双曲线比使用二次函数变体在不用gamma校正的时候看起来更真实,不过但我们开启gamma校正以后线性衰减看起来太弱了,符合物理的二次函数突然出现了更好的效果。下图显示了其中的不同:
这种差异产生的原因是,光的衰减方程改变了亮度值,而且屏幕上显示出来的不是线性空间,所以在监视器上效果最好的衰减方程,并不是符合物理的。
想想平方衰减方程,如果我们使用这个方程,而且不进行gamma校正,显示在监视器上的衰减方程实际上将变成
(
1.0
/
distance
2
)
2.2
\left(1.0 / \text { distance }{ }^2\right)^{2.2}
(1.0/ distance 2)2.2
若不进行gamma校正,将产生更强烈的衰减。这也解释了为什么双曲线不用gamma校正时看起来更真实,因为它实际变成了
(
1.0
/
distance
)
2.2
=
1.0
/
(1.0 / \text { distance })^{2.2}=1.0 /
(1.0/ distance )2.2=1.0/ distance
2.2
{ }^{2.2}
2.2
。这和物理公式是很相似的。
因为线性空间更符合物理世界,大多数物理公式现在都可以获得较好效果,比如真实的光的衰减。我们的光照越真实,使用gamma校正获得漂亮的效果就越容易。这也正是为什么当引进gamma校正时,建议只去调整光照参数的原因。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)