Hair Rendering and Shading头发渲染和着色[GDC2004]

摘要:
Kajiya-Kay着色模型是头发着色的经典模型,在RealTimeRendering3th有介绍。这里的着色实现结合了Marschner在SIGGRAPH2003上展示的模型,效果更佳。同时Marschner模型也是UE4在Paragon中使用的头发着色模型,只不过这里用到的Marschner着色模型的特性比较单一。用于为头发的次要精美发射高光提供随机强度遮罩。Kajiya-Kay中,头发丝被视作圆柱体。这里采用了Marschner模型的两个观察结果,主要考虑了头发生长方向对高光的影响,Paragon的头发采用的更复杂。从SpecularShiftTexture中查找偏移值,避免头发面片上一致的表现,增加随机表现。

GDC2004上ATI的一个分享,现在还是大多数移动平台游戏的头发着色方案。

Hair Rendering and Shading头发渲染和着色[GDC2004]第1张

主要的技术要点:Kajiya-Kay着色模型,Marschner着色模型,多Pass解决深度排序。

Kajiya-Kay着色模型是头发着色的经典模型,在Real Time Rendering 3th有介绍。这里的着色实现结合了Marschner在SIGGRAPH2003上展示的模型,效果更佳。同时Marschner模型也是UE4在Paragon中使用的头发着色模型,只不过这里用到的Marschner着色模型的特性比较单一。Kajiya-Kay着色模型作为毛发的入门级模型,本文会比较详细的介绍。至于Marschner模型,由于本文实现利用的模型特征很表面,真正的Marschner模型涉及到复杂的光路,如果之后会写UE中的基于物理的头发着色,会详细介绍。

本文不是简单的翻译,而是加入了一些进阶或补充的内容。

——多边形的头发建模优势

1.比线渲染更低的几何体复杂度——加快深度排序。显卡厂商Nvidia和ATI都提供了自己的毛发解决方案,Hairworks和TressFx,提供了优秀的物理效果,其对引擎的支持和GPU的要求是局限。

2.能够更好的整合进美术制作的管线流程中。制作对美术人员没有任何难度,同时游戏引擎都提供完全的支持。

——多边形的头发建模步骤

1.多个面片层叠来近似体现头发的体积感,在Paragon中,也是使用面片来进行头发的制作。

2.逐顶点的环境遮蔽来近似自投影效果。目前自投影实现已经不是难题,所以这部分的计算可以忽略。如果要使用环境遮蔽的话,使用目前通用的AO方案,如SSAO,DFAO等。

——需要的纹理要求

1·Base Texture(gayscale):垂直拉伸的噪波贴图。主要是区分面片上发丝。

2.Alpha Texture(gayscale):透明度贴图,用于表现头发丝状,需要有完全不透明的部分。完全不透明的部分是用于深度写入的部分,在纹理制作上,发梢区域应该为完全不透明。

3.Specular Shift Texture(gayscale):镜面反射偏移纹理。用于为头发的两个镜面反射高光提供随机值。

4.Specular Noise Texture(gayscale):镜面反射噪波纹理。用于为头发的次要精美发射高光提供随机强度遮罩。

以上都是灰度图,在效果要求不高,即四张纹理可以通用的情况下,可以合成一张RGBA纹理,节约采样器。

——Kajiya-Kay模型

Kajiya-Kay是一个各向异性的线/纤维光照模型,它使用了头发的切线,而不是法线来进行光照计算。假设头发的法线位于切线和视线范围内的平面上。

Kajiya-Kay模型的原始论文:https://www.cs.drexel.edu/~david/Classes/CS586/Papers/p271-kajiya.pdf

论文的内容主要是使用3D纹理渲染毛发,关于Kajiya-Kay模型,只需要看Lighting model for hair这一节即可。

Kajiya-Kay中,头发丝被视作圆柱体。光照模型包含两个部分,分别是漫反射项镜面反射项。漫反射项本质是Lambert模型在微小的圆柱体的应用,镜面反射项则是与Phong镜面反射模型相似的,同样为圆柱体做调整的特殊模型。

如下图所示,介绍Kajiya-Kay模型中用到的数学表示,向量均为单位向量。

t——切线

l——光照向量

e——观察向量,应该用v更合适

x0——圆柱上的任意一点,也就是发丝上的任意一点

P——垂直于t的屏幕,也是l‘的投影面

l'——l在P上的投影

b——与t,l'垂直的向量

t,l',b为基向量(basis vector),他们互相垂直

Hair Rendering and Shading头发渲染和着色[GDC2004]第2张

计算三个基向量,用于之后的计算。

t——模型切线,资源给出。

l'——normalize(l-dot(t,l)*t) ,l减去l在t上的投影向量,并进行单位化

