Casting a float3 to fixed3 doesn't work in a shader

Just updated my project to Unity 4 and have run into buckets of shader errors. I’m slowly getting through them, but this one is seriously stumping me:

(Obviously an extremely simplified version of the shader just to show the part with the issue.)

struct Input {
    float4 color : COLOR; // vertex color
};

struct SurfOutput {
    fixed3 VertexColor;
};

void surf (Input IN, inout SurfOutput o) {
  o.VertexColor = IN.color.rgb; // The offending line
}

I understand what it’s saying but it doesn’t make any sense at all because the following things I’ve tried do not fix it:

//
o.VertexColor = fixed3(IN.color.rgb); // Still fails
o.VertexColor = fixed3(IN.color.r, IN.color.g, IN.color.b); // Still fails
// Even changing SurfOutput.VertexColor to a float3 doesn't change a thing.
fixed r = IN.color.r;
fixed g = IN.color.g;
fixed b = IN.color.b;
o.VertexColor = fixed3(r, g, b); // Still fails

// The only thing that works:
o.VertexColor = normalize(IN.color.rgb);

There’s no reason I can imagine that I should not be allowed to cast from float3 to fixed3, and certainly if I rebuild the fixed3 using the r, g, and b components individually. I do not like calling normalize on it just to get it to cast.

Is this some bug in Unity rewriting my code behind the scenes?

Edit:

Ironically, THIS works:

//
o.VertexColor = float3(1, 2, 3); // and notice its a float, not even a fixed

So it looks like there’s something wrong with IN.color.rgb… This is bringing back memories of struggling against Unity’s surface shaders in the past on version 3.x. I have a feeling there’s something else underlying this problem and Unity is just not giving the the right error… Possibly too many registers…

Apparently, for some reason a fixed3 isn’t enough to initialize the whole input struct. Try using

UNITY_INITIALIZE_OUTPUT(SurfOutput,o);

or making VertexColor a fixed4

struct SurfOutput {
    fixed4 VertexColor;
};

Also, in future, it is worth mentioning that you’re running the editor in DX11 mode.

[Edit] If you want more help, please provide an example shader that actually compiles, showing the error message you’re asking about.

Thanks for the help! I just upgraded the project to Unity 4 so I wasn’t quite aware it automatically switched to DX11 mode, but I did see that mentioned that the truncation error is a common issue in DX11, so I should have mentioned it.

UNITY_INITIALIZE_OUTPUT didn’t help, it gave the same errors. I’m actually not worried about the 2nd error (“not completely initialized”) because this error is only a result of the 1st error causing it to not fill the VertexColor field. If I rem out the line with assigning the VertexColor, there are no errors.

Also, converting VertexColor to a float 4 just caused more errors unless I rem out the VertexColor assignment line and then I get zero errors.

EDIT: Here is the updated 1-file shader to test with

