【图形学】Realtime Skin Shading综述

又是天气晴朗的一天,开开心心来到工位,接收美术给的模型和贴图,打开引擎,新建场景,添加材质,嘴角疯狂上扬,然后——

这个角色,皮肤蜡黄,没有血色,像塑料一样!

看不出来问题在哪的话,可以看看这张图,对比一下会发现好的皮肤的红润感主要体现在明暗交界处,会发红:

1. BSDF

为什么皮肤会发红?我们的Standard PBR Shader为什么不能处理这种情况呢?这是因为,我们使用的默认pbr shader模拟的是“硬表面”这种介质,而光进入不同介质时的行为是不一样的,可以分为以下三类。

你可能听说过中间的BRDF,它描述了光打在物体表面某一点,又从该点重新出射的情况;左边的BSSRDF则是光的出射点不同于入射点;而BTDF则是光从另一面出射了。真实情况下,这三种现象都会发生,它们统称为BSDF。我们人为地将它们划分为这三类,方便列公式,并且在有的时候舍弃掉其中的某些部分,用来加速计算。比如unity中的standard pbr shader只能表达BRDF,这对一般的硬表面是够用的,而到了大理石、牛奶、皮肤、树叶这种材质时,BSSRDF和BTDF的视觉效果变得很明显,我们不能再忽视掉它。

2. 皮肤的BSDF

那么皮肤会涉及哪些BSDF呢?我们来看看学者的研究结果:

学界认为皮肤由表面的油脂层和下面的另外两层构成。皮肤有这样的结构,它的BSSRDF和BTDF是不可忽视的。

我们插播一段话解释一下漫反射和高光反射,不需要可以跳过。在图形学中我们经常将光照结果分为漫反射(diffuse)和高光反射(specular)两类,两者的和是最终的颜色。高光反射就是我们日常所说的高光点,它的本质是说高光反射值和视线(或光线)方向有直接关系,光或者你转了方向,高光点的位置也会发生改变。相对的,漫反射则表示不随视线(或光线)方向变化而明显变化的光照。

 继续刚才的话题,这个皮肤模型的高光项和漫反射项可以怎么写呢?

 3. 皮肤的高光项

计算高光项最常用的是phong或者blinn-phong模型。学者通过对皮肤油脂层的建模,设计出了Kelemen/Szirmay-Kalos高光模型,是专门给皮肤的模型。我们从下图可以看到,ks模型在视线和法线夹角更大的时候高光更明显(下边两张图)。根据这篇博客的实际体验,使用ks模型,“油腻的师姐”感太强,参数调低效果又不明显。所以phong模型不是全输的,使用哪种模型看自己的选择。

 除了高光模型的选择,我们还要介绍高光的第二件事,这算是一种从视觉效果出发的渲染技巧了:计算多次高光。计算一遍低光滑度的高光,再计算一遍高光滑度的高光,加在一起。nvidia07年放出的demo计算了四次高光。

4. 皮肤的漫反射项

如下图所示,漫反射包括我们开始讲过的BTDF和BSSRDF 。BTDF对应光从手指后边照过来,手指边缘会透光的现象。BSSRDF则对应阴影有红光的效果。

我们接下来看看这两种效果如何模拟

4.1皮肤的BSSRDF

 如下图左侧所示,一束光线垂直射入皮肤,光线在皮肤内部传播交互,在其他点重新出射且没有明显的方向性,是漫反射光。右侧的图是黑暗中一道光线照在耳朵上,可以看到光线直接照射处是白色的,周围还会有散射的红光。

我们用Diffusion Profile来表示扩散的参数,如下图所示。在unity HDRP中,系统内置了该功能,可以在预览中看到左侧这个图。现在我们懂了,这是扩散情况的可视化。右侧的曲线表示不同距离下rgb三种光线的扩散情况,可以看到红色光下降最慢扩散最远,所以我们觉得皮肤会“透红光”。如果是绿皮肤的外星生物,那这个diffusion profile可能就是绿色的。

怎么在技术上实现这种光的扩散呢?以下介绍几种方法

