UE4 HDR LightMap压缩优化
为什么要压缩HDR贴图?
由于很多游戏的LightMap要表达HDR信息,而HDR贴图的RGB范围超出了1,暗部和亮部所拥有的细节比是均衡的。如果直接使用色彩会有偏差。因为对于图片来说,暗部和亮部是拥有相同细节的,而人眼对亮度的感知不是线性的,相比于更亮的部分对于更暗的部分的颜色变化更为敏感。
e.g.
所以需要一种压缩策略,使得更暗的部分保留更多细节,更亮的部分保留较少细节。这样才符合人眼对颜色的识别曲线,呈现更真实的颜色信息。
UE4的方案
UE4的编码
UE4的方案是通过图片每个Color的RGB值,根据0.3/0.59/0.11的权值来计算一个该Pixel下的亮度L,通过该亮度L计算出亮度空间下的UVW值(分别对应RGB),之后通过映射公式: =log_2( )/16+0.5 将原RGB空间的Color映射到UVW亮度空间下。
通过画函数软件可以绘得该函数图像:
可以发现,x(原色彩)(0,1]区间内(暗部),而x[1,255]区间内(亮部),各占用了y(亮度空间色彩)(0,0.5]将近1/2的色域。
通过这个函数,可以将HDR信息全部按照暗部更多细节,亮部更少细节映射到(0,1]区间内。
原图:
编码后:
UE4的解码:
UE4的解码是需要在Shader里写的,这里为了方便调试,直接写在C#里了。
解码部分则构造了编码函数的反函数 =2^{16( −0.5)} ,将UVW亮度空间内的色值重新映射回RGB色彩空间。
通过画函数软件可以绘得该函数图像:
解码后:
细节对比:
解码后虽然有了一些噪点,但整体效果还是不错的。
由于UE4的方案是为PC端设计的,移动平台难以承受解码函数高额的开销。
下面开始优化。
根据UE4方案的分析,不难发现,所谓压缩其实就是需要构造一个函数,通过该函数将HDR信息的表达方式改为暗部更多细节,亮部更少细节的新型表达方式。解压缩则是构造压缩函数的反函数从而对压缩后的图像达到还原的过程。
所以需要设计一种新的编/解码函数满足:
1、编/解码函数计算复杂度尽可能低,需要设计简单的低阶函数。
2、保证正确的映射关系,形状和原函数拟合。
结合以上两点,可以尝试通过Cubic Spline Interpolation(CSI)构造函数实现。
这里略去我进行大量猜想、尝试和验证的过程,直接上我进行简化后的函数。
之前的解码函数用到了数学函数Pow,这个在Shader里是非常耗的。通过优化,我们用CSI函数 y=x(26x^2-5.2x+0.1) 拟合了UE4原生较费的 =2^{16( −0.5)} 解码函数。
在Shader端粗略估计,新的函数解码只需13个ALU
(经过大佬jiff的点拨,用mad的形式书写,可以优化为12个ALU,详情可看评论 )
可以发现两函数在较暗部分较为拟合,较亮部分有少许偏差,但这个差异我们是可以忽略的,因为在这个亮度下人眼几乎感觉不到变化。
CSI解码后:
细节对比:
可以发现效果甚至比原来的要好一些,而且计算方面节省了很多性能。