Lighting in 1 pass in a shader

So… I have a shader that reacts to light in a non-additive way, and since passes in forward rendering are additively blended with each other, I have to find a way to only render the shader once (in forward mode). I can make it compile a black version of the shader for any passes but base, which works, but that seems like a waste of resources.

On top of that, afaik I should expect 1 directional light, and up to 4 lights rendered as vertex lights in that pass, right?
But it also seems that a point light works. THere is no attenuation though. What options do I have here?

It took 3 shaders and alot of reading before I realised these limitations to the rendering pipeline, so I would just like to know my options, and what I’m forgetting or missing here.

I either get only directional light support, no point, or I get the problem with I have no way of including more lights (even vertex ones, oh, and my frag shader just got commented out :frowning: )

Right now it works fine with 1 light, point or directional, and that’s it, no shadows either it seems.

Shader "Custom/LineTest3" {
	Properties {
    _MainTex ("Texture", 2D) = "white" { }
   	_Color ("Main Color", Color) = (0,0,0,0)
	_LineTex ("Line (RGB)", 2D) = "white" { }
	_EdgeSoftness ("Softness of shaded edge", Range (0.1, 0.5)) = 0.1
	_WrapAround ("Warp Lighting around Object", Range(0.5, 1.0)) = 1.0
	}
SubShader {
		Tags {"RenderType"="Opaque"}
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Sharpline
		#include "UnityCG.cginc"
		
		
		sampler2D _MainTex;
		sampler2D _LineTex;
		half _EdgeSoftness;
		float _WrapAround;
		float _Color;
		
		float LineValue;

		struct Input {
			float2 uv_MainTex;
			float2 uv_LineTex;
		};
		
		

	
		

		half4 LightingSharpline (SurfaceOutput s, half3 lightDir, half atten) {
			
			half4 c;
			c.rgba = 0;
			#ifndef UNITY_PASS_FORWARDADD
			float NdotL = dot(s.Normal, lightDir);
			NdotL = NdotL * _WrapAround + (1 - _WrapAround);
			c.rgb = (1 - saturate((LineValue - (NdotL * atten)) / _EdgeSoftness)) * s.Albedo;
			c.a = 1;
			#endif
			return c;
		}

		void surf (Input IN, inout SurfaceOutput o) {

			o.Albedo = tex2D(_MainTex, IN.uv_MainTex);
			LineValue = tex2D(_LineTex, IN.uv_LineTex);
			
		}
		ENDCG
	}

}

The easiest way would be to compile the shader with #pragma debug. Then, in the compiled source, find the ForwardBase and ForwardAdd passes and copy them into a new shader. You can then use blending commands on the ForwardAdd pass just like with any other pass to achieve multiplicative or any other blending with the previous passes.

I am not quite sure what you mean, exactly… I get the part with copying out the passes, but the whole blending part?
Know anywhere where I can see an example of what you describe?
And thankyou for the help.

You want to get something like this:

Shader "Custom/Bumped Diffuse" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BumpMap ("Normalmap", 2D) = "bump" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 300
		Pass {
            Tags { "LightMode"="ForwardBase" }

		    CGPROGRAM
			   << copied source >>
		    ENDCG
		}
		
		Pass {
			Tags { "LightMode" = "ForwardAdd" }
			ZWrite Off Blend One One Fog { Color (0,0,0,0) }
            << add the blend command here... for example "Blend DstColor Zero" for multiplicative blending >>
			
			CGPROGRAM
			    << copied source >>
			ENDCG
		}
	}
	FallBack "Bumped Diffuse"
}

Then you can add the blend command inside the ForwardAdd pass: Unity - Manual: ShaderLab command: Blend

Cool, didn’t know you could do that.
I’ll look into that, thanks :slight_smile:

I just realised now, after playing with the suggestion, that my shader won’t do what it is suppose to by blending after doing it’s kind-of-special shading.
Unless I can somehow control what the last pass is. The problem is that the diffuse texture should never be “overlit”, it should never get brighter “than 1”.
So this brings a number of questions, which I hope you guys won’t mind helping me with, because I can’t seem to find an answer to them.

Can I somehow make a shader with passes that I add with the above method, in which I write frag and vert programs like in “the good ol days” without getting it commented out? And can I somehow use this to draw a pass for each light, and then a final pass afterwards?

What are my options with one pass, and can I somehow force Unity to only draw 1 pass per object?
What I mean is, my effect should have plenty of “fidelity” even when done as per-vertex-calculated shading, so can I actually somehow get support for 1 pixel and 4 vertex lights? And maybe even 1 point light with attenuation?

And lastly, would it be a bad idea to try and make it a vertex shader?