下面的 OSL 材质模拟了 Tony Reynolds 制作的皮肤着色器,它采用了由红、绿、蓝波长调制的分层次表面散射(layered subsurface scattering)。此外,着色器实现了一个 Dual lobe Specular (two-lobe spec,双叶瓣高光)来捕捉人类皮肤上高光的微妙变化。
不同波长的光穿透皮肤的深度不同。如果我们将可见光谱简化为三种颜色(红、绿、蓝),那么每一种颜色的平均自由路径(光子在被散射或吸收之前穿过特定物质的距离)的值就会不同。对于人类的皮肤来说,红色是最深的,然后是绿色,蓝色是相当浅的。
color Depth = getTextureDif(RGB_Scatter_Depth, color(0.8,0.2,0.1));
...
color SubColor = getTextureDif(Subsurface_Color, color(0.85,0.75,0.65));
...
color SubR = SubColor * color(1,0,0);
color SubG = SubColor * color(0,1,0);
color SubB = SubColor * color(0,0,1);
基于以上理论,着色器将主颜色纹理(次表面颜色)分离为红色、绿色和蓝色部分,并将这些部分输送到三个次表面着色器中,然后将它们组合在一起。这三个次表面着色器的深度(即平均自由路径)由相应的 RGB_Scatter_Depth
颜色的红、绿、蓝通道驱动。为了加快速度,将绿色和蓝色结合在一起,所以着色器将两个 SSS 闭包组合在一起(红+绿&蓝)而不是三个。
closure color SSS = vray_subsurface ( IOR, PhaseFunction, Depth * Depth_Scale * 0.5, SubColor * Subsurface_Amount, "subdivs", Subdivs);
closure color SkinR = vray_subsurface ( IOR, PhaseFunction, SubR * Depth[0] * Depth_Scale, SubR * Subsurface_Amount, "subdivs", Subdivs); closure color SkinGB = vray_subsurface ( IOR, PhaseFunction, ((SubG * Depth[1]) + (SubB * Depth[2])) * Depth_Scale, (SubG + SubB) * Subsurface_Amount, "subdivs", Subdivs);
所基于的想法是,所有人类的主要散射颜色是红色(因为我们在不同的肤色下都有红色的血液)。
换句话说,RGB 散射深度本身并不是一种颜色,而是分别代表红、绿、蓝三种波长的散射系数。因此,将波长 RGB 深度颜色的红色通道设置为 1.0 意味着红色波长的深度是 100% ,而将其设置为 0.7 意味着它是 70%,以此类推,对于所有三个 RGB 通道。
请注意,在下图中,红色通道比绿色或蓝通道更柔和,因为它有更多的深度。
这种方法的另一个好处是,当它设置的散射量较大时,它几乎消除了 SSS2
材质的薄部分可能出现的 “绿色误差”,因为它将多个次表面着色器线性混合在一起,而不像在 SSS2
材质将中次表面和散射颜色之间进行更复杂的相互作用,虽然更 物理正确,但对艺术家来说可能 不直观。
总的来说,这个着色器的 重点 是优先考虑 艺术控制 而不是 物理正确性 。像 VraySkinMtl
这样的分层皮肤着色器的一个 缺点 是,用户需要将不同(通常 不直观)的颜色混合在一起到达想要的皮肤颜色。autodesk 在谈到他们的分层 Miss*
着色器时表示: “每一层都有自己独特的颜色,这使得很难达到给定的“最终”颜色,并且改变图层之间的平衡会使颜色失去效果。” 同样, SSS2
材料的一个 缺点 是,由于次表面散射计算涉及复杂的数学,艺术家可能会选择一个特定的颜色,在渲染中却得到一个完全不同的颜色。
相比之下,使用本文的这个着色器,用户只需输入皮肤的单个纹理贴图到 Subsurface Color
中,就可以得到他们想要的结果。同样地,用户设置想要在渗出效果中看到的 Wavelength RGB Depth
的颜色(记住,这也会影响散射深度),他们也会得到 所见即所得 的结果。
该着色器包含一个设置为灰色的漫反射参数,以便在真实皮肤中模拟单一散射。换句话说,这就像一个非常粗糙的镜面反射,因为皮肤是电介质(dielectric),所以应该是无色的。由于 OSL 采用线性混合的工作方式,这比 SSS2
中的漫反射工作得更好,后者实际上抵消了 SSS
。相反,在这里,0.5 左右的值就可以起到不错的效果。如果你想要更偏向一些卡通效果,你可以将其设置为 0。
着色器将两个高光叶瓣(specular lobe)组合在一起。使用 GGX microfacet BRDF (GGX 相当于尾部衰减参数锁定在 2.0 的广义 Trowbridge-Reitz (GTR) BRDF) 作为 “尖锐”(硬) 高光叶瓣,以及使用 Blinn BRDF 作为 “软” 高光叶瓣。OSL 闭包用 SSS 平衡了这两个高光叶瓣,因此闭包总和不超过 1.0,使其 能量守恒(即_反射光总量不能超过入射光总量_)。如果您想要更专业一点,渲染器将闭包各组件根据权重相加,如果它们超过 1.0,则将权重除以它们的和,使它们保持相同的平衡。
/// @note 硬高光
closure color spcHard =Sharp_Amount * SpecColor * Spec_total_amount * 0.5 *microfacet_ggx (Nb, Roughness, Reflection_IOR,"gtr_gamma", Tail,"subdivs", Reflection_Subdivs, "reflection_glossiness", Spec_sharpness,"highlight_glossiness", Spec_sharpness,"trace_reflections", Trace_Sharp_Reflections);
/// @note 软高光
closure color spcSoft = Bloom_Amount * Fresnel * Spec_total_amount * 2 *vray_blinn (Nb, SoftGloss, Anisotropy, Aniso_rotation,"subdivs", Reflection_Subdivs, "trace_reflections", Trace_Bloom_Reflections);
高光光泽度可以通过连接黑白贴图(参见下面的图1)到 Gloss Mask
参数来驱动。该贴图的 白色 部分将获得 “最大光泽度” 值,黑色 部分将获得 “最小光泽度” 值,灰色 区域将获得介于最小和最大值之间的值(见下图2)。如果没有提供贴图,着色器将 默认 使用最大光泽度值。这样一来,可以允许艺术家来确定面部更有光泽的部分,比如嘴唇和 T 字区。
color GlossMask = getTextureDif(Gloss_Mask, color(1));
float Spec_sharpness = blendGloss(Gloss_Min, Gloss_Max, GlossMask);
...
float SoftGloss = (1-Spec_bloom * 0.6) * Spec_sharpness;
注意 下面的图片(图3),嘴唇和鼻子看起来光滑和有光泽,因为遮罩是白色的,接收到最大的光泽值,而嘴唇上面的区域是粗糙的,因为遮罩是黑色的,因此接收到最小的光泽值。
双叶瓣高光 设置进一步增加了细节,结合了一个 “锐利” 的高光(由最小/最大光泽度值控制)和一个由 spec bloom
数量控制的 “软” 高光。bloom 越高,规格就越大,所以它类似于 “粗糙度” 参数。bloom 也受到光泽遮罩的影响。通过使用它们的 “数量” 滑块,两个高光叶瓣可以被关闭。
这个着色器 没有 凹凸,这是由于目前在 Vray 上 OSL 的限制(或者换句话来说,Vray 凹凸是相当复杂的,所以在 OSL 中实现它并不简单)。要获得 bump,只需将 OSLMtl
连接到 VrayBumpMtl
即可。
作为给用户的额外提示,拥有一个漂亮的凹凸贴图——包括皮肤毛孔之间的微观细节——对于有效捕捉人类皮肤是必不可少的。注意下面的图片,你可以在高光中看到比皮肤毛孔小得多的细节。双高光叶瓣 是捕捉这个细节所 必需 的。创建这些贴图的一种方法是在建模包中雕刻细节,并提取一个浮点 EXR 置换贴图。然而,在像 Mari 这样的程序中简单地将这些绘制成凹凸贴图通常更容易,通过为毛孔和毛孔之间的微观细节使用不同的层即可。在凹凸贴图中获得这种细节的技巧(与之相对的是置换贴图)是降低 凸起的增量比例(bump delta scale) (从 1 到 0.1),这将产生一个与置换贴图的精细细节相媲美的更清晰的结果。另一种方法是分离微观细节,并将其应用到高光图中。
0.85,0.75,0.65
0.8,0.2,0.1
use local subdivs
” 关闭时,此选项将被忽略。要使用以下代码,将其复制粘贴到 VRayOSLMtl 中
/*
*
* 依赖波长的次表面散射 OSL 材料由 Derek Flood (cc)2016
* 灵感来自 Tony Reynolds 的超阴影树,增加了 two-lobe spec 和可映射光泽度。
*
*
*/
color getTextureDif(string texture_name, color no_texture_default_color) {int channels = -1;if (gettextureinfo(texture_name, "channels", channels)){return texture(texture_name, u, v);}
return no_texture_default_color;
}
float fresnelReflectionFactor(normal Nb, float ior) {float c = abs(dot(I, Nb));float g = ior * ior - 1.0 + c * c;if (g > 0.0) {g = sqrt (g);float A = (g - c) / (g + c);float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);return 0.5 * A * A * (1.0 + B * B);}
return 1.0;
}
float blendGloss(float GlossMin, float GlossMax, color Blender) {
Blender = luminance(Blender);return GlossMin * (1.0 - Blender[0]) + GlossMax * Blender[0];
}
surface DFskinMtl[[ string description = "blended skin material" ]]
(/* 全局区 */string Transparency = "trs.png",float Depth_Scale = 1,float Overall_Brightness = 0.8,
color Diffuse_Color = 0.5,
float Diffuse_Amount = 0.5,
/* SSS 区 */string Subsurface_Color = "color.png",float Subsurface_Amount = 1,string RGB_Scatter_Depth = "RGB.png"
[[ string description =
"Proportional scatter depth for red, green, and blue wavelengths."
]],float Texture_Gamma = 2.2,
/* 高光区 */string Spec_color = "specular.png",string Gloss_Mask = "mask.png"
[[ string description =
"black and white mask for glossiness min/max values."
]],
float Spec_total_amount = 0.5
[[ string description =
"Total amount of sharp and soft spec."
]],float Sharp_Amount = 0.3 [[ string description =
"For the sharp spec lobe."
]],float Bloom_Amount = 0.7 [[ string description =
"For the soft spec lobe."
]],
float Gloss_Min = 0.6
[[ string description =
"Glossiness value for the black parts of the Gloss Mask map."
]],float Gloss_Max = 0.8
[[ string description =
"Glossiness value for the white parts of the Gloss Mask map."
]],float Spec_bloom = 0.3 [[ string description =
"For the soft spec lobe (Blinn). Controls tail size of spec."
]], float Reflection_IOR = 1.5,int Reflection_Subdivs = 8,
int Trace_Sharp_Reflections = 1[[ string widget = "checkBox" ]],
int Trace_Bloom_Reflections = 1[[ string widget = "checkBox" ]],
int Use_Single_SSS = 0[[ string widget = "checkBox" ]],
output color result = 1
)
{/* 定义 Bump */normal Nb = N;/* 声明 SSS 变量并读取纹理贴图 */color TrsColor = getTextureDif(Transparency, color(0));color Opacity = 1-TrsColor;
color Depth = getTextureDif(RGB_Scatter_Depth, color(0.8,0.2,0.1));Depth = clamp(Depth,0.001,1); // 防止深度值为零,这会破坏着色器 RGB 混合color SubColor = getTextureDif(Subsurface_Color, color(0.85,0.75,0.65));SubColor = pow(SubColor, Texture_Gamma); // gamma 校正纹理
SubColor *= Overall_Brightness;color SubR = SubColor * color(1,0,0);color SubG = SubColor * color(0,1,0);color SubB = SubColor * color(0,0,1);
/* 定义高光 */float IOR = 1.38;float PhaseFunction = 0.8;int Subdivs = 8;
color SpecColor = getTextureDif(Spec_color, color(1,1,1));color GlossMask = getTextureDif(Gloss_Mask, color(1));float Spec_sharpness = blendGloss(Gloss_Min, Gloss_Max, GlossMask);color Fresnel = SpecColor * fresnelReflectionFactor(Nb, Reflection_IOR);// Blinn 的菲涅尔float SoftGloss = (1-Spec_bloom * 0.6) * Spec_sharpness; float Tail = 2.0;float Roughness = 0;float Anisotropy = 0;float Aniso_rotation = 0;/* 闭包区 */closure color Diff = Diffuse_Color * Diffuse_Amount * Overall_Brightness *
diffuse(Nb, "roughness", 0);
closure color SSS = vray_subsurface ( IOR, PhaseFunction, Depth * Depth_Scale * 0.5, SubColor * Subsurface_Amount, "subdivs", Subdivs);
closure color SkinR = vray_subsurface ( IOR, PhaseFunction, SubR * Depth[0] * Depth_Scale, SubR * Subsurface_Amount, "subdivs", Subdivs); closure color SkinGB = vray_subsurface ( IOR, PhaseFunction, ((SubG * Depth[1]) + (SubB * Depth[2])) * Depth_Scale, (SubG + SubB) * Subsurface_Amount, "subdivs", Subdivs);
closure color spcHard =Sharp_Amount * SpecColor * Spec_total_amount * 0.5 *microfacet_ggx (Nb, Roughness, Reflection_IOR,"gtr_gamma", Tail,"subdivs", Reflection_Subdivs, "reflection_glossiness", Spec_sharpness,"highlight_glossiness", Spec_sharpness,"trace_reflections", Trace_Sharp_Reflections);
closure color spcSoft = Bloom_Amount * Fresnel * Spec_total_amount * 2 *vray_blinn (Nb, SoftGloss, Anisotropy, Aniso_rotation,"subdivs", Reflection_Subdivs, "trace_reflections", Trace_Bloom_Reflections); if (Use_Single_SSS)
{ Ci = Opacity * Diff + SSS + spcHard + spcSoft + (1.0 - Opacity) * transparent(); }else
{ Ci = Opacity * Diff + SkinR + SkinGB + spcHard + spcSoft + (1.0 - Opacity) * transparent(); }
}
最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享