UE4 Hair Shading

相关参考:

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 将反射分成了三个部分。

图1
图1
  • R:在头发表面反射的光线
  • TT:光线折射进头发然后折射出的光线
  • TRT:光线折射进头丝,再在内部反射后最终折射出的光线

R 表示反射 (reflection),T 表示穿过 (transmission)。R 和 TRT 都是正面反射的光,但是形成了两块完全不同的区域。并且,R 因为没有穿过头发表面而呈现白色,TRT 则带有头发的颜色。

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

图2
图2
  • u:头发切线,从发根到发梢
  • w:头发法线(v垂直于w,wv平面为法平面)
  • wi:光线方向
  • wr:摄像机方向
  • θi:光线与法平面夹角
  • θr:摄像机与法平面夹角
  • φi:光线方位角(wi在法平面的投影与v的夹角)
  • φr:摄像机方位角(wr在法平面的投影与v的夹角)

对于每个像素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 的作用已经分析过了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 201. M-Q型显影液组合是()。 (2.0 分) A. 米吐尔与菲尼酮的组合 B. 对苯二酚和菲尼酮的组合 C...
    我们村我最帅阅读 3,301评论 0 4
  • 一、Surface Output (表面着色器的标准输出结构)Surface Shader的标准输出结构-第一要素...
    CarlDonitz阅读 906评论 0 1
  • 1. 关于诊断X线机准直器的作用,错误的是()。 (6.0 分) A. 显示照射野 B. 显示中心线 C. 屏蔽多...
    我们村我最帅阅读 10,249评论 0 5
  • 只要你能够连接到网络,什么都没有,什么都不知道,你也可以分分钟成就为:数据(相片)恢复大师——只要你愿意,这个可以...
    斯麦尔阅读 863评论 3 21
  • 每次走过你的身旁 总能闻见那淡淡的 百合花的香 从此那种花香 成为我无边无际的向往 当不能遇见你的日子 我买来百合...
    因你写诗阅读 429评论 2 1