酷炫的头发渲染着色器

解析头发渲染

Posted by Bob on December 28, 2018

解读头发渲染

本人才疏学浅,翻译难免有误,请不惜指教。

头发渲染

大多数人头上都有头发,它是一个很重要的可视化效果。头发渲染做起来很难:

1、人头发依种族和发色的不同,数量也略有差异。黄种人有10万根,金发色头发的白种人头发较细,有12万根, 红色头发略粗,有8-9万根。

2、人有大量不同类型的发式。

3、要渲染效果精致、看起来很有神韵,要花费整个人体渲染时间超25%的占比。

为什么选用多边形头发模型

1、比line rendering的几何体复杂度更加简单,depth sorting更快

2、美术工作流集成也方便

头发模型创作

1、多层面片近似模拟头发的体积质量

image

2、用环境光遮蔽(Ambient Occlusion)近似自阴影,逐顶点操作。

3、基本贴图:拉伸的噪音贴图(Stretched noise)

image

4、Alpha贴图:有完全不透明的区域

image

5、Specular shift texture/Specular noise texture

头发光照模型:Kajiya-kay Model

1、各向异性股(anisotropic strand)光照模型

2、假设头发法线在T和视角V构成的平面上

image

3、在光照方程中使用发丝切线T替代法线N

image

切线T和半角向量H之间夹角为θ,切线T和法线N垂直

反射光H = normalize(入射光方向L+视角方向V)

cosθ = T·H

cosθ^2 + sinθ^2 = 1

头发光照模型:Marschner Model

1、基于测量的头发散射属性

2、观察:主高光向发梢偏移;二级高光向发根偏移,有闪烁的呈现。

image

主高光是直接反射,二级高光是穿透(折射)进头发内部,在头发内部进行反射,再穿透(折射)出。

image

3、数学很复杂,只是尝试匹配观察到的效果的和实际的生理效果

shader

1、vertex shader

仅仅传切线向量、法线向量、视角向量、光照向量、环境光遮蔽

2、piexel shader

漫反射光照

  • Kajiya-kay 漫反射中sin(T,L) 看起来太亮,没有自身阴影效果。

  • 使用调整过的N.L公式

二层偏移镜面高光(shifted specular highlights)

合并公式

偏移镜面高光(shifting specular highlights)

image

1、为了沿着头发丝方向产生偏移镜面高光,我们需依着法线的方向将切线做一些偏移。

2、假设T是从发根到发梢一系列点

  • 正向偏移为向发根偏移高光

  • 负向偏移为向发梢偏移高光

3、通过下面贴图来描述偏移值不连续头发效果

image


float3 ShiftTangent(float3 T,float3 N,float shift){

    float3 shiftedT = T + shift*N;

    return normalize(shiftedT);

}

发丝的切线方向T代替了法线方向N来进行各向异性的光照计算,美术沿着发丝的生长方向去展开uv坐标(u或v的方向),使得这个方向恰好是切向方向。

发丝高光(specular strand Lighting)

1、我们使用半角向量来做高光,使用反射向量和视角向量会使着色器更复杂一些

2、两个高光采用了不同颜色的、高光指数(exponent)和不同偏移的切线系数

3、用噪音图来调整二级高光

image


float3 StrandSpecular(float3 T,float3 V,float3 L,float exponent){

    float3 H = normalize(L + V);

    float dotTH = dot(T,H);

    float sintTH = sqrt(1.0-dotTH*dotTH);

    float dirAtten = smoothstep(-1.0,0.0,dot(T,H));

    return dirAtten * pow(sintTH,exponent);

}

Smoothstep解释:


float smoothstep(float edge0, float edge1, float x) {
  // Scale, bias and saturate x to 0..1 range
  x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); 
  // Evaluate polynomial
  return x * x * (3 - 2 * x);
}

float clamp(float x, float lowerlimit, float upperlimit) {
  if (x < lowerlimit)
    x = lowerlimit;
  if (x > upperlimit)
    x = upperlimit;
  return x;
}

关于dirAtten解释来自stackoverflow,可见下图。

image

最终代码

image


