可以参考:
1.http://blog.csdn.net/ngrandmarch/article/details/46407017
2.http://blog.csdn.net/candycat1992/article/details/46228771
关键是文2,但文2许多东西都只是“作者知道,读者不知道,但作者以为读者知道”,文1可以辅助理解。
首先文1的内容应该是正确的,我再加几点帮助在文1的基础上理解文2提到的几个观点。
0.线性纹理输出屏幕上,因为colorbuffer到屏幕的影响(硬件),得到的颜色会是(0-1)^2.2.
1.sRGB格式意思是0-1映射到(0-1)^(1/2.2)=(0-1)^0.45,也就是说sRGB格式的图片是非线性纹理。
2.如果sRGB格式的纹理不经过色值缩放直接经过shader输出到colorbuffer,因为colorbuffer到屏幕上需要进行col^(2.2),所以屏幕上刚好是原来的颜色。
3.但是,如果sRGB格式的纹理的色值在shader里进行了缩放,比如文2中的颜色混合,那输出到屏幕的结果是:((0-1)^0.45 * scale)^2.2,这个显然是错误的,毕竟我们想要的是(0-1)*scale。
4.采用sRGB格式的本质原因是颜色的精度太低,而人的眼睛对暗部分变化比较敏感,所以需要让更多的精度来表示暗的部分,所以搞了个0.45次方映射:(0-1)=>((0-1)^0.45),后者的范围依然是(0-1)只是更多位数来表示暗的部分了:0.18^0.45=0.5=>有一半精度表示0-0.18之间的亮度,一半表示0.18-1之间的亮度。
下面看unity的gama space 和 linear space:
5.但是,unity的linear space帮我们做了一些处理:对输入纹理先进行^2.2变换到线性空间,然后进行shader处理,当输出到colorbuffer时再^0.45回归sRGB格式即gama空间,这样shader里的缩放等处理都是对原色值进行线性处理了。但是呢,这里默认了输入纹理是sRGB,如果你的输入是线性的,那就直接出错了!,可以通过勾选纹理设置的bypass srgb sample来越过^2.2和^0.45的空间转换处理,现在Unity默认都把图片搞成sRGB格式,所以linear space是正确的显示。
6.unity的gamma space没有做任何事情,即没有做输入转换到线性空间和输出转回gamma空间,这个时候,对于线性纹理,输出会是((0-1)*scale)^2.2,对于sRGB格式:((0-1)^0.45 * scale)^2.2,都特么是错的。前者很容易矫正,但sRGB格式可以更好的表现暗域,这估计也是unity默认转成sRGB格式的原因。
7.unity默认对纹理都搞成sRGB格式(通过勾选纹理设置的bypass srgb sample课可以搞成线性的),并且在PC上可以选择linear space,手机上只能选择gama space,这意味着我们的手游需要gama矫正:
在采样时先转线性空间:
float3 diffuseCol = pow(tex2D( diffTex, texCoord ), 2.2 );
然后在输出时再转gama 空间:
fragColor.rgb = pow(fragColor.rgb, 1.0/2.2);return fragColor;,
但是但是,因为片段着色器的结果并不是直接写得colorbuffer里面的,所以我们做的线性空间转回gama空间做早了,这有可能会出问题!因为gama 矫正的正确操作是(unity linear space做的):
一切工作都是为了“保证所有的输入都转换到线性空间,并在线性空间下做各种光照计算,最后的输出(最最最最后的输出)进行伽马校正后再显示”。这个最后的输出是colorbuffer。
但事实上,即使我们不进行gama矫正也没什么,因为我们的shader又不是万能的,调整各种参数满足表现就好了嘛,又不是动态变化的。不过如果你的shader的亮度看起来不如意,有可能是没进行gama矫正导致的,知道就好。
8.存疑:
1.所谓sRGB格式是存储的格式还是读到GPU里的格式?按说unity不会直接改导入图片的信息,那应该是GPU读原始纹理后进行处理的格式了,但没看到文档不敢揣测。
答:unity不会改导入图片的信息,但图片被导入后会根据设置在Library目录生成一个对应的文件,给游戏使用,所以sRGB就是存储的格式。
2.许多游戏场景因为没有gama矫正而变暗,为什么?
答:其实这点我没查到原理,仅根据公式推:因为当scale在0-1之间时,scale^2.2比scale小,所以偏暗。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章