unity shader 线性空间与伽马校正

以下整理主要摘自《Unity Shader 入门精要》

效果差异

image
左图是线性空间渲染结果,右图是伽马空间。
可以看出伽马空间下的结果更真实。
默认情况下,Unity使用的是伽马空间,下面是Unity内置设置方法。

Unity下的设置为线性空间的方法:

Edit -> Project Setting -> Player ->
Other Settings -> Color space
image
Unity设置虽然方便,但是有缺点,需要一些硬件支持线性计算,然而一些移动平台其支持不大好。下面是原理介绍和如何在shader中实现伽马校正。

原理

image
视频讲解的例子(7min):https://www.youtube.com/watch?v=LKnqECcg6Gw

上面两个例子都能看出来,亮度上的线性变化对人眼感知来说是非均匀的。假设用8位空间表示亮度值,亮部和暗部各占128其实是浪费。摄影设备可能会有0.45的编码来对应亮部,也就是说,0.5对应的是0.22 因为0.22^0.45≈0.5。
当如此编码之后,显示的时候就应当解码。由于游戏界长期以来都忽视了伽马校正的问题,造成渲染出来的游戏总是暗沉沉的。由于解码伽马的存在,如果在非线性空间下计算,输出的图像就是非线性的。(Unity默认就是非线性)结果就如下图:

image
左边是伽马空间下渲染结果,右边是线性空间下渲染结果。

伽马存在还会对混合造成影响,色块叠加的模糊边缘在伽马空间下叠加颜色不对(视频里有讲为何)。
image
左边是伽马空间下渲染结果,右边是线性空间下渲染结果。

shader中进行伽马校正

好在知道了解码密码,就能在shader里进行计算、校正。
若有非线性纹理,那输入前校正代码如下。

1
float3 diffuseCol = pow(tex2D (diffTex, texCoord), 2.2);

输出前,对输出像素值的校正代码通常如下:

1
2
fragColor.rgb = pow(fragColor.rgb, 1.0/2.2);
retturn fragColor;

那什么时候可以不用管伽马呢?当通用格式不是8位,而是32位的时候,就有足够的颜色空间可以利用了,也就是后面要说的HDR。

HDR

HDR - High Dynamic Range 高动态范围。如果用远高于8位的精度来记录亮度信息,就可以更精确的反映光照。即使最后显示是LDR的设备,中间的计算却能让看到的东西更真实可信。
在Unity中使用HDR也很简单,在Camera组件面板打开HDR选项。
官方文档:https://docs.unity3d.com/Manual/HDR.html