Shader "BakedVertexLighting/Bumped Specular Alpha Cutout" {

	Properties {
		_Color ("Main Color", Color) = (1,1,1,1) // Required for alpha shadows
	    _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
	    _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
	    _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
	    _BumpMap ("BumpMap (RGB)", 2D) = "bump" {}
		_SpecMap ("Specular Map (RGB)", 2D) = "white" {}
		_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
	}
	 
	SubShader {
		Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
		LOD 400
		
		// DEFERRED SURFACE SHADER
		CGPROGRAM
		#pragma target 3.0
		#pragma surface surf BlinnPhong_AlphaTestGlowFix_BakedVertexLighting nolightmap nodirlightmap noambient alphatest:_Cutoff exclude_path:forward vertex:vert

		fixed4 _Color;
		sampler2D _MainTex;
		sampler2D _BumpMap;
		sampler2D _SpecMap;
		half _Shininess;
		
        struct SurfaceOutputVtxC {
            fixed3 Albedo;
            fixed3 Normal;
            fixed3 Emission;
            half Specular;
            fixed Gloss;
            fixed Alpha;
            fixed3 VertexColor;
        };
        
        struct Input {
		  	float2 uv_MainTex;
		  	float2 uv_BumpMap;
		  	float4 color : COLOR; // vertex color
		};
	
        inline fixed4 LightingBlinnPhong_AlphaTestGlowFix_BakedVertexLighting (SurfaceOutputVtxC s, fixed3 lightDir, half3 viewDir, fixed atten) {
            // Forward lighting model
            half3 h = normalize (lightDir + viewDir);
            fixed diff = max (0, dot (s.Normal, lightDir));
            float nh = max (0, dot (s.Normal, h));
            float spec = pow (nh, s.Specular*128.0) * s.Gloss;
            fixed4 c;
            c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec * 2.0f) * (atten * 2);
            c.a = 0; // No specular glow
            return c;
        }
		
        inline fixed4 LightingBlinnPhong_AlphaTestGlowFix_BakedVertexLighting_PrePass (SurfaceOutputVtxC s, half4 light) {
            // Deferred lighting model
            fixed spec = light.a * s.Gloss;
            fixed4 c;
            light.rgb += s.VertexColor;
            c.rgb = s.Albedo * light.rgb + light.rgb * _SpecColor.rgb * spec * 2.0f;
            c.a = 0; // No specular glow
            return c;
        }

		void surf (Input IN, inout SurfaceOutputVtxC o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			fixed4 spec = tex2D(_SpecMap, IN.uv_MainTex);
			o.Albedo = tex.rgb * _Color.rgb;
			o.Gloss = spec.r * tex.a; // multiply red channel of specular map by alpha for final gloss
			o.Alpha = tex.a;
			o.Specular = _Shininess;
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
			o.VertexColor = IN.color.rgb; // Offending line
			// Change to:
			// o.VertexColor = IN.color.rgb * 1.0f; // Now it works!
		}
		
		// Alpha fog vertex function
		void vert (inout appdata_full v, out Input o) {
            // The fog components of this function were removed for simplicity. However, the vert function
            // could not be removed for this example because without it, the error does not happen
            o.uv_MainTex = v.texcoord;
            o.uv_BumpMap = v.texcoord;
            o.color = v.color;
        }
		ENDCG
		
		// FORWARD SURFACE SHADER
		CGPROGRAM
		#pragma target 3.0
		#pragma surface surf BlinnPhong_AlphaTestGlowFix_BakedVertexLighting nolightmap nodirlightmap noambient fullforwardshadows alphatest:_Cutoff exclude_path:prepass vertex:vert

		fixed4 _Color;
		sampler2D _MainTex;
		sampler2D _BumpMap;
		sampler2D _SpecMap;
		half _Shininess;
		
        struct SurfaceOutputVtxC {
            fixed3 Albedo;
            fixed3 Normal;
            fixed3 Emission;
            half Specular;
            fixed Gloss;
            fixed Alpha;
            fixed3 VertexColor;
        };
		
		struct Input {
		  	float2 uv_MainTex; // uv coords 1
		  	float2 uv_BumpMap;
		  	float4 color : COLOR; // vertex color
		};
		
        inline fixed4 LightingBlinnPhong_AlphaTestGlowFix_BakedVertexLighting (SurfaceOutputVtxC s, fixed3 lightDir, half3 viewDir, fixed atten) {
            // Forward lighting model
            half3 h = normalize (lightDir + viewDir);
            fixed diff = max (0, dot (s.Normal, lightDir));
            float nh = max (0, dot (s.Normal, h));
            float spec = pow (nh, s.Specular*128.0) * s.Gloss;
            fixed4 c;
            c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec * 2.0f) * (atten * 2);
            c.a = 0; // No specular glow
            return c;
        }
		
        inline fixed4 LightingBlinnPhong_AlphaTestGlowFix_BakedVertexLighting_PrePass (SurfaceOutputVtxC s, half4 light) {
            // Deferred lighting model
            fixed spec = light.a * s.Gloss;
            fixed4 c;
            light.rgb += s.VertexColor;
            c.rgb = s.Albedo * light.rgb + light.rgb * _SpecColor.rgb * spec * 2.0f;
            c.a = 0; // No specular glow
            return c;
        }

		void surf (Input IN, inout SurfaceOutputVtxC o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			fixed4 spec = tex2D(_SpecMap, IN.uv_MainTex);
			o.Albedo = tex.rgb * _Color.rgb;
			o.Gloss = spec.r * tex.a; // multiply red channel of specular map by alpha for final gloss
			o.Alpha = tex.a;
			o.Specular = _Shininess;
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
			o.Emission = IN.color.rgb * tex.rgb;
		}
		
		// Alpha fog vertex function
		void vert (inout appdata_full v, out Input o) {
            // The fog components of this function were removed for simplicity. However, the vert function
            // could not be removed for this example because without it, the error does not happen
            o.uv_MainTex = v.texcoord;
            o.uv_BumpMap = v.texcoord;
            o.color = v.color;
        }
		ENDCG
	}
	Fallback "Transparent/Cutout/Bumped Specular"
}

