Mobile里实现Linear Rendering

Linear Rendering的意义就不用多说了,Unity的文档中说得非常清楚。

首先看看人眼对亮度感知的函数曲线。这里取最常见的参数2.2

y=power(x, 2.2)

 

横轴是亮度,纵轴是人眼感知到的亮度。

美术用所见即所得的方式调出来的贴图,实际像素都是被曲线所影响过的,

光照如果也在Gamma空间内计算,相当于也同样受这个曲线影响,结果就是亮部更亮。(LDR管线这个问题更加突出,因为超过1的像素都被一刀切成1了)

我们看看Unity提供的参考效果对比图:

 

 

实际在项目中,如果用Gamma空间,美术调亮一点贴图,亮部就会大面积过爆,而调暗一点,暗部就会太黑。非常难以调整。

有时候美术就会调整光照环境,或者要求增加一个光源来给暗部补光。这会带来额外的性能开销,尤其Unity的Forward Rendering会增加一个ForwardAdd pass。

最关键的问题是,用一个错误弥补另一个错误是不对的,只会越来越乱。

 

硬件实现的LinearRendering的方法是:

1.标记处贴图是否在Gamma空间(sRGB)。

2.shader内采样sRGB时,硬件做一个pow(2.2)操作

3.在线性空间内进行光照计算

4.全部shading完成后,在LDR做pow(1/2.2),转成Gamma空间显示。

 

根据上面的流程,可以自己在shader中实现

1. 采完颜色贴图后,做pow(2.2)

2. 所有光源同样做pow(2,2)处理

3. 最终输出光照结果时,做pow(1/2.2)(对于LDR)

 

其中1可以利用Unity的AssetPostProcessor来做。变成在导入颜色贴图时(可以用命名规范来识别),逐像素做pow(2.2)。shader中在linear space做光照计算,最后补个gamma correction的后处理。

 

测试后发现,上述方法只适用于较亮的贴图。暗的贴图会出现严重的banding artifact。这是因为pow(2.2)之后暗部会被压缩,rgb8的精度有限,细微的变化就损失掉了。引擎中打光会提亮暗部,暴露缺陷。

 

所以不能直接修改贴图的像素,只能回归shader方案,进行优化。pow(2.2)可以用简单的平方代替,这样

1. shader采样sRGB后,做平方计算

2.光源亮度,色块等可以提前做好平方,传入gpu

3.补上pow(0.5)的gamma校正

效果与unity自带的linear rendering几乎一样,并且不用担心兼容性,gles2.0上也可以运行。

posted @ 2016-12-21 14:45  潜水的牛  阅读(614)  评论(0编辑  收藏  举报