You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为现有Sprite Shader添加实时光照支持?

如何为现有Sprite Shader添加光照支持(实现Sprites/Diffuse效果)

问题描述

我现在有一个自定义Sprite Shader(代码如下),它的效果和Unity内置的「Sprites/Default」一致,但无法响应场景中的实时光源。我需要将它修改成类似「Sprites/Diffuse」的效果,让Sprite能正确受到光照影响。

当前Shader代码:

Shader "Sprites/Stencil Mask" {
    Properties {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    }

    SubShader {
        Tags {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Fog { Mode Off }
        Blend One OneMinusSrcAlpha

        Pass {
            Stencil {
                Ref 1
                Comp always
                Pass replace
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile DUMMY PIXELSNAP_ON
            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                float4 color : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord : TEXCOORD0;
            };

            fixed4 _Color;

            v2f vert(appdata_t IN) {
                v2f OUT;
                OUT.vertex = UnityObjectToClipPos(IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target {
                fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
                if (c.a<0.1) discard;
                c.rgb *= c.a;
                return c;
            }
            ENDCG
        }
    }
}

解决方案

我来帮你搞定这个需求——要让你的Shader支持光照,其实就是把Unity内置的漫反射光照逻辑整合进去,同时保留原有的模板遮罩功能。下面是具体的修改步骤和完整代码:

关键修改点解析

  1. 移除光照禁用指令
    原Shader里的Lighting Off直接关闭了光照计算,删掉这一行,Shader才能接收场景中的光源数据。

  2. 引入光照计算工具库
    在CG代码块里添加#include "UnityLightingCommon.cginc",这个库提供了现成的Lambert光照计算函数和光源变量,不用自己从头写光照公式。

  3. 添加法线与世界空间数据传递
    光照计算需要用到顶点法线和世界空间位置,所以要修改顶点输入输出结构:

    • 给appdata_t加上float4 normal : NORMAL;来接收模型法线
    • 给v2f加上float3 worldPos : TEXCOORD1;和half3 worldNormal : TEXCOORD2;,用来传递转换后的世界空间数据
  4. 顶点着色器中转换空间
    在vert函数里,把顶点位置和法线从模型空间转成世界空间:

    OUT.worldPos = mul(unity_ObjectToWorld, IN.vertex).xyz;
    OUT.worldNormal = UnityObjectToWorldNormal(IN.normal);
    
  5. 片段着色器中计算光照
    用Unity提供的工具计算漫反射和环境光,再把光照结果叠加到原纹理颜色上:

    • 先归一化法线确保方向正确
    • 计算主光源方向和漫反射强度,避免出现负光照值
    • 加上环境光保证阴影区域不会全黑
    • 最后将光照结果与纹理颜色相乘

修改后的完整Shader代码

Shader "Sprites/Stencil Mask Diffuse" {
    Properties {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    }

    SubShader {
        Tags {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        ZWrite Off
        Fog { Mode Off }
        Blend One OneMinusSrcAlpha

        Pass {
            Stencil {
                Ref 1
                Comp always
                Pass replace
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile DUMMY PIXELSNAP_ON
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc" // 引入光照计算库

            struct appdata_t {
                float4 vertex : POSITION;
                float4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                float4 normal : NORMAL; // 新增:接收模型法线
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord : TEXCOORD0;
                float3 worldPos : TEXCOORD1; // 新增:世界空间顶点位置
                half3 worldNormal : TEXCOORD2; // 新增:世界空间法线
            };

            fixed4 _Color;

            v2f vert(appdata_t IN) {
                v2f OUT;
                OUT.vertex = UnityObjectToClipPos(IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                // 将顶点和法线转换到世界空间
                OUT.worldPos = mul(unity_ObjectToWorld, IN.vertex).xyz;
                OUT.worldNormal = UnityObjectToWorldNormal(IN.normal);
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target {
                fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
                if (c.a < 0.1) discard;

                // 计算漫反射光照
                half3 worldNormal = normalize(IN.worldNormal);
                half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                half nl = max(0, dot(worldNormal, lightDir)); // 避免负的光照值
                half3 diff = nl * _LightColor0.rgb;

                // 计算环境光
                half3 ambient = ShadeSH9(half4(worldNormal, 1));

                // 将光照应用到纹理颜色上
                c.rgb *= (diff + ambient);

                // 保留原有的预乘Alpha处理
                c.rgb *= c.a;
                return c;
            }
            ENDCG
        }
    }
}

额外提示

  • 这个Shader现在会像「Sprites/Diffuse」一样响应主光源和环境光,阴影区域也会自然过渡
  • 如果需要支持多光源,可以在CGPROGRAM块里加上#pragma multi_compile_fwdbase,然后调整光照计算逻辑
  • 原有的模板遮罩功能完全保留,不会影响你之前的遮罩效果

内容的提问来源于stack exchange,提问作者viruseg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:51:05