[edit] sorry, I should have refreshed before posting, I didn’t realize you already replied. Apparently it took me 7 minutes to type :wink:

I hacked together a shader that compiles with the shader warnings mentioned in the original post, if Unity is running in DX11 mode.

The solutions I suggested don’t seem to help a lot.
For those who are interested in debugging this problem, here’s the hacked together shader code:

Shader "Custom/CastingProblem" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		
		CGPROGRAM
		#pragma surface surf CastProblem
		#pragma debug

		sampler2D _MainTex;

		struct Input {
			float2 uv_MainTex;
			float4 color : COLOR; // vertex color
		};
		
		struct SurfOutput {
			fixed3 Albedo;
			fixed3 Normal;
			fixed3 Emission;
			half Specular;
			fixed Gloss;
			fixed Alpha;
			fixed3 VertexColor;
		};
		
		inline fixed4 LightingCastProblem (SurfOutput s, fixed3 lightDir, fixed atten)
		{
			fixed diff = max (0, dot (s.Normal, lightDir));
			
			fixed4 c;
			c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
			c.a = s.Alpha;
			return c;
		}
		
		void surf (Input IN, inout SurfOutput o) {
//			UNITY_INITIALIZE_OUTPUT(SurfOutput, o);
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
//			o.VertexColor = float3(1,0,0);
			o.VertexColor = IN.color.rgb; // The offending line
		}
		ENDCG
	} 
}

Ahh, thanks for this! My shader was too bulky and would probably scare away some people who would otherwise help, so your version is perfect. I see you also got the error then. Good, then I’m not crazy afterall… well… @_@

I just checked and actually my editor/player is not in DX11 mode.

Actually, unfortunately, my shader doesn’t throw the ‘implicit truncation of vector type’ warning, but just the ‘output parameter ‘o’ not completely initialized’ warnings. They’re still mysterious, but unfortunately I didn’t quite replicate the problem exactly. Might have something to do with the lighting models, but I haven’t been able to find it yet.

Well, that complicates things. This is the first time I’ve seen that warning in regular mode. I don’t understand why it’s compiling for DX11, I thought that only happened in DX11 mode.

Found another band-aid solution:

Edit: An even better one:

//
o.VertexColor = IN.color.rgb * 1.0f;

I suppose I will need to put #pragma exclude_renderers d3d11 in all of my shaders as I don’t imagine I’ll be using any DX11 shaders for this project. (Excluding d3d11 works to fix this bug also, btw.)

Ahhh, yes, you found another one of those as well.

I came back here to post that

 o.VertexColor = IN.color.rgb * 1.00001;

also removes the error. So it looks like the compiler is removing instructions. Actually, you posted about this problem before.

It doesn’t like IN.color. Have you reported the bug yet?

D’oh! No wonder it felt like a deja vu nightmare… I knew I had some issues like this, but this is basically the same dang thing. No, I never reported it because it was just too inconsistent. I’d have dozens of shaders with no problem then ONE would pop in and start doing that. Now it seems like all do.

I’m getting some other equally baffling and hair pulling problems with using tex2D.rgb in some shaders… Different error and not fixable by a * 1.0f hack.

Here’s another baffler… Throws this error because of the line “o.Albedo = tex.rgb”…

