如何为现有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内置的漫反射光照逻辑整合进去,同时保留原有的模板遮罩功能。下面是具体的修改步骤和完整代码:
关键修改点解析
移除光照禁用指令
原Shader里的Lighting Off直接关闭了光照计算,删掉这一行,Shader才能接收场景中的光源数据。引入光照计算工具库
在CG代码块里添加#include "UnityLightingCommon.cginc",这个库提供了现成的Lambert光照计算函数和光源变量,不用自己从头写光照公式。添加法线与世界空间数据传递
光照计算需要用到顶点法线和世界空间位置,所以要修改顶点输入输出结构:- 给
appdata_t加上float4 normal : NORMAL;来接收模型法线 - 给
v2f加上float3 worldPos : TEXCOORD1;和half3 worldNormal : TEXCOORD2;,用来传递转换后的世界空间数据
- 给
顶点着色器中转换空间
在vert函数里,把顶点位置和法线从模型空间转成世界空间:OUT.worldPos = mul(unity_ObjectToWorld, IN.vertex).xyz; OUT.worldNormal = UnityObjectToWorldNormal(IN.normal);片段着色器中计算光照
用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
相关产品推荐
相关产品推荐

