前情提要:
🕹️OpenGL丨ShadowDepthMap实现SSS透射 - 皮肤渲染(3)
🕹️OpenGL丨可分离的次表面散射SSSS - 皮肤渲染(4)
本文主要内容为GDC2013 Next-Generation Character Rendering 的ppt笔记整理总结。如果有空的话还会再研究一下UE5中的具体实现。
令人惊叹竟然已经马上十年过去了.....
第一部分我们主要关注皮肤渲染的细节实现。
皮肤反射 - 双镜叶高光与压表面结构
Gramham(2013)论文通过测量提出皮肤的高光具有两个独立的高光波瓣,Jorge等通过使用不同的粗糙度计算两个高光项来模拟这样的视觉效果,然后以85%,15%的方式线性插值两个高光。
分享中建议第二个高光波瓣的gloss值可以使用2倍的原gloss值,或将specular的指数乘方。
这样我们其实已经得到了不错的表面压结构,但皮肤的微小褶皱会打破高光的形状,Jorge等使用了一张类似噪波的法线纹理来实现这个效果:
距离稍微离远一点的效果是这样的:成片的毛孔结构变成了更细碎的高光。
注:这里的rand()并非每像素随机,而应当是一个提前确定的随机数表。
在作业项目中,使用SD制作了这张噪波纹理。理论上其实应该用FX-map
来做循环,但一直传循环数失败(...),所以改用pixel processor
写了一个正弦波生成器,再用Tile Generator
去实现随机位置和随机大小来模仿上述的公式。
——效果肯定是不如原文的,一方面pixel processor
生成的是一定区域内的正弦波,虽然给了比较大的缩放值,但必然有些部分没有完全覆盖;另一方面缩放损失了一定图像质量。最终生成的噪波纹理只剩下了颗粒感,没有像原文中那么多细线条的纹理效果。
如果用
FX-map
理论上是可以的—— 先用Quadrant
将纹理分到1024*1024(因为FX-map不是逐像素的而是逐细分(?)的,$Pos
是细分的中心位置),然后用iteration
处理循环,并将当前循环次数传给计算的Quandant
用作于Global Random
的seed,剩余绘制跟下面贴的pixel processor
差不多~
获得噪波纹理后,我们要将这些细节与原法线混合,Jorge等推荐使用的混合方法是:Reoriented Normal Mapping (RNM),详细解释可以参考这里。
(但但但....我用RNM直接套公式算出来不太对.......)
这些表面的亚结构也会作用于高光上,计算方法如下:
为了增加美术对该模型的控制,可以对base glossiness和细节变化结合得到:
注意这里的base glossiness应当已经包含了normal map中的表面变换。
总结一下!在本工作流中,gloss map应该只包含低频度的细节信息,比如T区的划分等。
中频度的细节信息储存于normal map中,高频度的细节信息存储于detail tiled noise normal map中。
这两种normal的表面变化都会反应到粗糙度的最终结果上。
(but这个我作业项目里没写)
关于数值的具体信息,Jorge给出了一个参考表格,但还是需要美术根据具体的灯光环境进行微调。 Second Lobe对应的数值应为 2.0*G 或者 S^2.0。
皮肤毛孔
人类皮肤的毛孔是一个非常粗糙复杂的孔状结构,当它被简化至模型与法线表示时,它就变成了一个“盆地”,which means毛孔的底部会产生不正确的反光。
一张曲率贴图即可屏蔽正面的毛孔反光,但这张表现毛孔的specular occlusion会极大程度地影响脸侧面的高光:将cavity乘入Fresnel项可以解决这个问题。