美文网首页以废弃
UE4 Hair Shading

UE4 Hair Shading

作者: PanzerVor | 来源:发表于2016-08-13 13:55 被阅读2103次

相关参考:

Pekelis et al. 2015, "A Data-Driven Light Scattering Model for Hair"

Marschner et al. 2003, "Light Scattering from Human Hair Fibers"

Marschner 论文相关的分析 part I part II part III 翻译

《GPU Gems 2》中头发制作的章节

http://www.fseraph.com/?p=683

Marschner 的头发散射

Marschner 将反射分成了三个部分。

Marschner 论文中需要的参数如下图:

对于每个像素P都有R,TT,TRT 三项。然后把每项分解成 M 和 N,其中 M 项是纵切面的散射分布(Longitudinal scattering),N 项是横切面的散射分布(Azimuthal scattering)。这样将4D散射,分解成2个2D的部分。

R,TT,TRT 中的 M 项

在 Unreal 的实现中,M 项是一个正态分布

其中 μ 为中位数,代码中为 Shift。

float Shift = 0.035;
float Alpha[] =
{
    -Shift * 2,
    Shift,
    Shift * 4,
};

R 使用的是 Shift, TT 和 TRT 使用的分别是 Alpha[1], Alpha[2]。

σ 是方差,代码中与 Roughness 相关。

float B[] =
{
    Area + Pow2( ClampedRoughness ),
    Area + Pow2( ClampedRoughness ) / 2,
    Area + Pow2( ClampedRoughness ) * 2,
};

B[0],B[1],B[2] 分别对应R,TT,TRT 中的 σ 。Area 固定为0。Roughness 越小反射却强烈,而集中。

x 是 SinThetaL + SinThetaV。与在头发纵切面上,光线和摄像机方向夹角有关。

// R
float Mp = Hair_g( B[0] * sqrt(2.0) * CosHalfPhi, SinThetaL + SinThetaV - Shift );
...

// TT
float Mp = Hair_g( B[1], SinThetaL + SinThetaV - Alpha[1] );
... 

// TRT
float Mp = Hair_g( B[2], SinThetaL + SinThetaV - Alpha[2] );
...

R,TT,TRT 中的其他部分

R

R 部分是菲涅尔项和普通镜面反射相乘。

float Np = 0.25 * CosHalfPhi;
float Fp = Hair_F( sqrt( saturate( 0.5 + 0.5 * VoL ) ) );
S += Mp * Np * Fp * ( GBuffer.Specular * 2 ) * lerp( 1, Backlit, saturate(-VoL) );

Mp 是上面讲的 M 项(下同)。

Fp 是菲涅尔项。

Np 中的 CosHalfPhi,是方位角差的半角,Np 是公式中 N 项。

Specular 越大,反射越强。

因为光线没有进入发丝,所以没有头发的 BaseColor。

TT

float a = 1 / n_prime;
float h = CosHalfPhi * rsqrt( 1 + a*a - 2*a * sqrt( 0.5 - 0.5 * CosPhi ) );
float yt = asin(h / n_prime);
float3 Tp = pow( GBuffer.BaseColor, 0.5 * cos(yt) / CosThetaD );

float f = Hair_F( CosThetaD * sqrt( saturate( 1 - h*h ) ) );
float Fp = Pow2(1 - f);

float s = 0.3;
float Np = exp( (Phi - PI) / s ) / ( s * Pow2( 1 + exp( (Phi - PI) / s ) ) );

S += Mp * Np * Fp * Tp * Backlit;

Tp 相当于上面公式中 pow(cosθd, 2)

Backlit 控制这一项的强度,也就是背光时头发的透光度。

TRT

float f = Hair_F( CosThetaD * 0.5 );
float Fp = Pow2(1 - f) * f;
float3 Tp = pow( GBuffer.BaseColor, 0.8 / CosThetaD );

float Np = (1/PI) * 2.6 * exp( 2 * 2.6 * (CosPhi - 1) );

S += Mp * Np * Fp * Tp;

思路与TT相同,只不过各项的公式不同。

漫反射

float3 FakeNormal = normalize( V - N * dot(V,N) );
N = FakeNormal;

// Hack approximation for multiple scattering.
float Wrap = 1;
float NoL = saturate( ( dot(N, L) + Wrap ) / Square( 1 + Wrap ) );
float DiffuseScatter = (1 / PI) * NoL * GBuffer.Metallic;
float Luma = Luminance( GBuffer.BaseColor );
float3 ScatterTint = pow( GBuffer.BaseColor / Luma, 1 - Shadow );
S += sqrt( GBuffer.BaseColor ) * DiffuseScatter * ScatterTint;

漫反射部分用一个 Hack 的方法模拟次表面散射材质。

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

材质编辑器

Material Attributes

在材质编辑器中 Shading Model 属性选择 Hair,就可以使用 Hair 的光照模型。选择 Masked 是使用 Dither Mask 技术代替半透明。

Material Inputs

Hair 的 Material Inputs 与普通的有所不同。

Metallic 变成了 Scatter。从上文的分析中也可以看出,Metallic 控制着散射强度。

Normal 变成了 Tangent。这个切线应该是指向发根的。

Custom Data 0 变成了 Backlit。这个输入本应该是控制透光度,但是实际上并没有用到。

Opacity Mask,因为选了 Masked,所以有这个选项。可以使用 DitherTemporalAA 节点将半透明的部分变成网点,配合 TemporalAA 产生半透明的效果。虽然效果比真正的半透明差一点,但是不会产生排序问题。

另外比较重要的输入,Base Color,Specular,Roughness 的作用已经分析过了。

相关文章

网友评论

    本文标题:UE4 Hair Shading

    本文链接:https://www.haomeiwen.com/subject/oxoesttx.html