Shader looks different in scene view. Is it okay and can be used cross-platform?

Hi, I’m learning how to write shaders and for my game I need a simulation of volume on 2D sprite.

I came up with this shader:

Shader "Custom/Basic Toon"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _NormalTex("Normal Sprite", 2D) = "bump" {}
        _Color("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
            [HDR]
        _AmbientColor("Ambient Color", Color) = (0.4,0.4,0.4,1)
    }

        SubShader
        {
            Tags
            {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
                "PreviewType" = "Plane"
                "CanUseSpriteAtlas" = "True"
                "LightMode" = "ForwardBase"
                "PassFlags" = "OnlyDirectional"
            }

            Cull Off
            Lighting On
            ZWrite Off
            Blend One OneMinusSrcAlpha

            Pass
            {
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile _ PIXELSNAP_ON
                #include "UnityCG.cginc"
            #include "Lighting.cginc"

                struct appdata_t
                {
                    float3 texcoord : TEXCOORD0;
                    float3 normal : NORMAL;
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float4 tangent: TANGENT;
                    float2 uv : TEXCOORD1;
                };

                struct v2f
                {
                    float3 texcoord  : TEXCOORD0;
                    half3 tspace0 : TEXCOORD1;
                    half3 tspace1 : TEXCOORD2;
                    half3 tspace2 : TEXCOORD3;
                    float2 uv : TEXCOORD4;
                    float4 vertex   : SV_POSITION;
                    fixed4 color : COLOR;
                };

                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
                    half3 wNormal = UnityObjectToWorldNormal(IN.normal);
                    half3 wTangent = UnityObjectToWorldDir(IN.tangent.xyz);
                    half tangentSign = IN.tangent.w * unity_WorldTransformParams.w;
                    half3 wBitangent = cross(wNormal, wTangent) * tangentSign;
                    OUT.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
                    OUT.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
                    OUT.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
                    OUT.uv = IN.uv;
                    return OUT;
                }

                sampler2D _MainTex;
                sampler2D _AlphaTex;
                float _AlphaSplitEnabled;

                fixed4 SampleSpriteTexture(float2 uv)
                {
                    fixed4 color = tex2D(_MainTex, uv);

    #if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                    if (_AlphaSplitEnabled)
                        color.a = tex2D(_AlphaTex, uv).r;
    #endif

                    return color;
                }

                sampler2D _NormalTex;
                float4 _AmbientColor;

                fixed4 frag(v2f IN) : SV_Target
                {
                    half3 tnormal = UnpackNormal(tex2D(_NormalTex, IN.uv));
                    half3 worldNormal;
                    worldNormal.x = dot(IN.tspace0, tnormal);
                    worldNormal.y = dot(IN.tspace1, tnormal);
                    worldNormal.z = dot(IN.tspace2, tnormal);
                    float3 normal = normalize(worldNormal);
                    float NdotL = dot(_WorldSpaceLightPos0, normal);
                    float lightIntensity = NdotL > 0 ? 1 : 0;
                    float4 light = lightIntensity * _LightColor0;
                    fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;
                    c.rgb *= c.a;
                    return c * (_AmbientColor + light);
                }
            ENDCG   
            }
        }
}

So it looks different in Scene and Game views. Does anybody know how can I fix it? Btw, this is literally the first time I’m trying writing shaders so I know it’s bad and the solution maybe easy, thanks.

Here are the two key lines that are the problem.

float4 light = lightIntensity * _LightColor0;
// ...
return c * (_AmbientColor + light);

The light color is affecting the alpha. Specifically the _AmbientColor & _LightColor0 both have an w component of 1.0, meaning that last line is multiplying the alpha by 2.0, which can do weird things to the resulting blended color value.

What you should be doing is instead:

float3 light = lightIntensity * _LightColor0.rgb;
// ...
return fixed4(c.rgb * (_AmbientColor.rgb + light), c.a);
1 Like

Wow, thank you so much!

I spent a lot of time trying to figure it out and had no idea that was the problem. Now I’ll make sure to keep alpha in 0 to 1 range.

Thank you so much again! Now everything looks perfect : )