【转载】UNITY_LIGHT_ATTENUATION 光照衰减 解析

902 阅读3分钟

原文链接

版权声明:本文为CSDN博主「zengjunjie59」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:blog.csdn.net/zengjunjie5…

正文

用于计算光照衰减系数

  • 参数一 为返回值(光照衰减系数)
  • 参数二 用于阴影计算
  • 参数三 是世界坐标

实现代码位于 AutoLight 导入文件之中,使用之前需要 #include "AutoLight.cginc"

使用这个宏后,看起来衰减不再工作了。这是因为它有多个版本,每个光源类型都有一个。在默认情况下,它是为 方向光源 准备的版本,根本没有衰减。

正确的宏只有在知道我们正在处理的光源类型的时候才会被定义。如果处理 点光源,我们必须在 include AutoLight.cginc 之前 #define POINT

如果需要适应各种类型的光照,可以使用 #pragma multi_compile DIRECTIONAL POINT SPOT 创建多个变体,Unity 会根据光源类型启用不同的关键字

处理点光源时候的源码

代码来自 Unity-Built-in-Shaders/blob/master/CGIncludes/AutoLight.cginc

#ifdef POINT
sampler2D_float _LightTexture0;
unityShadowCoord4x4 unity_WorldToLight;
#   define UNITY_LIGHT_ATTENUATION(destName, input, worldPos) \
        unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)).xyz; \
        fixed shadow = UNITY_SHADOW_ATTENUATION(input, worldPos); \
        fixed destName = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r * shadow;
#endif

_LightTexture0

通过纹理实现光照衰减

Unity 在内部使用一张名为 _LightTexture0 的纹理来计算光源衰减。我们通常只关心 _LightTexture0 对角线上的纹理颜色值,这些值表明了在光源空间中不同位置的点的衰减值。

例如 (0,0) 点表明了与光源位置重合的点的衰减值,而 (1,1) 点表明了在光源空间中所关心的距离最远的点的衰减。

具体应用

float3 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)).xyz;  
fixed atten = tex2D(_LightTexture0, dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL; 

现将世界坐标与 _LightMatrix0 相乘得到在光源空间中的位置,用光源空间 中顶点 距离的平方 来对纹理采样,然后,使用宏 UINITY_ATTEN_CHANNEL 来得到衰减纹理中的衰减值所在的分量,以得到最终的衰减值。

通过数学公式计算光照衰减

例如,下面的代码可以计算光源的线性衰减:

float distance = length(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);  
atten = 1.0/distance; 

unity_WorldToLight

用于把世界空间下的坐标变换到光源空间下。

例:

float3 lightCoord = mul(unity_WorldToLight, float4(i.worldpos, 1)).xyz;           //变换顶点位置到光源空间

lightCoord 是顶点在 光源空间 下的坐标。用该坐标从 _LightTexture0 里提取衰减值,而 UNITY_ATTEN_CHANNELr 或者是 a,这具体取决于目标平台。最后乘以阴影系数得到最后的光源衰减系数

SHADOW_ATTENUATION

SHADOW_ATTENUATION(input) 与接受阴影有关,解析

处理聚光灯时候的源码

这里是它的代码。

注意 在采样纹理的时候所做的从 齐次坐标 到 欧几里得坐标 的转换。在转换之后向 uv 坐标加上 0.5,来确保 (0,0) 点在纹理坐标中央。

代码来自 Unity-Built-in-Shaders/blob/master/CGIncludes/AutoLight.cginc

#ifdef SPOT
sampler2D_float _LightTexture0;
unityShadowCoord4x4 unity_WorldToLight;
sampler2D_float _LightTextureB0;
inline fixed UnitySpotCookie(unityShadowCoord4 LightCoord)
{
    return tex2D(_LightTexture0, LightCoord.xy / LightCoord.w + 0.5).w;
}
inline fixed UnitySpotAttenuate(unityShadowCoord3 LightCoord)
{
    return tex2D(_LightTextureB0, dot(LightCoord, LightCoord).xx).r;
}
#if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)
#define DECLARE_LIGHT_COORD(input, worldPos) unityShadowCoord4 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1))
#else
#define DECLARE_LIGHT_COORD(input, worldPos) unityShadowCoord4 lightCoord = input._LightCoord
#endif
#   define UNITY_LIGHT_ATTENUATION(destName, input, worldPos) \
        DECLARE_LIGHT_COORD(input, worldPos); \
        fixed shadow = UNITY_SHADOW_ATTENUATION(input, worldPos); \
        fixed destName = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow;
#endif

处理点光源 cookie 时候的源码

在这种情况下,UNITY_LIGHT_ATTENUATION 是什么样子的?

它相当于常规 点光源 的宏,除了它也采样 cookie 以外。由于在这种情况下 cookie 是一个 立方体贴图,所以它使用 texCUBE 做采样。

代码来自 Unity-Built-in-Shaders/blob/master/CGIncludes/AutoLight.cginc

#ifdef POINT_COOKIE
samplerCUBE_float _LightTexture0;
unityShadowCoord4x4 unity_WorldToLight;
sampler2D_float _LightTextureB0;
#   if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)
#       define DECLARE_LIGHT_COORD(input, worldPos) unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)).xyz
#   else
#       define DECLARE_LIGHT_COORD(input, worldPos) unityShadowCoord3 lightCoord = input._LightCoord
#   endif
#   define UNITY_LIGHT_ATTENUATION(destName, input, worldPos) \
        DECLARE_LIGHT_COORD(input, worldPos); \
        fixed shadow = UNITY_SHADOW_ATTENUATION(input, worldPos); \
        fixed destName = tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).r * texCUBE(_LightTexture0, lightCoord).w * shadow;
#endif

案例(转自 catlikecoding)

UnityLight CreateLight (Interpolators i){

    UnityLight light;

    light.dir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos);
    // float3 lightVec = WorldSpaceLightPos0.xyz - i.worldPos;
    // float attenuation = 1 / (dot(lightVec, lightVec));
    UNITY_LIGHT_ATTENUATION(attenuation, 0, i.worldPos);
    light.color = _LightColor0,rgb * attenuation;
    light.ndotl = DotClamped(i.normal, light.dir);
    return light;
}