Shader "Test" {
	Properties {
		_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
	}
	SubShader {
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert2 nolightmap nodirlightmap noambient fullforwardshadows alpha
		
		sampler2D _MainTex;
		fixed4 _Color;
		
  	struct Input {
			float2 uv_MainTex;
		};
		
		struct SurfOutput {
      fixed3 Albedo;
      fixed3 Normal;
      fixed3 Emission;
      half Specular;
      fixed Gloss;
      fixed Alpha;
    };
		
    inline fixed4 LightingLambert2 (SurfOutput s, fixed3 lightDir, fixed atten) {
        fixed4 c;
        fixed diff = max (0, dot (s.Normal, lightDir));
        c.rgb = s.Albedo * _LightColor0.rgb * diff * atten * 2;
        c.a = s.Alpha;
        return c;
    }
		
		void surf (Input IN, inout SurfOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb; // Offending line
		}
		ENDCG
	}
}

It seems if you remove fullforwardshadows from the #pragma above or add target 3.0 it works. This worked just fine in 3.5.7. Either I’m doing things with just the right combination to break things or…

As a followup to the previous post, I’ve discovered there is a bug with fullforwardshadows on SM2. I’ve reported it. I am also reporting the COLOR bug as well.

SM2 fullforward shadows bug: Fullforwardshadows does not work with SM2.

Problem SM2 shader using full forward shadows always throws errors:

Shader "BUGS/Fullforwardshadows_SM2_Problem" {
	Properties {
		_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
	}
	SubShader {
		LOD 200
		
    // fullforwardshadows = ON
    
    // Errors:
    // Program 'frag_surf", error X4539: sampler mismatch: sampler used inconsistently (compiling for d3d11_9x) at line 11
    // Program 'frag_surf', implicit truncation of vector type (compiling for d3d11_9x) at line 77
		
		CGPROGRAM
		#pragma surface surf Lambert fullforwardshadows
		
		sampler2D _MainTex;
		
  	struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb;
		}
		ENDCG
	}
}

SM2 with fullforward shadows removed. No errors.

Shader "BUGS/No_fullforwardshadows_SM2" {
	Properties {
		_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
	}
	SubShader {
		LOD 200
		
    // fullforwardshadows = OFF
    // No errors
		
		CGPROGRAM
		#pragma surface surf Lambert
		
		sampler2D _MainTex;
		
  	struct Input {
			float2 uv_MainTex;
		};
		
		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb;
		}
		ENDCG
	}
	FALLBACK "Diffuse"
}

SM3 with fullforward shadows enabled. No errrors.

Shader "BUGS/Fullforwardshadows_SM3" {
	Properties {
		_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
	}
	SubShader {
		LOD 200
		
    // fullforwardshadows = ON
    // Shader model 3.0
    // No errors
		
		CGPROGRAM
		#pragma target 3.0
		#pragma surface surf Lambert fullforwardshadows
		
		sampler2D _MainTex;
		
  	struct Input {
			float2 uv_MainTex;
		};
		
		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb;
		}
		ENDCG
	}
	FALLBACK "Diffuse"
}

Shoot, I seemed to have missed this message yesterday. I noticed that today as I was about to file the bug report that it was not giving me the same errors… I found the reason for that one though:

Change the lighting model to this:

inline fixed4 LightingCastProblem (SurfOutput s, fixed3 lightDir, fixed atten)
            {
                fixed diff = max (0, dot (s.Normal, lightDir));
               
                fixed4 c;
                c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2) + s.VertexColor;
                c.a = s.Alpha;
                return c;
            }

Even though that’s not the right model for vertex coloring, just by calling s.VertexColor it eliminates the “not completely initialized” error and your shader compiles. It seems that non-standard fields in the struct must be called somewhere in the lighting model or it won’t initialize them properly.

So I will throw together the shader I have into one file and see if I can boil it down to basics while stll getting the error and post back.

Edit: Sorry for the double-post, IE crashed when I posted the 1st time and I rewrote it, but it looks like it went through afterall…

Okay, I posted the shader in post #3 above. I stripped the shader down as much as I could but still getting that error. (I took out fog completely, but I kept in the skeleton of the vert program for fog because without it the error changes…)

So here the weird part… If I remove the vertex programs from both the forward and deferred shaders, the “implicit truncation” error is gone, but a “not completely initialized” error comes in and nothing I do in the surf or the lighting model will get rid of it. Additionally, if I switch to SM2.0 instead of 3.0, I get even more “not completely initialized” errors.