Unfortunately, simply setting c.a = spec (or similar) in the lighting model of a surface shader won’t work (in fact, making any change to alpha in the lighting function has no effect, as noted here and here) because the surface shader output always seems to override whatever alpha was set in the lighting function with an additional c.a = o.Alpha; placed at the end of the fragment function.
Instead, you can still write a surface shader that handles only the specular component together with the alpha, something like this:
half4 LightingSpecularOnly (SurfaceOutput 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, s.Specular*128.0);
half4 c;
c.rgb = (_LightColor0.rgb * spec) * (atten * 2);
c.a = spec;
return c;
}
But then also add #pragma debug to generate the underlying vertex/fragment shaders from your code. Then simply copy and paste the generated code and remove the offending final c.a = o.Alpha; and you should be good to go. Here’s a quick test:
EDIT as suggested by @Professor Snake below, you can override the c.a = o.Alpha; by using a finalcolor function. Like this:
Shader "Custom/SpecOnly" {
Properties {
}
SubShader {
Tags { "RenderType"="Transparent" "Queue" = "Transparent"}
LOD 200
CGPROGRAM
#pragma surface surf SpecularOnly finalcolor:alphaFix alpha
struct Input {
half Specular;
};
half4 LightingSpecularOnly (SurfaceOutput 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, s.Specular*128.0);
half4 c;
c.rgb = (_LightColor0.rgb * spec) * (atten * 2);
c.a = spec;
return c;
}
void alphaFix (Input IN, SurfaceOutput o, inout fixed4 color) {
color.a = color.rgb;
}
void surf (Input IN, inout SurfaceOutput o) {
o.Specular = 1;
}
ENDCG
}
}