Alpha Blending and Surface Shader Lighting Models

Hi, I have a surface shader that uses a custom lighting model, but it seems like the alpha calculation from the lighting model function is not used at all, but the color calculation is being used:

Shader "Test/AlphaSpecTest" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Color Map (RGB)", 2D) = "white" {}
		_SpecTex ("Specular Color Map (RGB) Gloss (A)", 2D) = "gray" {}
		_Shininess ("Shininess (Spec. Exponent)", Range (75, 5)) = 32
	}
	
	SubShader {
		Tags{	
			"RenderType" = "Tranparent"
			"Queue" = "Transparent"
			}
			
		CGPROGRAM

		//using custom lighting functions
		#pragma surface surf BlinnPhongColor alpha
		#include "UnityCG.cginc"

		//custom surface output structure
		struct SurfaceOutputSpecColor {
			half3 Albedo;
			half3 Normal;
			half3 Emission;
			half Specular;
			half3 GlossColor; 
			half Alpha;
		};

		sampler2D _MainTex;
		sampler2D _SpecTex;

		float4 _Color;
		float _Shininess;

	  
		//forward lighting function
		inline half4 LightingBlinnPhongColor (SurfaceOutputSpecColor s, half3 lightDir, half3 viewDir, half atten) {
		  half3 h = normalize (lightDir + viewDir);

		  half diff = max (0, dot (s.Normal, lightDir));

		  float nh = max (0, dot (s.Normal, h));
		  float spec = pow (nh, _Shininess);
		  half3 specCol = spec * s.GlossColor;

		  half4 c;
		  c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * specCol * s.Specular) * (atten * 2);
		  c.a = s.Alpha * dot(s.Normal.xyz,viewDir);
		  return c;
		}

		struct Input {
			float2 uv_MainTex;
			float2 uv_SpecTex;
		};
		  
		void surf (Input IN, inout SurfaceOutputSpecColor o) {
			  o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
			  half4 spec = tex2D (_SpecTex, IN.uv_SpecTex);
			  o.GlossColor = spec.rgb;
			  o.Specular = spec.a;
			  o.Alpha = 1.0;
		}
		ENDCG
	}
	Fallback "Diffuse"
}

What am I doing wrong? It seems like no matter what the alpha value is set to in the lighting model function, the alpha value from the surf() function is the one that is always used. The color, however, does come from the lighting model function. Why?

As usual when dealing with Surface Shaders, the easiest is to look at the debug output (just add #pragma debug to your shader and hit “Open compiled shader”).

So basically the alpha keyword that you added to the surface shader definition does a couple of things:

Alphatest Greater 0
ZWrite Off
ColorMask RGB // ← see, no alpha will be spit out to the framebuffer
[…]
Blend SrcAlpha OneMinusSrcAlpha
[…]
c.a = o.Alpha; // ← added at the very end of your fragment program. o.Alpha comes from the surface function, overrides the color returned by the lighting function

What you can easily do is take that shader with debug output, keep only the usual stuff (passes with CGPROGRAMS, etc.) and strip the rest. Make sure it compiles at this point. Now you can remove the following lines to get what you wanted:
ColorMask RGB
c.a = o.Alpha;

But the question is - what do you actually want to achieve? :slight_smile:

I’m having a similar problem (not using any special pragmas). Anything I set in surf, e.g. o.Emission, o.Albedo, ends up being modulated with the output of my custom lighting function.

I just want the output of the lighting function to be the final fragment color, before blending. How can this be accomplished?