白纸一张

三十而立,四十而不惑

0%

练习项目(十一):次表面散射的近似实现

概述

实时图形中的很多着色模型只考虑光在物体表面的相互作用。但现实世界中,很多物体是半透明的,光射进表面,在材质里散射,然后从与入射点不同的地方退出表面。本文将介绍几种方法,近似实现次表面散射的效果。

一、URP中的多光源计算

在Build in渲染管线中,要计算多个光源的贡献,一般的方式是使用两个Pass。可以参考《Unity Shader入门精要》第九章。

但是在URP中,不需要多个Pass,可以在一个Pass中计算。参考URP内置的Lit.shader资源,可以发现计算多光源的方式。

1
2
3
4
5
6
7
8
#ifdef _ADDITIONAL_LIGHTS
uint pixelLightCount = GetAdditionalLightsCount();
for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
{
Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
color += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS);
}
#endif

主要的思路:使用GetAdditionalLightsCount()方法获得额外的光源数量,然后遍历这些光源;使用GetAdditionalLight()方法获得指定序号的光源信息。然后计算相应光源的贡献,叠加到主光源的贡献上。

二、环绕光照

使用经典的兰伯特定律计算漫反射时,使用的公式如下: \[ c_{diffuse} = (c_{light}*m_{diffuse})max(0,n \cdot l) \] 观察上式可以发现,当表面法线和光线的方向垂直时,漫反射提供的照明度为0。

环绕光照对上述公式进行一定的修改,使得光照环绕在物体的周围。这样,那些原本暗色的地方也会有亮度。

下面的代码,显示了如何改变漫反射公式:

1
2
half diffuse = max(0, dot(N, L));
half wrap_diffuse = max(0, (dot(N, L) + wrap) / (1 + wrap));

其中,环绕值wrap是范围为\([0,1]\)的浮点数,控制光照环绕物体的距离。如下图所示:

当wrap为0时,就是标准的兰伯特反射。观察与y轴的交点,原本照明度为0的情况,随着wrap的变化,交点也在变化,表明照明度不为0了。

这里需要用到多光源的计算,参考第一部分的内容。

效果如下:

代码如下

三、快速次表面散射

快速次表面散射的实现,主要是根据《Approximating Translucency for a Fast, Cheap and Convincing Subsurface Scattering Look》实现的。

主要的思路是:对于不透明材质,光的贡献直接来自光源,相对于光的方向倾斜90度以上的顶点不接受光照(如下左图);根据演示文稿中提出的模型,半透明材质具有额外的光源贡献,\(-L\)。从几何上来看,\(-L\)可以看作是某些光线实际穿过了材质并到达了另一侧(如下右图)。

这样,最终的颜色包含两部分,第一部分是正常的照明,另一部分是来自虚拟光源的照明。

计算虚拟光源照明的公式如下: \[ I_{back} = sarutate(V \cdot -<L + N\delta>)^p*s \] 其中:

  • L是光源的方向;
  • V是视线的方向;
  • N是法线的方向;
  • \(\delta\)是次表面变形参数,该参数迫使向量\(-L\)指向\(N\)
  • p(功率)和s(比例)用来改变曲线的属性

最终的效果如下所示:

代码如下

四、总结

本文介绍了两种对次表面散射的近似方法,只是一种近似方法,可能不太适合真实物体渲染。如果需要更详细的介绍,可以参考下面的参考内容。

参考

-------------本文结束感谢您的阅读-------------