虚幻4渲染编程(人物篇)【第二卷:Human Hair Shading】

虚幻4渲染编程(人物篇)【第二卷:Human Hair Shading】

MY BLOG DIRECTORY:

INTRODUCTION:

最近在研究头发渲染相关的东西,在网上找到了一些论文和资料,发现都很杂。论文有个典型的特点就是看完以后就没有然后了,根本不知道该如何实现它,甚至数学推导都不知道它是怎么算出来的。因此我只能从理论开始推导开始研究,但是因为数学功力不足,所以我在理论阶段还是比较吃力。网上查找资料发现大部分都是对论文的简单搬运,截图了事,也没有详细的推导过程。不过还是有幸能发现零星几位大佬留下的研究笔记博客,才拯救了我。

头发的shading方案有多次进化,Kajiya Kay's头发光照模型是经验性的光照模型,它只描述了大体上的头发视觉效果,仍然有很多光照成分被忽略了。

kajiyaKay's的光照建模如上图所示,头发丝被认为是光滑的不透明圆柱体。这里计算高光不是N和H的cos值,而是T和H的sin值

S_{specular}= sin(T,H)^{specularly}=\sqrt{1-dot(T,H)^2}^{Specularly}

这里需要稍微注意一下的是这个切线,切线并不是NBT切空间的那个切线T,这里的切线就是头发丝的几何切线,它就是一个向量,和模型切空间的tangent不是相等关系。下面的链接里有我在材质编辑器里对Kajiya高光的简单实现:zhuanlan.zhihu.com/p/38

kajiyaKay对头发的光线描述不准确。Marschner在他的论文里提出了一套更加准确的描述。Marschner假设头发是半透明的光滑圆柱。

所以光线可以分为三部分:

  • R – light that bounces off of the surface of the hair fiber toward the viewer.
  • TT – light that refracts into the hair and refracts out again toward the viewer.
  • TRT – light that refracts into the hair fiber, reflects off of the inside surface, and refracts out

R项是第一次反射项,因为这部分光线没有进入头发丝内部所以他的颜色不会发生变化,如果光是白色那么这部分反射光线的颜色就是白色的。TT是第二部分光线,这部分光线会投射出去,如果摄像机没有在发丝背面那么这部分光线将不会进入摄像机。第三部分是TRT,这部分光线是进入头发以后再反射再传播出头发的光线。这里T字母代表一次传播,R字母代表一次反射。因为TRT这部分光线在头发内部传播过,所以这部分光线的颜色会发生变化。

下面就开始探索基于Marschner的头发光照模型的头发方案。


MAIN CONTENT:

(1)HairShadingModle推导

通过电子显微镜发现,头发表面是粗糙的,并非像kajiyaKay's的假设那样是光滑不透明的圆柱体。

头发鳞片是规整排布的,且头发是半透明的柱状体。光线在头发丝内会有多次反弹,这里为了简单起见我们只把第一次反射,第一次折射和第透射考虑进我们的光照模型,如下图所示:

T字母代表一次折射,R字母代表一次反射。所以我们的光照模型分为三部分R项是第一次反射的光线,因为它没有进入发丝内部所以这个光线不会带有头发的颜色,第二条是TT光线,因为它进入头发前折射了一次,传播出头发的时候又折射了一次。第三条是TRT,经过第一次的折射然后在头发丝的内表面反射然后又再次折射。

其实还可以考虑更多光线的情况

因为性能和性价比等因素我们在这里只考虑前三项:R,TT,TRT。同时因为三维空间处理头发光线的几何问题比较困难,所以我们把头发切为横向和纵向。

横向和纵向:

把头发沿着切线 u 切一刀可得一个法平面(横截面)如下图所示示意图:

下面开始建立模型,光源方向,观察点方向等信息如下所示:

u 为头发切线方向从发根Root指向发梢Tip,这里需要注意的是这里的这个“切线”与模型的切线空间的tangent不要混为一谈,这里的头发切线就是几何意义上从发根到发烧几何方向的切线。

wv是右手标准坐标系的轴向量, w 和向量 v 组成了一个的右手正交基底,同时将 v-w 平面作为法向平面。并且设定【如果纤维的横截面是椭圆的,那么 v 是长轴, w 是短轴。】

