\(S_\omega(\omega_i)\)是有缩放因子的菲涅尔项,它的公式:
\[
S_\omega(\omega_i) = \frac{1-F_r(\cos\theta_i)}{c\cdot \pi}
\]
其中\(c\)是一个嵌套的半球面积分:
\[
\begin{eqnarray}
c &=& \int_0^{2\pi} \int_0^{\frac{\pi}{2}} \frac{1-F_r(\eta,\cos\theta)}{\pi}\sin\theta \ \cos\theta \ d\theta \ d\phi \\
&=& 1 - 2 \int_0^{\frac{\pi}{2}} F_r(\eta,\cos\theta)\sin\theta \ \cos\theta \ d\theta \ d\phi
\end{eqnarray}
\]
BSSRDF公式更具体的理论、推导、简化过程可参看下面两篇论文:
A Practical Model for Subsurface Light Transport
BSSRDF Explorer: A Rendering Framework for the BSSRDF
2.2.2 次表面散射的空间模糊
次表面散射本质上是采样周边像素进行加权计算,类似特殊的高斯模糊。也就是说,次表面散射的计算可以分为两个部分:
(1)先对每个像素进行一般的漫反射计算。
(2)再根据某种特殊的函数\(R(r)\)和(1)中的漫反射结果,加权计算周围若干个像素对当前像素的次表面散射贡献。
上述(2)中提到的\(R(r)\)就是次表面散射的扩散剖面(Diffusion Profile)。它是一个次表面散射的光线密度分布,是各向同性的函数,也就是说一个像素受周边像素的光照影响的比例只和两个像素间的距离有关。
实际上所有材质都存在次表面散射现象,区别只在于其密度分布函数\(R(r)\)的集中程度,如果该函数的绝大部分能量都集中在入射点附近(r=0),就表示附近像素对当前像素的光照贡献不明显,可以忽略,则在渲染时我们就用漫反射代替,如果该函数分布比较均匀,附近像素对当前像素的光照贡献明显,则需要单独计算次表面散射。
利用扩散剖面技术模拟的次表面散射,为了得到更柔和的皮肤质感,需要对画面进行若干次不同参数的高斯模糊。从模糊空间划分,有两种方法:
纹理空间模糊(Texture Space Blur)。利用皮肤中散射的局部特性,通过使用纹理坐标作为渲染坐标展开3D网格,在2D纹理中有效地对其进行模拟。
屏幕空间模糊(Screen Space Blur)。跟纹理空间不同的是,它在屏幕空间进行模糊,也被称为屏幕空间次表面散射(Screen Space SubSurface Scattering,SSSSS)。
纹理空间和屏幕空间进行0, 3, 5次高斯模糊的结果
上图:屏幕空间的次表面散射渲染过程
2.2.3 可分离的次表面散射(Separable Subsurface Scattering)
次表面散射的模糊存在卷积分离(Separable Convolution)的优化方法,具体是将横向坐标U和纵向坐标V分开卷积,再做合成:
由此产生了可分离的次表面散射(Separable Subsurface Scattering,也叫SSSS或4S),这也是UE目前采用的人类皮肤渲染方法。它将\(R_d\)做了简化:
\[
R_d(x,y) \approx A_g(x,y) = \sum_{i=1}^N \omega_i G(x,y,\sigma_i)
\]
具体的推导过程请参看:Separable Subsurface Scattering。
该论文还提到,为了给实时渲染加速,还需要预积分分离的卷积核(Pre-integrated Separable Kernel):
\[
A_p(x,y) = \frac{1}{\parallel R_d \parallel_1} a_p(x)a_p(y)
\]
利用奇异值分解(Singular Value Decomposition,SVD)的方法将其分解为一个行向量和一个列向量,并且保证了分解后的表示方法基本没有能量损失。下图展示了它的计算过程:
本节将从UE的C++和shader源码分析皮肤渲染的实现。UE源码下载的具体步骤请看官方文档:下载虚幻引擎源代码。
再次给拥有充分共享精神的Epic Game点个赞!UE的开源使我们可以一窥引擎内部的实现,不再是黑盒操作,也使我们有机会学习图形渲染的知识,对个人、项目和公司都大有裨益。