原文链接
版权声明:本文为CSDN博主「zengjunjie59」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
正文
用于计算光照衰减系数
- 参数一 为返回值(光照衰减系数)
- 参数二 用于阴影计算
- 参数三 是世界坐标
实现代码位于 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_CHANNEL 是 r 或者是 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;
}