w_{i} 是光线或者照明测量方向

w_{r} 摄像机方向

\theta_{i}w_{i} 在法平面上的投影向量与其的夹角

\theta_{r}w_{r} 在法平面上的投影向量与其的夹角

\phi_{i}w_{i} 在法平面的投影向量与 v 轴的夹角

\phi_{r}w_{r} 在法平面的投影向量与 v 轴的夹角

下面假设一些推导量

\theta_{d} = \frac{\theta_{r}-\theta_{i}}{2}

\phi = \phi_{r}-\phi_{i}

\theta_{h}=\frac{\theta_{i}+\theta_{r}}{2}

\phi_{h}=\frac{\phi_{i}+\phi_{r}}{2}

\theta_{i}=\theta_{r} 时, \theta=\theta_{i}=\theta_{r}

其中 w,w_{i},w_{r},u,v,w 都是已知量,可以被我们调节测量,在图形程序里我们也能获取到。\theta_{i}\theta_{r}\phi_{i}\phi_{r}都是未知项。

下面对光路在横纵面上进行简单分析:首先是R项如下图所示:

设纵头发丝是一个单位圆,原点在头发丝圆形横截面圆心处,光线与头发交互处到圆心的偏移值假设为 h ,很容易可以得到 -1<h<1

接下来是TT项如下图所示:

最后一项是TRT

TRT项因为进入了头发所以这项高光是带有颜色的,且TRT紧靠着R项且有一定偏移是因为头发丝鳞片的固定折角。

最后我们得到了完整的纵向和横向的光线传播路径

R,TT,TRT三项均是横向和纵向组成的,它们的纵轴横轴方程分别为MN

要想通过2D的横纵切面分析3D的情况,我们假设的头发丝是光滑的圆柱体表面,光滑的圆柱体表面有两个性质支撑了这一思路。

性质1:一束以特定轴倾角进入电介质圆柱体的射线,总是以相同的倾角离开,不论它的经历的反射和折射的先后顺序

这说明了一束来自与方向 ωi 平行的入射射线,将会产生一个散射射线的集合,散射射线的方向均位于以纤维的轴为中心的椎体上并且包含 -ωi 。头发内部折射的方向也在锥体内。因为散射只在 θi=-θr 时发生有效的将散射方程从4D降低到3D(减少一个参数)当然,这个等式只圆柱体表面粗糙的时候成立,并且它需要假设圆柱体内没有体积散射发生。

性质2:散射分布在 \phi_r 的依赖可以通过只检查在与头发垂直的平面的投影分析。

可以知道R分量可以只通过投影计算——入射和反射向量如同3D空间一样,是关于在投影的表面法线对称的,所以来自3D圆柱体的镜面反射在投影上依然保持镜面反射。这对于折射来说也是一样成立的,也是Bravais定律的结论,表明了如果在电介质表面交界的入射和透射向量投影到一个包含表面法线的平面,投影后的向量依然遵循Snell定律,但是折射率 ηη'(η,θ)>η 代替。注意 η' 只取决于入射光线超出投影平面的倾角 θ ,与投影平面的角度无关。因此,我们知道所有来自特定入射方向的射线保持与法线平面相同的的倾角。对任意折射率在法线平面一个2D的分析足够描述3D散射方程。

因为以上两个性质,允许我们把S方程以写作横轴和方位散射函数N,纵向散射函数M的组合:

S_{R}(\theta_{i},\theta_{r},\phi)=M_{R}(\theta_{i},\theta_{r})N_{R}(\theta_{i},\theta_{r},\phi)/cos\theta_{d}^2

第一个因子 M 表示散射只发生在镜面反射锥体中,第二个因子 N 表示方位角的散射函数

同理TT和TRT可以写作分别为

S_{TT}(\theta_{i},\theta_{r},\phi)=M_{TT}(\theta_{i},\theta_{r})N_{TT}(\theta_{i},\theta_{r},\phi)/cos\theta_{d}^2

S_{TRT}(\theta_{i},\theta_{r},\phi)=M_{TRT}(\theta_{i},\theta_{r})N_{TRT}(\theta_{i},\theta_{r},\phi)/cos\theta_{d}^2

所以最终的高光公式为

