Implement Unity Forward multi-light source real-time shadow Shader

First look at the effect of the shadow:

Here the lighting model only uses diffuse reflection for simplicity

Use the following macros to sample parallel light shadows in forwardbase:

SHADOW_COORDS(1)  // 接受shadowmap的uv索引值
TRANSFER_SHADOW(o); // 根据光的范围进行缩放和再计算uv值
SHADOW_ATTENUATION(i); // 采样阴影贴图

In forwardadd, due to the different types of light sources (different light source attenuation values) and the need to calculate shadows at the same time, Unity has a built-in macro that can do these two things:

LIGHTING_COORDS(5, 6) // 定义光照衰减的uv 和 shadowmap 的uv
TRANSFER_VERTEX_TO_FRAGMENT(o); // 计算 shadowmap uv 和 light 衰减 uv
half attenuation = LIGHT_ATTENUATION(i); // 可以使用宏直接计算衰减*阴影的结果

These macros need to use the world coordinate information of the object, so for a2v, there must be a specified name in the v2f structure:

v2f vert(a2v v) {
    
    
    v2f o; // 这里必须是o
    o.pos = UnityObjectToClipPos(v.vertex);		 // 两个结构体中必须有o.pos 和 v.vertex	
    o.worldNormal = UnityObjectToWorldNormal(v.normal);				
    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

    TRANSFER_SHADOW(o); // 宏-阴影uv计算 
    return o;
}

Since the lighting of multiple light sources needs to be processed in forwardadd, if you need to display the shadows of multiple light sources, you need to define the following commands:

 #pragma multi_compile_fwdadd_fullshadows
// multi_compile_fwdadd_fullshadows 多光照阴影(可以物体可接受多个光照的阴影)
// multi_compile_fwdadd 只能接受一个,如果定义这个则不会传递其他光照的阴影贴图进来

If you need to customize the attenuation value, you can also use the macro to distinguish the type of light source to customize the attenuation value of different light sources:

//【不同光源的光照方向】
#ifdef USING_DIRECTIONAL_LIGHT
  fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
  fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
fixed3 diffuse = _LightColor0.rgb * max(0, dot(worldNormal, worldLightDir));

// 不同光源的衰减值
#ifdef USING_DIRECTIONAL_LIGHT // 平行光
  fixed atten = 1.0;
#else
  #if defined (POINT)    // 点光灯
  float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
  fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
  #elif defined (SPOT) // 聚光灯
  float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
  fixed atten = (lightCoord.z > 0) 
    * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w 
    * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
  #else
  fixed atten = 1.0;
  #endif
#endif

float shadow = SHADOW_ATTENUATION(i); // 采样阴影贴图

Complete code
ForwardBase Pass

 Pass
{
    
    
    Tags {
    
     "LightMode"="ForwardBase" } //计算平行光
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma multi_compile_fwdbase // forward base 光照

    #include "UnityCG.cginc"
    #include "AutoLight.cginc"

    struct appdata
    {
    
    
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };

    struct v2f
    {
    
    
        float4 pos : SV_POSITION; // 这个坐标名称一定要是 pos, TRANSFER_SHADOW会使用
        float3 pos_world : TEXCOORD3;
        SHADOW_COORDS(1) // 使用TEXCOORD1 存放
        float3 normal_world : TEXCOORD2; 
    };

    v2f vert(appdata v)
    {
    
    
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.pos_world = mul(unity_ObjectToWorld, v.vertex).xyz;
        o.normal_world = UnityObjectToWorldNormal(v.normal);
        TRANSFER_SHADOW(o);
        return o;
    }
    
    float4 _LightColor0; // unity内置变量 - lightcolor

    half4 frag(v2f i) : SV_Target
    {
    
    
        i.normal_world = normalize(i.normal_world);
        float3 light_dir = normalize(UnityWorldSpaceLightDir(i.pos.xyz));
        
        float3 diffuse = max(0., dot(i.normal_world, light_dir)) * _LightColor0.rgb;
        half shadow = SHADOW_ATTENUATION(i);
        float3 final_col = diffuse * shadow;
        return half4(final_col, 1.0f);
    }
    ENDCG
}

ForwardAdd Pass

Pass
{
    
    
    Tags {
    
     "LightMode"="ForwardAdd" } 
    Blend One One
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma multi_compile_fwdadd_fullshadows // 多个阴影

    #include "UnityCG.cginc"
    #include "AutoLight.cginc"

    struct appdata
    {
    
    
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };

    struct v2f
    {
    
    
        float4 pos : SV_POSITION; // 这个坐标名称一定要是 pos, TRANSFER_SHADOW会使用
        float3 pos_world : TEXCOORD3; 
        LIGHTING_COORDS(5, 6) // 定义光照衰减的uv 和 shadowmap 的uv
        float3 normal_world : TEXCOORD2;
    };

    v2f vert(appdata v)
    {
    
    
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.pos_world = mul(unity_ObjectToWorld, v.vertex).xyz;
        o.normal_world = UnityObjectToWorldNormal(v.normal);
        TRANSFER_VERTEX_TO_FRAGMENT(o); // 计算 shadowmap uv 和 light 衰减 uv
        return o;
    }
     
    float4 _LightColor0; // unity内置变量 - lightcolor

    half4 frag(v2f i) : SV_Target
    {
    
    
      	// v2f中定义了 _LightCoord 可以使用宏直接计算 衰减值*阴影值
        half attenuation = LIGHT_ATTENUATION(i); 
        fixed3 light_dir = normalize(UnityWorldSpaceLightDir(i.pos_world));
        fixed3 normal_dir = normalize(i.normal_world);

        half3 diffuse = max(0., dot(normal_dir, light_dir)) * _LightColor0.rgb * attenuation;
        half3 final_color = diffuse * attenuation;
        return half4(final_color, 1.0f);
    }
    ENDCG
}

Shadow Caster Pass

Fallback "Diffuse"

Guess you like

Origin blog.csdn.net/qq_41709801/article/details/127876838