此文件还有SSSSTransmittance,但笔者搜索了整个UE的源代码工程,似乎没有被用到,所以暂时不分析。下面只贴出其源码:
//----------------------------------------------------------------------------- // Separable SSS Transmittance Function // @param translucency This parameter allows to control the transmittance effect. Its range should be 0..1. Higher values translate to a stronger effect. // @param sssWidth this parameter should be the same as the 'SSSSBlurPS' one. See below for more details. // @param worldPosition Position in world space. // @param worldNormal Normal in world space. // @param light Light vector: lightWorldPosition - worldPosition. // @param lightViewProjection Regular world to light space matrix. // @param lightFarPlane Far plane distance used in the light projection matrix. float3 SSSSTransmittance(float translucency, float sssWidth, float3 worldPosition, float3 worldNormal, float3 light, float4x4 lightViewProjection, float lightFarPlane) { /** * Calculate the scale of the effect. */ float scale = 8.25 * (1.0 - translucency) / sssWidth; /** * First we shrink the position inwards the surface to avoid artifacts: * (Note that this can be done once for all the lights) */ float4 shrinkedPos = float4(worldPosition - 0.005 * worldNormal, 1.0); /** * Now we calculate the thickness from the light point of view: */ float4 shadowPosition = SSSSMul(shrinkedPos, lightViewProjection); float d1 = SSSSSampleShadowmap(shadowPosition.xy / shadowPosition.w).r; // 'd1' has a range of 0..1 float d2 = shadowPosition.z; // 'd2' has a range of 0..'lightFarPlane' d1 *= lightFarPlane; // So we scale 'd1' accordingly: float d = scale * abs(d1 - d2); /** * Armed with the thickness, we can now calculate the color by means of the * precalculated transmittance profile. * (It can be precomputed into a texture, for maximum performance): */ float dd = -d * d; float3 profile = float3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + float3(0.1, 0.336, 0.344) * exp(dd / 0.0484) + float3(0.118, 0.198, 0.0) * exp(dd / 0.187) + float3(0.113, 0.007, 0.007) * exp(dd / 0.567) + float3(0.358, 0.004, 0.0) * exp(dd / 1.99) + float3(0.078, 0.0, 0.0) * exp(dd / 7.41); /** * Using the profile, we finally approximate the transmitted lighting from * the back of the object: */ return profile * saturate(0.3 + dot(light, -worldNormal)); } 2.3.2 SeparableSSS.cppSeparableSSS.cpp主题提供了扩散剖面、透射剖面、高斯模糊计算以及镜像卷积核的预计算。
为了更好地理解源代码,还是先介绍一些前提知识。
2.3.2.1 高斯和的扩散剖面(Sum-of-Gaussians Diffusion Profile)扩散剖面的模拟可由若干个高斯和函数进行模拟,其中高斯函数的公式:
\[
f_{gaussian} = e^{-r^2}
\]
下图是单个高斯和的扩散剖面曲线图:
由此可见R、G、B的扩散距离不一样,并且单个高斯函数无法精确模拟出复杂的人类皮肤扩散剖面。
实践表明多个高斯分布在一起可以对扩散剖面提供极好的近似。并且高斯函数是独特的,因为它们同时是可分离的和径向对称的,并且它们可以相互卷积来产生新的高斯函数。
对于每个扩散分布\(R(r)\),我们找到具有权重\(\omega_i\)和方差\(v_i\)的\(k\)个高斯函数:
\[
R(r) \approx \sum_{i=1}^k\omega_iG(v_i,r)
\]
并且高斯函数的方差\(v\)有以下定义:
\[
G(v, r) := \frac{1}{2\pi v} e^{\frac{-r^2}{2v}}
\]
可以选择常数\(\frac{1}{2v}\)使得\(G(v, r)\)在用于径向2D模糊时不会使输入图像变暗或变亮(其具有单位脉冲响应(unit impulse response))。