S=S_{R}+S_{TT}+S_{TRT}

但是我们这里只考虑了前三项,其实光线还可以继续迭代传播下去

S=S_{R}+S_{TT}+S_{TRT}+S_{TRRT}+S_{TRRRT}+...

下面就把它写得更加通用,所以:

S(\theta_{i},\theta_{r},\phi)=\sum_{p=0}^{\infty}{M_{p}(\theta_{i},\theta_{r})N_{p}(\theta_{i},\theta_{r},\phi)/cos\theta_{d}^2}

其中p=0,1,2,3...

当然在我们的光照模型中只考虑p=0,1,2的情况

要求出整个光照方程下一步就是要求出 M_{p}(\theta_{i},\theta_{r}) 方程和 N_{p}(\theta_{i},\theta_{r},\phi) 方程


纵向散射函数M

光滑圆柱体的分析表明反射的光线会精确的保持在一个镜面反射圆锥体内。我们的头发模型包括两个造成这个表现的偏差的特性。首先,接触面是粗糙的。光线通过圆柱体传播的时候,粗糙度会导致他们的方向从模型计算的方向随机偏离。整体效果为模糊了散射分布,不同的散射模型的模糊程度不同。第二,角质层的鳞屑造成了和理想的圆柱体表面相比法线的倾斜,这造成了散射的瓣(lobe)不在镜面反射圆锥体的中心。

图一表明了我们可以期待的瓣偏移的位置,R模式向偏向发根 角度位移,TRT向远离发梢很远的地方位移,TT是向发梢位移,我们通过偏移每个瓣不同M的平均值近似鳞屑效果。

这里 g(β,x) 是一个单位积分,宽度为β的平均值0的lobe函数。在我们的实现中,使用一个标准差为 β 的标准化的高斯函数。

下面是,模型参数集合

g(\sigma,x-u)=\frac{1}{2\pi \sigma^2}e^{-\frac{(x-u)^2}{2\sigma^2}}

可以得到

M_{R}(\theta_h)=g(\beta_R,\theta_h - \alpha_R)\\ M_{TT}(\theta_h)=g(\beta_{TT},\theta_h - \alpha_{TT})\\ M_{TRT}(\theta_h)=g(\beta_{TRT},\theta_h - \alpha_{TRT})

M_{p}(\theta_h)=\sum_{p}^{}{g(\beta_R,\theta_h - \alpha_R)}



方位散射函数N

我们假设一个圆形截面来推导N

光线的反射和折射传播都会有能量损失,在介质交界处会有反射和折射。在介质里传播时能量还会被介质吸收。

所以可以列出我们的方位散射函数N

N(\phi)=\sum_{p}{N_p(p,\phi)}

N_{p}(\phi)=\sum_{r}{A(p,h(p,r,\phi))\left| 2\frac{d\phi}{dh}(p,h(p,r,\phi)) \right|^{-1}}

其中A是吸收方程

当p=0时,此时就是第一次反射,因为光线未进入发丝内部所以只有菲尼尔项