b——cross(t,l'),t和l’的叉积

之后,我们给出P在x0处圆柱的切面,用于分析漫反射项的计算。

n(θ)——角度为θ的法线向量

Hair Rendering and Shading头发渲染和着色[GDC2004]第3张

漫反射项的计算

我们知道,光在平面上的一点产生漫反射,方向范围是一个半球,在切面上,就是一个半圆。也就是说,我们只需要在图中0到π的角度范围中计算漫反射项。

首先,根据Figure7,我们能得到半圆上的法线分布方程:

n(θ)=bcos(θ)+l'sin(θ)

假设漫反射系数为kd,那么漫反射项就是l·n在0到π的定积分*kd。经过数学计算,我们可以得到漫反射项的计算结果:

Kd*sin(t,l)

Hair Rendering and Shading头发渲染和着色[GDC2004]第4张

镜面反射项的计算

Phong的原始公式是镜面反射系数*pow(cos(V,R),镜面反射指数)。在Kajiya-Kay模型的镜面反射公式中,将反射向量R用e'来代替。

这是因为在Kajiya-Kay模型中,光照击中头发之后,反射方向是沿着切线而不是法线以镜面反射角度射出的,这样所有的反射光线均位于以切线为轴线,角度为θ(光照与切线之间的夹角)的圆锥体上。

Hair Rendering and Shading头发渲染和着色[GDC2004]第5张

e'是被包含在圆锥体中的最接近观察向量的镜面反射向量,如下图所示。

可以理解为e'是e在圆锥体上投影

Hair Rendering and Shading头发渲染和着色[GDC2004]第6张

——Marschner模型

Marschner是一个基于头发散射属性测量而得到的模型,模型本身比较复杂。这里采用了Marschner模型的两个观察结果,主要考虑了头发生长方向对高光的影响,Paragon的头发采用的更复杂。

1.主要的镜面反射高光向发梢偏移。

2.次要的镜面反射高光向发根偏移,且有色。

这是因为:

主要的镜面反射高光是R(反射)光路的表现,直接反射了光照。

次要的镜面反射高光是TRT(透射-反射-透射)光路的表现,它透射进头发内部(折射+吸收),在头发内部又反射(反射多次,这里只考虑一次)到空气中。在这个过程中,光线从头发透射出去的位置已经与光线击中头发透射进的位置产生了偏移,同时因为光线在头发内部散射而产生了颜色。

Hair Rendering and Shading头发渲染和着色[GDC2004]第7张

——Shader拆解

顶点着色器:将切线,法线,观察向量,光照向量,环境遮蔽项传递给片段着色器。追求效果可以考虑逐像素计算部分向量。

片段着色器:

1.漫反射照明:在没有适合的自投影下,Kajiya-Kay模型的漫反射项sin(T,L)看起来过亮,这里使用调整的N·L项。

2.两个偏移的镜面反射高光:主要镜面反射高光和次要镜面反射高光。

2.将以上各项合并到颜色输出。

——偏移镜面反射高光

为了沿着头发长度偏移镜面反射高光,我们将切线向着法线的方向微移。

假设切线T是从发根指向发梢的向量:

—正的微移值向发梢移动高光。

—负的微移值向发尖移动高光。

从Specular Shift Texture中查找偏移值,避免头发面片上一致的表现,增加随机表现。

使用半角向量来计算发丝的镜面反射高光:

—使用反射向量和观察向量会增加一点着色器的复杂度。(使用IBL可以完全无视)

两个高光有不同的颜色,镜面反射指数和不同的偏移的切线。

使用Specular Noise Texture来调整次要高光。

——近似的深度排序

对于正确的alpha blending显示,需要从后向前的顺序渲染。

对于一个有头发的头部,从内到外的绘制顺序。

使用静态索引缓冲从内到外的绘制顺序,在预处理时间被计算:

—对相连的组件(头发丝面皮)而不是单独的三角形进行分类。

——分类的头发渲染步骤

Pass1——不透明部分

—开启alpha test,只通过不透明像素

—关闭背面剔除Cull Off

—开启深度写入Zwrite On,深度测试为Ztest Less

Pass2——半透明的背面部分

—开启alpha test,只通过半透明像素

—开启背面剔除Cull Back

—关闭深度写入Zwrite Off,深度测试为Ztest Less

Pass3——半透明的前面面部分

—开启alpha test,只通过半透明像素

—开启前面剔除Cull Front

—关闭深度写入Zwrite Off,深度测试为Ztest Less

第一个Pass用来渲染不透明像素并写入深度缓冲,因为我们要求alpha贴图一定要有不透明区域。这样头发丝的不透明部分写入了深度,可以很大的减少排序问题。

第二个Pass和第三个Pass只有剔除设置不同,先渲染背面后渲染前面,一是头发是双面显示的,二是避免半透明像素不正确的前后遮挡关系。

——性能调整

广泛的使用eary Z剔除可以节省运行昂贵像素着色器。

通常来讲,大约一半的头发会被头挡在后面看不到:

—先绘制头。(如果头部使用更复杂的计算的话,自己权衡吧。)

当alpha test开启的时候,early Z不能使用:

—用一个十分简单的Pass只做alpha test。

—对于相同的效果,在子队列Pass中使用深度测试而不是alpha测试。

early Z剔除节省了可预见的填充开销。

不过现在移动平台是tiled架构,early Z更多的还是针对PC平台。现在移动平台GPU广泛使用的是Low Resolution Z的技术。

——优化的渲染步骤

Pass1——深度缓冲

—开启alpha test,只通过不透明像素

—关闭背面剔除Cull Off

—开启深度写入Zwrite On,深度测试为Ztest Less

—禁用颜色缓冲写入ColorMask RGB

Pass1——不透明部分

—关闭背面剔除Cull Off

—开启深度写入Zwrite On,深度测试为Ztest Less

Pass2——半透明的背面部分

—开启背面剔除Cull Back

—关闭深度写入Zwrite Off,深度测试为Ztest Less

Pass3——半透明的前面面部分

—开启前面剔除Cull Front

—关闭深度写入Zwrite Off,深度测试为Ztest Less

——优缺点

1.可以在低端硬件&移动平台实现一般的头发渲染效果,性能消耗在可以接受的范围内。在深度优化相关的方面,可以学习网易《楚留香》的trick,返回黑色的不透明头发模型+使用各向异性镜面反射渲染的半透明模型叠加的实现方式,减少Pass。

2.这种制作方式适用于多层面片叠加制作的头发。对于头发为有体积的polygon的渲染实现则不适用。

免责声明:文章转载自《Hair Rendering and Shading头发渲染和着色[GDC2004]》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇前端插件--总结ClickHouse源码笔记3:函数调用的向量化实现下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章