UnityApplyLinearShadowBias — Unity中计算阴影bias的方法

作用:

在裁剪空间中的线性增加Z坐标的值

偏移片元在光源空间下的裁剪坐标,用于解决shadow acne(阴影失真)的问题。该偏移作用于光源生成阴影深度图的时候。

原理:

通过减少光源到物体的深度图的数值(距离越远数值越小),从而使光源到物体表面片元的距离数值增加,在比较物体片元到摄像机的距离和到光源的距离时,到光源的距离比真实数值更远一点,通过距离比较之后,把处于边界值的片元都当作被光源照射到。

如下图,如果不启用bias,ad片元能被照射到,而bc片元则不能被照射到。详细说明:https://blog.csdn.net/lawest/article/details/106364935

preview

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float4 UnityApplyLinearShadowBias(float4 clipPos)
{
#if defined(UNITY_REVERSED_Z)
    // We use max/min instead of clamp to ensure proper handling of the rare case
    // where both numerator and denominator are zero and the fraction becomes NaN.
    // Direct3D11-like
    clipPos.z += max(-1, min(unity_LightShadowBias.x / clipPos.w, 0));
    float clamped = min(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
#else
    // Direct3D9-like OpenGL-like
    clipPos.z += saturate(unity_LightShadowBias.x/clipPos.w); //
    float clamped = max(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
#endif
    clipPos.z = lerp(clipPos.z, clamped, unity_LightShadowBias.y);
    return clipPos;
}

源码分析:(本人并不确定,望指教)

clipPos.z += saturate(unity_LightShadowBias.x/clipPos.w);:

clipPos是裁剪空间下的齐次坐标:(N * x, N * y, az + b, -z), 其中x,y,z分别是相机空间下的坐标数值

unity_LightShadowBias.x是一个负数,默认一般是-0.0005,除以clipPos.w也就是除以相机空间下的-z,可以知道为了补偿透视投影,这里除以clipPos.w之后片元距离光源越远偏移量越小。近大远小?至于数学层面的分析本人还不清楚。

float clamped = max(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);:

UNITY_NEAR_CLIP_VALUE是NDC空间下z的最小值。这行代码就是当clipPos.z比近裁切面还要小的时候取近裁切面的值

clipPos.z = lerp(clipPos.z, clamped, unity_LightShadowBias.y);:

unity_LightShadowBias.y, 当光源是平行光的时候该值为1,其他光源为0,当是平行光时最后clipPos直接取clamped的值,这是为了避免在平行光原点背面的片元的深度也保持在0~1,当然平行光并没有原点,这里这是方便计算。当不是平行光的时候该值为0,相当于直接取上面偏移bias之后的clipPos的值。

应用: