Really weird issue when blending normal (compile time issue!)

Hey there,
I’m having a really weird issue trying to blend 2 normals in a surface shader, as soon as i assign the result to the normal property of my output:

This works:

fixed3 normal0 = UnpackNormal(tex2D(_TexBump1, IN.uv_Tex1));
fixed3 normal1 = UnpackNormal(tex2D(_TexBump2, IN.uv_Tex2));
o.Normal = normal0;
o.Normal += normal0;

This works too:

fixed3 normal0 = UnpackNormal(tex2D(_TexBump1, IN.uv_Tex1));
fixed3 normal1 = UnpackNormal(tex2D(_TexBump2, IN.uv_Tex2));
o.Normal = normal1;
o.Normal += normal1;

This doesn’t work, and actually produces a compile time error!

fixed3 normal0 = UnpackNormal(tex2D(_TexBump1, IN.uv_Tex1));
fixed3 normal1 = UnpackNormal(tex2D(_TexBump2, IN.uv_Tex2));
o.Normal = normal0;
o.Normal += normal1;

The error is as follow:

  • Target level displays as SM4.0 (it correctly displays as 5.0 in the other 2 cases)
  • I get “program frag_surf , variable ‘surfIN’ used without having been initialized”
  • as well as "program frag_surf , ‘distance’ : implicit truncation of . . .

None of those errors seem related at all to those 2 lines but the shader goes from rendering just fine to not working and having compile errors just from changing that, add normal0 to itself, works , add normal 1 to itself, works, add both together and the world crashes in other unrelated places!

Any help is most welcome.

This might be of use to you.

http://blog.selfshadow.com/publications/blending-in-detail/

Also, it might be worth setting up a temporary normal value and then assigning that to o.Normal at the end…

normal0 = blah
normal1 = blah
normal = normal0 + normal1
o.Normal = normal

I tried that too, same issue, the normal= normal0+normal1 line works fine, i get the bug as soon as i assign that result to o.Normal, not sure what’s happenning but it doesn’t crash on the normal math but on the assignment to the o.Normal (not worried about the normal math here, gave a simple exemple but i’m sure i must have an issue somewhere to get this bug, i didn’t expect normal blending to work by adding them, but i didn’t expect a compile time error either!)

Just to be clear:

normal0 = blah
normal1 = blah
finalnormal = normal0 + normal1 // no problem here as long as i don’t use “finalnormal”
o.Normal = normal0; // this work
o.Normal = finalnormal // compile time errors i mentioned above

Full code in case this can help as i’m doubting the error is on those lines, i guess i’m just running in a weird bug where the issue is missreported?

Shader "Custom/MultiLevel" {
	Properties {
		_FarTexDist ("Far Texture", Float) = 10
		_MidTexDist ("Mid Texture", Float) = 5
		_MinTexDist ("Min Texture", Float) = 2
		_Tex1 ("Texture 1", 2D) = "white" {} 
		_TexBump1 ("Bump 1", 2D) = "white" {} 
		_Tex2 ("Texture 2", 2D) = "white" {} 
		_TexBump2 ("Bump 2", 2D) = "white" {} 
		_EdgeLength ("Edge length", Range(3,50)) = 10
		_Smoothness ("Smoothness", Range(0,1)) = 0.5
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert vertex:vert 
		#pragma target 5.0
		#include "Tessellation.cginc"

		struct Input {
			float distance;
			float2 uv_Tex1 : TEXCOORD1;
			float2 uv_Tex2 : TEXCOORD1;
		};
		
		struct appdata {
			float4 vertex : POSITION;
			float4 tangent : TANGENT;
			float3 normal : NORMAL;
			float2 texcoord : TEXCOORD0;
			float2 texcoord1 : TEXCOORD1;
		};
		
		float _FarTexDist;
		float _MidTexDist;
		float _MinTexDist;
		sampler2D _Tex1;
		sampler2D _TexBump1;
		sampler2D _Tex2;
		sampler2D _TexBump2;
		float _EdgeLength;
		float _Smoothness;
		

		void vert(inout appdata v,out Input o) 
		{			
			UNITY_INITIALIZE_OUTPUT(Input,o);
			o.distance = distance(_WorldSpaceCameraPos, mul(_Object2World, v.vertex));
		}
		
		
		float3 trilerp(float3 val1, float val1point, float3 val2,float val2point, float3 val3,float val3point, float interpolationPoint)
		{			
			// Find highest value under interpolation
			float3 lowval = val1;
			float  lowvalpoint = val1point;
			
			if(val2point <= interpolationPoint)
			{
				lowval = val2;
				lowvalpoint = val2point;			
			}			
			else if(val3point <= interpolationPoint)
			{
				lowval = val3;
				lowvalpoint = val3point;			
			}		
			
			// Find lowest value over interpolation
			float3 highval = val3;
			float  highvalpoint = val3point;
			
			if(val2point >= interpolationPoint)
			{
				highval = val2;
				highvalpoint = val2point;			
			}			
			else if(val1point >= interpolationPoint)
			{
				highval = val1;
				highvalpoint = val1point;			
			}
			
			float range = highvalpoint - lowvalpoint;			
			float effectiveInterpolationPoint = interpolationPoint - lowvalpoint;			
			return lerp(lowval,highval, effectiveInterpolationPoint/range);
		}
				
		void surf (Input IN, inout SurfaceOutput o) 
		{		
			float actualdist = IN.distance;			
			
			float3 tex1 = float3(1,0,0);
			float3 tex2 = float3(0,1,0);
			float3 tex3 = float3(0,0,1);
			
			o.Albedo = trilerp(tex1,_MinTexDist,tex2,_MidTexDist,tex3,_FarTexDist,actualdist);
			
			fixed3 normal0 = UnpackNormal(tex2D(_TexBump1, IN.uv_Tex1));
			fixed3 normal1 = UnpackNormal(tex2D(_TexBump2, IN.uv_Tex2));
			o.Normal =  normal0; 
			//o.Normal += normal1; // Uncomment to make it crash
			//o.Normal = lerp(normal0,normal1,0.5);
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

Note: i’ve read your link and while it’s not related to my issue i’m pretty happy you posted it, very nicely detailed blending information there, i was going to go for something much simpler but this gave me food for thought thanks!

Hmm, you’re trying to pass the two normal map coords through as the same TEXCOORD semantic and you’ve got no semantic on the distance. Might be the issue?

I think you can leave off the semantics with the Input structure, Unity will handle it for you.

struct Input {
float distance;
float2 uv_Tex1;
float2 uv_Tex2;
};

Omg thank you, silly copy paste error from another one of my shaders and ended up using texcoord1 twice instead of 0 and 1, could’ve looked forever!
Fully fixed now thanks again, probably saved me 2-3 hours of looking elsewhere frenetically :slight_smile:

Hi,

I’ve a similar problem but I really need to keep Tessellation active. I use it for getting more detailed waves near the camera. The problem is that I would like to pass the waves values in the Input structure like I did i SM3, but it return the following error :

Program ‘frag_surf’, variable ‘surfIN’ used without having been completely initialized

I’ve tried “UNITY_INITIALIZE_OUTPUT” wich is useless in that case, I also tried to set COLOR or NORMAL semantic to set the interpolation method but nothing seems to work.

So my question is How the hell can I pass information from vertex to surface shader using tessellation in Unity. Can someone provide us some example please ?

Thanks for providing this excellent link to blog.selfshadow.com :slight_smile: