Hello, I am wondering about #pragma multi_compile. If I use #define, it works fine. Code (csharp): Shader "Test" { Properties { _Color ("Color" , Color) = (1, 1, 1, 1) _MainTex ("Base (RGB)", 2D ) = "white" {} _Cube ("Cube" , CUBE ) = "black" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert #define ON half4 _Color; #if defined (ON) sampler2D _MainTex; samplerCUBE _Cube; #endif struct Input { float2 uv_MainTex; float3 worldRefl; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = _Color; half4 r; #if defined (ON) c *= tex2D (_MainTex, IN.uv_MainTex); r = texCUBE (_Cube, IN.worldRefl); o.Emission = r.rgb; #endif o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } But, I use #pragma multi_compile, it doesn't works. Code (csharp): Shader "Test" { Properties { _Color ("Color" , Color) = (1, 1, 1, 1) _MainTex ("Base (RGB)", 2D ) = "white" {} _Cube ("Cube" , CUBE ) = "black" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert #pragma multi_compile ON OFF half4 _Color; #if defined (ON) sampler2D _MainTex; samplerCUBE _Cube; #endif struct Input { float2 uv_MainTex; float3 worldRefl; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = _Color; half4 r; #if defined (ON) c *= tex2D (_MainTex, IN.uv_MainTex); r = texCUBE (_Cube, IN.worldRefl); o.Emission = r.rgb; #endif o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" CustomEditor "Test_Editor" } Code (csharp): using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.Linq; public class Test_Editor : MaterialEditor { public override void OnInspectorGUI () { base.OnInspectorGUI (); if (!isVisible){ return; } Material targetMat = target as Material; string[] keyWords = targetMat.shaderKeywords; bool redify = keyWords.Contains("ON"); EditorGUI.BeginChangeCheck (); redify = EditorGUILayout.Toggle ("ON", redify); if (EditorGUI.EndChangeCheck ()) { var keywords = new List<string> { redify ? "ON" : "OFF"}; targetMat.shaderKeywords = keywords.ToArray (); EditorUtility.SetDirty (targetMat); } } } Program 'frag_surf', variable 'surfIN' used without having been completely initialized.
The multi compile version get compiled twice, once with ON defined and once without it. The version withou ON gives error when compiling for D3D11 if you don't initialize emission. Try setting emission to zero in #else block. Hope that helps, I can't verify now.
Thank you for your reply but it doesn't works. Now, I tried to get out _MainTex(uv_MainTex, tex2D) from #if, there is no error, but emission still doesn't works.:-?
Does it work if you set the keyword globally with Shader.EnableKeyword("ON")? I think iaanus meant #ifdef keyword instead of just #if
I may have just run into the same problem you are seeing. It appears that sometimes the shader system removes interpolators too aggressively when using #pragma multi_compile in combination with surface shaders. You can work around this problem by referencing all interpolators in all multi compile permutations. The problem does not seem to affect explicit vertex/fragment shaders. In your case the workaround would be something like: Code (csharp): #if defined (ON) c *= tex2D (_MainTex, IN.uv_MainTex); r = texCUBE (_Cube, IN.worldRefl); o.Emission = r.rgb; #else // Reference interpolators here but multiply by a really small number so they don't really change the final result o.Emission = IN.uv_MainTex.x * IN.worldRefl.x * 0.000001; #endif I have submitted a bug to Unity showing the problem. For those interested here is a package showing the bug and a workaround. Edit: Just tried the package on PC (was testing on Mac before). Now I get a load of D3D errors and the same warning about 'surfIN' not being fully initialised. Looks to be the same problem but D3D is stricter about it than OpenGL.
Not at my work rig at the moment, but try using Code (csharp): #pragma multi_compile ON OFF .... #ifdef ON #endif #ifdef OFF #endif and in your script -- Code (csharp): Shader.EnableKeyword("ON"); Shader.DisableKeyword("OFF"); ..depending on which one you're after.
Just to clarify. The problem I have isn't to do with the usage of the shader keyword defines in the shader or the enabling/disabling of keywords from script. The problem I'm seeing is in the code Unity generates from the surface shader code. If I enable #pragma debug I can see that there are missing interpolators in the v2f_surf struct that Unity generates from the Input struct. This means that when I sample textures in the fragment shader rather than getting correct texture coordinates I just get a default value of 0. Edit: If you open the package I posted earlier and open the compiled shader for 'ShaderBroken' you can see that there is no interpolator for the main texture coordinate in v2f_surf. If you open the compiled shader for 'ShaderWorkaround' you will see an interpolator called 'pack0', Unity packs the main texture coordinate into this and everything works fine.
No worries Gibbonator. My post was in reply to #ifdef not working correctly a few posts up.. just in case it was a mixup with the defines
After a bit more experimentation I think I've figured out what's going on. It appears that for the purposes of determining which interpolators are required Unity assumes that none of the custom multicompile keywords are defined. So if I do this: Code (csharp): #pragma multi_compile FEATURE_OFF FEATURE_ON void surf(Input IN, inout SurfaceOutput o) { #if defined(FEATURE_ON) o.Emission = tex2D(_MainTex, IN.uv_MainTex) * texCUBE(_CubeTex, IN.dir); #endif } I get these interpolators: Code (csharp): struct v2f_surf { float4 pos : SV_POSITION; float3 cust_dir : TEXCOORD0; float2 lmap : TEXCOORD1; LIGHTING_COORDS(2,3) }; There is no interpolator for uv_MainTex, which is why it breaks. But if I do this: Code (csharp): #pragma multi_compile FEATURE_OFF FEATURE_ON void surf(Input IN, inout SurfaceOutput o) { // Workaround code #if !defined(FEATURE_OFF) !defined(FEATURE_ON) o.Emission = IN.uv_MainTex.x * IN.dir.x; #endif // Actual code #if defined(FEATURE_ON) o.Emission = tex2D(_MainTex, IN.uv_MainTex) * texCUBE(_CubeTex, IN.dir); #endif } I get these interpolators: Code (csharp): struct v2f_surf { float4 pos : SV_POSITION; float2 pack0 : TEXCOORD0; float3 cust_dir : TEXCOORD1; float2 lmap : TEXCOORD2; LIGHTING_COORDS(3,4) }; uv_MainTex is put in the pack0 interpolator. This is better than the previous workaround because the workaround code never makes it into the final shaders. @Arkhivrag: Your code started working when you changed #if defined (ON) to #ifndef OFF because the code will now be present in the case when you have ON !OFF and also in the case where you have !ON !OFF. Edit: In the interests of coming up with a tidier workaround to these problems I've come up with this: Code (csharp): #pragma multi_compile FEATURE_OFF FEATURE_ON #if !defined(FEATURE_OFF) !defined(FEATURE_ON) #define GET_INTERPOLATORS #endif void surf(Input IN, inout SurfaceOutput o) { #if defined(FEATURE_ON) || defined(GET_INTERPOLATORS) o.Emission = tex2D(_MainTex, IN.uv_MainTex) * texCUBE(_CubeTex, IN.dir); #endif } What this does is define a new keyword (GET_INTERPOLATORS) for when Unity is determining which interpolators are used. You can then use this keyword in all your multicompile code blocks to ensure that they are taken into account. Edit 2: This also works and is more scalable if you have several multi compiles. Code (csharp): #pragma multi_compile FEATURE_OFF FEATURE_ON #pragma multi_compile IS_MULTI_COMPILE void surf(Input IN, inout SurfaceOutput o) { #if defined(FEATURE_ON) || !defined(IS_MULTI_COMPILE) o.Emission = tex2D(_MainTex, IN.uv_MainTex) * texCUBE(_CubeTex, IN.dir); #endif }
@Gibbonator Thanks for answer. But as it happens only in surface shaders and not in fragment shaders - I suppose think it is a bug and requires improvement from Unity. Here is example where your suggestion does not work. Mine too Code (csharp): Shader "Custom/test" { SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert #pragma only_renderers d3d9 #pragma multi_compile OFF ON #if !defined(OFF) !defined(ON) #define GET_INTERPOLATORS #endif struct Input { float4 color : COLOR; #if defined(ON) || defined(GET_INTERPOLATORS) float3 viewDir; #endif }; void surf (Input IN, inout SurfaceOutput o) { half3 albedo = half3(1, 0, 0); #if defined(ON) || defined(GET_INTERPOLATORS) albedo = IN.viewDir; #endif o.Albedo = albedo; } ENDCG } Fallback "Diffuse" } Can not use #pragma debug - unrecognized #pragma debug at line 10
The problem here is the conditionals in the Input struct. I'm not sure how to work around that. Given that Unity will only create one set of interpolators anyway makes trying to remove things from the Input struct redundant. That set of interpolators would need to be the set of all required interpolators across all multicompile permutations. Ideally Unity would create a new set of interpolators for each multicompile permutation but this doesn't seem to be the case
Did you ever get any feedback from Unity developers regarding the bug report you submitted? I just ran into this issue late last week and have been searching for a solution. I was able to narrow the compiler error down, and below is a very simplified version of my shader. If I change colorB's assignment to "colorB = tex2D(_Color2Tex, IN.uv_Color1Tex);", then everything compiles fine. Using the second uv set is causing the uninitialized component compilation error. Code (csharp): Shader "SimpleSensor/Diffuse" { Properties { _Color1Tex ("Base (RGB)", 2D) = "white" {} _Color2Tex ("Heat", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert #pragma multi_compile USE_COLOR_A USE_COLOR_B #pragma debug sampler2D _Color1Tex; sampler2D _Color2Tex; struct Input { float2 uv_Color1Tex; float2 uv2_Color2Tex; }; void surf (Input IN, inout SurfaceOutput o) { float4 colorA = tex2D(_Color1Tex, IN.uv_Color1Tex); // this one doesn't compile float4 colorB = tex2D(_Color2Tex, IN.uv2_Color2Tex); // replacing In.uv2_Color2Tex with In.uv_Color1Tex fixes compilation error, // but I lose my ability to use the second UV set. float4 color = 0; #if USE_COLOR_A color = colorA; #endif #if USE_COLOR_B color = colorB; #endif o.Albedo.rgb = color.rgb; o.Alpha = color.a; } ENDCG } Fallback "Diffuse" }
Unfortunately I have not heard anything from Unity on this issue. The issue is still open in FogBugz though, so they may still get back to me.
Out of interest, does this work? Code (csharp): Shader "SimpleSensor/Diffuse" { Properties { _Color1Tex ("Base (RGB)", 2D) = "white" {} _Color2Tex ("Heat", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert #pragma multi_compile USE_COLOR_A USE_COLOR_B #pragma multi_compile IS_MULTI_COMPILE #pragma debug sampler2D _Color1Tex; sampler2D _Color2Tex; struct Input { float2 uv_Color1Tex; float2 uv2_Color2Tex; }; void surf (Input IN, inout SurfaceOutput o) { float4 colorA = tex2D(_Color1Tex, IN.uv_Color1Tex); // this one doesn't compile float4 colorB = tex2D(_Color2Tex, IN.uv2_Color2Tex); // replacing In.uv2_Color2Tex with In.uv_Color1Tex fixes compilation error, // but I lose my ability to use the second UV set. float4 color = 0; #if USE_COLOR_A color = colorA; #endif #if USE_COLOR_B color = colorB; #endif #if IS_MULTI_COMPILE o.Albedo.rgb = color.rgb; #else o.Albedo.rgb = colorA.rgb * colorB.rgb; #endif o.Alpha = color.a; } ENDCG } Fallback "Diffuse" }
YES! Thank you! What I cannot figure out though is "why does this work?" btw... I apologize for the late reply. I'm being pulled in multiple directions lately.
Wow I've just ran into this issue in 4.2.2f1 --- seems like this wasn't important enough to fix at any point during the last 2.5 months? (Or even updating the docs for #pragma multi_compile / custom material editors could have done the trick to save some of us an hour or three of confused frustation.) So multi_compile doesn't work with surface shaders. Well why should two great features be indeed combinable as implicated possible in the docs, right? So we have to keep creating many .shader files for all possible permutations and cginc everything everywhere, when multi_compile could in theory solve this so elegantly (if it actually worked fully with surface shaders)? This is insanity. So Gibbonator can you give us the FogBugz ID? Or at least let us know if anyone ever got back to you?
WOAH looking through the 4.3 release notes I see this nuggest: Shaders: Fixed #pragma multi_compile shader variants not working with surface shaders in some cases Here's hoping! Will hold out for a few days before updating though (in the middle of a mess right now etc), if anyone does before me, do report back
Below code works in 4.3! Haven't tested more complex stuff yet. Code (csharp): Shader "Custom/MulticompTest" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} _BumpMap ("Normalmap", 2D) = "bump" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 300 CGPROGRAM #pragma surface surf Lambert #pragma multi_compile BUMP_OFF BUMP_ON sampler2D _MainTex; #ifdef BUMP_ON sampler2D _BumpMap; #endif fixed4 _Color; struct Input { float2 uv_MainTex; #ifdef BUMP_ON float2 uv_BumpMap; #endif }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; #ifdef BUMP_ON o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); #endif } ENDCG } FallBack OFF CustomEditor "MulticompTest" } Code (csharp): using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.Linq; public class MulticompTest : MaterialEditor { public override void OnInspectorGUI () { base.OnInspectorGUI (); if (!isVisible) return; Material targetMat = target as Material; string[] keyWords = targetMat.shaderKeywords; bool bump = keyWords.Contains ("BUMP_ON"); EditorGUI.BeginChangeCheck(); bump = EditorGUILayout.Toggle ("Bump mappped", bump); if (EditorGUI.EndChangeCheck()) { var keywords = new List<string> { bump ? "BUMP_ON" : "BUMP_OFF"}; targetMat.shaderKeywords = keywords.ToArray (); EditorUtility.SetDirty (targetMat); } } }
Has there ever been an update to the docs synced timely with an update to the Editor? sometimes I think the docs are just some dude's weekend hobby..