float4 HairLighting (float3 tangent, float3 normal, float3 lightVec, 
                     float3 viewVec, float2 uv, float ambOcc)
{

    // shift tangents

    float shiftTex = tex2D(tSpecShift, uv) - 0.5;

    float3 t1 = ShiftTangent(tangent, normal, primaryShift + shiftTex);

    float3 t2 = ShiftTangent(tangent, normal, secondaryShift + shiftTex);

    // diffuse lighting

    float3 diffuse = saturate(lerp(0.25, 1.0, dot(normal, lightVec)));

    // specular lighting

    float3 specular = specularColor1 * StrandSpecular(t1, viewVec, lightVec, specExp1);

    // add second specular term

    float specMask = tex2D(tSpecMask, uv); 

    specular += specularColor2 * specMask * StrandSpecular(t2, viewVec, lightVec, specExp2);

    // Final color

    float4 o;

    o.rgb = (diffuse + specular) * tex2D(tBase, uv) * lightColor;

    o.rgb *= ambOcc; 

    o.a = tex2D(tAlpha, uv);

    return o;
}

最终效果

image

image

近似深度排序 Approximate Depth Sorting

1、需要按照从后向前顺序进行绘制,才能正确地进行alpha-blending

2、头发也是由内向外的

3、使用一个静态index buffer由内向外绘制,预先计算。

4、排序连接的组件(发丝贴片)而不是单个三角形

image

Sorted Hair Rendering Scheme

Pass 1 - 不透明部分

  • 启用alpha test仅通过不透明像素

  • 禁用背面裁剪

  • 启用Z writes, 设置Z test为Less

Pass 2 - 透明背面部分

  • 启用alpha test仅通过所有非不透明像素

  • 剔除前面的多边形

  • 禁用Z writes, 设置Z test为Less

Pass 3 - 透明前面部分

  • 启用alpha test仅通过所有非不透明像素

  • 剔除背面的多边形

  • 启用Z writes, 设置Z test为Less

性能调优 Performance Tuning

1、广泛使用early Z 剔除,以节省我们运行昂贵的pixel shader

2、通常一半的头发藏在脑后,所以先画头

3、当启用 alpha test 时,不能使用early Z 剔除!

  • 解决方案: Prime Z buffer with a very simple shader that uses alpha test

  • Use Z testing instead of alpha testing in subsequent passes for same effect

4、Early Z剔除节省了相当大的填充开销(fill overhead)!

Optimized Rendering Scheme

Pass 1-prime Z buffer

  • 启用alpha test仅仅传不透明的像素

  • 禁用背面剔除

  • 启用Z writes, 设置Z test为Less

  • 禁用color buffer writes

  • 使用只返回alpha的简单pixel shader

Pass 2-不透明部分

  • 禁用背面剔除

  • 禁用Z writes, 设置Z test为Equal

Pass 3-透明背面部分

  • 剔除前面的多边形

  • 禁用Z writes, 设置Z test为Less

Pass 4 -透明前面部分

  • 剔除背面的多边形

  • 禁用Z writes, 设置Z test为Less

优化后渲染效果

image

优缺点 Pros and Cons

优点:

1、低几何复杂性

a、减少顶点引擎的负载 

b、使深度排序更快

2、可用于低端硬件使用简单的着色器或固定功能管道

缺点:

1、排序构架假设头发模型中没有动画

a、像摆动的马尾辫这样的东西需要分开处理

b、在运行时对几何图形进行排序以克服这个问题

2、像摆动的马尾辫这样的东西需要分开处理

Unity Demo工程

Unity工程收集整理于互联网

参考资料

眼睛渲染

角色渲染

GDC2004头发渲染

头发渲染

Anisotropic Lighting

Light Scattering from Human Hair Fibers

Applications of Explicit Early-Z Culling

Early-Z Culling

Early Z Rejection

日本毛发效果

Advanced Hair Shader Pack插件

The Blacksmith: Hair Shader

The Blacksmith

UnityHairShader

J. Kajiya and T. Kay. Rendering fur with three dimensional textures. In SIGGRAPH 89 Conference Proceedings, pp. 271-280,1989.

Stephen R. Marschner, Henrik Wann Jensen, Mike Cammarano, Steve Worley, and Pat Hanrahan, Light Scattering from Human Hair Fibers. In Proceedings of SIGGRAPH 2003.