4.1.1 texture space blur

如下图所示,我们把光打在一个像素上,那么光会“扩散”(之后也用这个更加形象的词)到周围,我们可以想到最直观的方案——It's a blur!

 

上图是这种做法的流程图。先计算整张皮肤漫反射的结果(比如用lambert diffuse)并展开成一张纹理(也叫转换到texture space),进行若干次blur(demo用了六次),将每次blur结果加在一起作为diffuse,最终加上specular。

4.1.2 screen space blur

还是blur,只不过不在texture space做,而是在screen space做,将需要blur的部分用stencil buffer标记。这样的话开销是固定的,在屏幕几乎没有皮肤时最不值。

 4.1.3 pre-integrated预积分方法

每次blur要消耗两个pass,横着blur一次,竖着blur一次,多次blur消耗很大。预积分方法完全从结果出发,将结果烘焙在贴图上,以光照的变量作为索引,制成一个查找表(look-up table, LUT)。

如上图所示,左侧是LUT,输入横轴纵轴的值,对应像素的颜色就是diffuse color(当然后续还要乘以物体颜色光颜色之类的)。横轴是N dot L的取值,他表示法线与光线夹角的cos值。图中的这些颜色怎么理解呢?我们忽略纵轴,只看NdotL对颜色值的影响,可以看到右侧NdotL>0时,N和L夹角小于90度,表明光照在物体表面,所以结果是白色;反之,NdotL<0,N和L夹角大于90度,意味着这个点在物体背面,所以光照结果是黑色的。

 纵轴1/r表示曲率,可以看到在右侧的人脸上额头、脸颊相对平整,曲率低,而鼻沟、眼皮和耳朵曲率高。

曲率高的地方​LUT的y值更大,此时NdotL在明暗交界处会偏红,暗部会更亮,也就是更透红光。

为了使用该方法,我们需要提前渲染一张角色的curvature map,就像图中的人脸一样。Substance painter等dcc软件可以生成。进行皮肤渲染的时候,在shader中计算NdotL作为x值(-1~1映射到0~1),读取curvature map作为y值,读取look-up table的颜色值,这个值在光照计算中替换掉原来的光照模型的NdotL。也就是说新的NdotL不仅是0~1的,而且是有色相的。下图是默认的光照计算和修改后的光照计算。

4.1.4 Light Warping

这是一种比预积分更省的方法,它不需要look-up table也不需要curvature map,但是也像预积分方法一样用重新映射NdotL的手段模拟次表面散射。如下图所示,我们直接对NdotL的结果做一个数学运算,用一个参数控制暗处的提亮。

可以看到warp=0时,NdotL输入是多少输出也是多少,当warp增加时,暗部会越来越亮,但最大值还是1。想要色相变化,再乘以一个颜色参数即可。

4.1.5 讨论

手机上是不考虑blur方法的,再怎么优化来回blit也受不了。至于预积分和warping方法,warping方法少两张图的消耗,无法区分身体不同部位的细节变化,相当于让预积分方法的curvature map都是一个颜色。不过其实curvature map的颜色变化也不是很多,如果不是贴近怼脸,看不出什么区别。如果是结构复杂的模型,还可以考虑下预积分。

4.2 皮肤的BTDF

BTDF对应的是逆光也能看到光的透射(transmission)效果,其实经常被和BSSRDF搞混。体现皮肤质感最主要的是BSSRDF,BTDF只有在皮肤薄的地方才有明显效果。下图是开启BTDF前后的效果。

这篇文章讲解了具体做法,其核心是令NdotL小于零的部分变亮。想实现比较完整的效果还需要一张表示模型厚度的thickness map。

5. References

Chapter 14. Advanced Techniques for Realistic Real-Time Skin Rendering | NVIDIA Developer

https://developer.download.nvidia.com/presentations/2007/gdc/Advanced_Skin.pdf

《GPU Gems 3》:真实感皮肤渲染技术总结 - 知乎

角色渲染技术——皮肤 - 知乎

Unity Shader - 伪次表面散射模拟 - 知乎

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值