A(0,h)=F(\eta',\eta'',\gamma_i)

当P>0时,光线进入发丝,此时除了菲尼尔项还会有吸收项

A(p,h)=(1-F(\eta',\eta'',\gamma_i))^2F(\frac{1}{\eta'},\frac{1}{\eta''},\gamma_i)^{p-1}T(\sigma',h)^p

因为T=1-R(下面有j解释)所以吸收项的光强其实是第一次折射的光线。反射部分的 1/\eta 是空气折射率比上介质折射率。

公式中的F就是菲尼尔项。光有两种偏振状态

  1. 偏振入射光的电场分量与入射光及反射光所形成的平面相互垂直。此时的入射光状态称为“s偏振态”,源于德语“senkrecht”。
  2. 偏振入射光的电场分量与入射光及反射光所形成的平面相互平行。此时的入射光状态称为“p偏振态”,源于德语“parallel”。

由反射定律可得

\theta_i=\theta_r

入射光线与折射光线的方向由斯涅尔定律约束:

{\displaystyle {\frac {\sin \theta _{\mathrm {i} }}{\sin \theta _{\mathrm {t} }}}={\frac {n_{2}}{n_{1}}}}

一定功率的入射光被界面反射的比例称为反射比 {\displaystyle R\,} ;折射的比例称为透射比 {\displaystyle T\,}

对反射比和透射比的计算需要用到电动力学中的电磁波传播理论,具体方法可参考玻恩的《光学原理:光的传播、干涉和衍射的电磁理论》[2]以及杰克逊的《经典电动力学》

反射比和透射比的具体形式还与入射光的偏振有关。如果入射光的电矢量垂直于右图所在平面(即s偏振),反射比为

{\displaystyle R_{s}=\left[{\frac {\sin(\theta _{t}-\theta _{i})}{\sin(\theta _{t}+\theta _{i})}}\right]^{2}=\left({\frac {n_{1}\cos \theta _{i}-n_{2}\cos \theta _{t}}{n_{1}\cos \theta _{i}+n_{2}\cos \theta _{t}}}\right)^{2}=\left[{\frac {n_{1}\cos \theta _{i}-n_{2}{\sqrt {1-\left({\frac {n_{1}}{n_{2}}}\sin \theta _{i}\right)^{2}}}}{n_{1}\cos \theta _{i}+n_{2}{\sqrt {1-\left({\frac {n_{1}}{n_{2}}}\sin \theta _{i}\right)^{2}}}}}\right]^{2}}

其中 {\displaystyle \theta _{t}\,}

是由斯涅尔定律从 {\displaystyle \theta _{i}\,}

导出的,并可用三角恒等式化简。

如果入射光的电矢量位于右图所在平面内(即p偏振),反射比为

{\displaystyle R_{p}=\left[{\frac {\tan(\theta _{t}-\theta _{i})}{\tan(\theta _{t}+\theta _{i})}}\right]^{2}=\left({\frac {n_{1}\cos \theta _{t}-n_{2}\cos \theta _{i}}{n_{1}\cos \theta _{t}+n_{2}\cos \theta _{i}}}\right)^{2}=\left[{\frac {n_{1}{\sqrt {1-\left({\frac {n_{1}}{n_{2}}}\sin \theta _{i}\right)^{2}}}-n_{2}\cos \theta _{i}}{n_{1}{\sqrt {1-\left({\frac {n_{1}}{n_{2}}}\sin \theta _{i}\right)^{2}}}+n_{2}\cos \theta _{i}}}\right]^{2}}

透射比无论在哪种情况下,都有 {\displaystyle T=1-R\,}

如果入射光是无偏振的(含有等量的s偏振和p偏振),反射比是两者的算数平均值: {\displaystyle R={\frac {R_{s}+R_{p}}{2}}\,}

所以

F(\gamma,\eta)=\frac{\left| F_{\bot}(\gamma,\eta') \right|+\left| F_{||}(\gamma,\eta'') \right|}{2}

F_{\bot}(\gamma,\eta')=\frac{\eta_1cos{\gamma_i}-\eta_2cos{\gamma_t}}{\eta_1cos{\gamma_i}+\eta_2cos{\gamma_t}}

F_{||}(\gamma,\eta')=\frac{\eta_2cos{\gamma_i}-\eta_1cos{\gamma_t}}{\eta_2cos{\gamma_i}+\eta_1cos{\gamma_t}}

\eta_1=1\\\eta_2=\eta\\\gamma_t=sin^{-1}(\frac{sin\gamma_i}{\eta})

吸收项T

可以直接拟合一个衰减函数

T(\sigma_a,h)=e^{-2\sigma_a(1+cos\gamma_t)}

h=sin\gamma_i=\eta'sin\gamma_t

所以最后的方位散射函数N为

N_{p}(p,\phi,\theta_d)=\sum_{h}{A(p,h)\left| 2\frac{d\phi}{dh} \right|^{-1}}

\frac{d\phi}{dh} =\frac{(\frac{6pc}{\pi}-2)-3\frac{8pc}{\pi^3}\gamma_i^2}{cos\gamma_i}

已知 h=sin\gamma_i

\Rightarrow\frac{d\phi}{dh} =\frac{(\frac{6pc}{\pi}-2)-3\frac{8pc}{\pi^3}\gamma_i^2}{\sqrt{1-h^2}}

c=sin^{-1}(\frac{1}{\eta'})

这个 \frac{d\phi}{dh} 是路径

所以暂时可以得到最后的公式:

S(p)=\frac{\frac{1}{2\pi \sigma^2}e^{-\frac{(x-u)^2}{2\sigma^2}}\times \sum_{h}{A(p,h)\left| 2\frac{d\phi}{dh} \right|^{-1}}}{cos^2\theta_d}



(2)虚幻4中的头发Shading实现

如上图所示,标记1,2,3分别对应R,RTR,TT

在实际工程中,因为头发的完整Shading计算量还是太大,所以做了大量简化和拟合。

纵向散射函数M

M项是高斯分布,其中 μ 为中位数,代码中为 Shift。R 使用的是 Shift, TT 和 TRT 使用的分别是 Alpha[1], Alpha[2]。σ 是方差,代码中与 Roughness 相关。B[0],B[1],B[2] 分别对应R,TT,TRT 中的 σ 。Area 固定为0。Roughness 越小反射却强烈,而集中。x 是 SinThetaL + SinThetaV。与在头发纵切面上,光线和摄像机方向夹角有关。

方位散射函数N

UE4的头发对N项也是极尽所能拟合

R项的N

float Np = 0.25 * CosHalfPhi;

TT项的N

float Np = exp( -3.65 * CosPhi - 3.98 );

TRT的N

float Np = exp( 17 * CosPhi - 16.78 );


R项

菲涅尔项和普通镜面反射相乘Mp 是上面讲的 M 项(下同)。Fp 是菲涅尔项。Np 中的 CosHalfPhi,是方位角差的半角,Np 是公式中 N 项。Specular 越大,反射越强。因为光线没有进入发丝,所以没有头发的 BaseColor。

TT项

T_p 相当于上面公式中 pow(cosθd, 2),Backlit 控制这一项的强度,也就是背光时头发的透光度。这里UE使用了简单公式拟合了吸收因子。

TRT

也是使用了和TT项相同的拟合。

漫反射项

UE4的HairShading自己拟合了一个漫反射

J_d=satruate(\frac{N\cdot L+w}{(1+w)^2})

公式中 w 是 0~1 的一个参数,用来加亮远离光线方向的面,Unreal直接用了1。 Metallic 控制了散射的强度。



(3)资源制作和材质制作以及对虚幻HairShading方案的改进

头发材质制作第一个问题就是头发切线数据从哪里来,要从发根到发烧的切线数据。如果我们制作头发使用的是模型插片的方式,如果这个头发面片的UV是竖直向下,那么可以直接把顶点数据的Binormal变换到世界空间作为发丝的切线,如果头发面片在UV空间是横着摆放,那么就使用顶点数据的Tangent作为发丝的切线。

然后再给头发一个扰动把高光扰遂即可,因为发丝的tangent肯定不是一致的。

完成这些以后还需要对发丝的高光进行过滤,考虑几何遮挡屏蔽掉不应该出现的高光。我对M项也进行了改进,原Marschner的M项是高斯分布,但是其实它严格上并不是

当然也是需要一系列拟合才能搬到实时渲染当中。为了方便起见下面是我在Unity中改进的效果如下:

我对上述模型做了几点改进

(1)发丝可见性烘焙

这一步可以让面数很低的模型发丝更清晰,而不是一片一片的,降低面片感,同时还能为后续的shading作遮蔽。

(3)环境光GI对头发的影响

(2)环境光各向异性

最后关于效率问题,这个头发方案消耗只比UE官方的HairShading高一点点,但是因为大量的预计算和烘焙大大降低了耗损。


SUMMARY AND OUTLOOK:

头发shading发展了很多年,在前人一点点的努力完善下渐渐有了一套正确的解决方案。

enjoy it.


NEXT:


By YivanLee 2020/7/3


Reference:

[1]jianshu.com/p/a16bfbd5c

[2]cnblogs.com/jaffhan/p/7

[3]graphics.pixar.com/libr

[4]graphics.stanford.edu/p

[5]hairrendering.wordpress.com

[6]hairrendering.wordpress.com

[7]hairrendering.wordpress.com

[8]zhuanlan.zhihu.com/p/44

[9]eugenedeon.com/wp-conte

[10]eugenedeon.com/wp-conte

编辑于 2020-12-09 12:03