实现Unity Forward 多光源实时阴影Shader

先看看阴影的效果:
实现Unity Forward 多光源实时阴影Shader_第1张图片

这里光照模型为了简单只使用了漫反射

在forwardbase中使用如下宏采样平行光阴影:

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

在forwardadd由于光源类型不同(光源衰减值不同)同时又需要计算阴影,Unity内置了一个宏可以完成这两件事:

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

这些宏需要使用到物体的世界坐标信息,因此对于a2v,v2f结构体中必须有指定的名称:

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;
}

由于forwardadd中需要处理多个光源的光照,如果需要显示多个光源的阴影需要定义如下的命令:

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

如果需要自定义衰减值也可以自己通过宏来区分光源的类型,来自定义对不同光源的衰减值进行处理:

//【不同光源的光照方向】
#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); // 采样阴影贴图

完整代码
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"

你可能感兴趣的:(游戏开发,unity,游戏引擎)