Unity 3.0, complex surface shaders.

So I’m converting our 2.6 shaders to 3.0.

So far I enjoyed the new surface shader system. Makes things a lot easier.

But once I started putting together the complex shaders I used to have I started getting weird compilation errors.

Things like:
Program ‘SurfShaderInternalFunc’, cannot locate suitable resource to bind parameter “Emission” at line 82
Or something about “too many Texture Interpolators”.

From what I understand the shader somehow goes beyond the allowed number of varying attributes and this causes this error.

In 2.6 what we used to do is call Passes from other shaders to compose one complex shader with many inputs, etc.

How do we go about that in unity 3?

Thanks beforehand,
Gennadiy

Bump, I’m pretty much stuck converting our shaders :frowning:

Doesn’t anyone have experience writing shaders that access more than 3 textures using Surface Shaders?

Or implementing the passes in the Surface Shaders?
The old syntax won’t compile for me either.

Yeah, you might be hitting the limit of texture interpolators (you can see what Cg source is generated by adding #pragma debug and opening the compiled shader in inspector). Usually what burns a lot of interpolators is per-pixel normal mapped reflection (needs 3 interpolators to pass tangent space vectors).

Tricks to save interpolators: if you use multiple textures, and you know they don’t need different UV scale/offset (e.g. main texture normal map usually use the same), use one uv* variable in surface shader input. Compiling shaders to shader model 3.0 (#pragma target 3.0) also increases number of interpolators available from 8 to 10.

If you still can’t fit into one pass, and need to split into multiple passes, that is generally possible. Surface shaders just generate a bunch of passes behind the scenes, so if you put manually written passes, UsePasses or just two surface shaders next to each other, it will result in multiple passes. Check out the lit Toon Outline shader in 3.0 resources for example: it’s a surface shader followed by UsePass that does black extruded outline.

Also, perhaps post your shader that you have troubles with here, maybe it’s possible to optimize it without resorting to multipass rendering.

So, passes are no longer supported?

Are the chances of seeing the fur shader ported to 3.0 now really slim? (it uses a ton of passes)

Thanks!
So I’ve spent quite some time trying to get this work, with no luck so far :frowning:

I tried doing it in 3 different ways.

Adding #pragma target 3.0 and removing the uv’s for the normal map didn’t seem to free enough interpolators. I still couldn’t compile the shader.

The shader inputs looks like this:

sampler2D _MainTex;
sampler2D _MatCap;
sampler2D _BumpMap;
samplerCUBE _Cube;
half4 _ReflectColor;  // Used to modulate reflection colors, could be optimized away but doesn't really seem to help.
		
struct Input {
		float2 uv_MainTex;
                //float2 uv_NormalMap; // Optimized away for now.

                // Values needed for the lit sphere sampling.
                float3   tangentX; 
                float3   tangentY; 

                float3 worldRefl;
                INTERNAL_DATA
 };

And I do compute the view related tangents using the tangent space rotation in the custom vertex program, as shown in the help example.

So I tried using the reflective pass from the built-in reflective shader:

SubShader {
     UsePass "Reflective/Bumped Unlit/BASE"
     CGPROGRAM
     .... surface shader code ....
     ENDCG
}

This compiles, but reflections are not seen. Doesn’t matter if I put the UsePass before or after my surface shader code.

Then I tried putting 2 surface shaders together in one sub shader:

SubShader {
     UsePass "Reflective/Bumped Unlit/BASE"
     CGPROGRAM
     .... surface shader code ....
     ENDCG
     CGPROGRAM
     ....reflections shader code written as surface shader....
     ENDCG
}

This compiles but it looks like the last CGPROGRAM on the list just overrides the previous one.
How can it be made additive as it seemed to be the case in 2.6?
So that the output from reflections shader would be added to the output of the first shader?

I tried using Pass { } directive to put the two shaders into their own passes, but couldn’t get the syntax to compile.

SubShader {
     Pass {
         Name "Blah"
              CGPROGRAM
               .... surface shader code ....
              ENDCG
          }
     }
     Pass {
         Name "Blah2"
              CGPROGRAM
               .... reflections shader code ....
              ENDCG
          }
     }
}

Thanks again,
Gennadiy

Bump? It’s been 3 days since my last post and it’s close to weekend. We are pretty late on schedule with converting our shaders to Unity 3.

Did anyone have experience/found solutions to these problems?

By default all passes are opaque (no blending is used), so unless you explicitly specify transparency in one way or another, further passes will “replace” the previous ones. You can specify transparency in various ways, e.g. add “Blend One One ZWrite Off” after first pass, or add “decal:add” tag to surface shader pragma line, etc.

That said, what do your tangentX/tangentY do in the shader?

They are.

From a quick look, it looks like that shader is not per-pixel lit, it just does simple vertex lighting calculation in the vertex shader. Hence it should not be rewritten as a surface shader; i.e. it should just work like it used to.

They are used to sample the lit sphere texture. (http://www.cs.utah.edu/npr/papers/LitSphere_HTML/node3.html)

Could you please pin point me to the reference page where the flags like “decal:add” and are explained? Sounds like it’s really good stuff, don’t wanna miss any of it :slight_smile:

Thanks again,
Gennadiy

I’ve tried using the Blend Add Add ZWrite Off,

it allows for some layering, but in deferred rendering mode it somehow “damages” the normal information. So the specs and lighting is no longer properly computed.

As far as I understand the surface shaders are just called one after another to write to normal/depth/etc buffer for deferred rendering.
Does the Blend Add Add influence normal/depth information as well? Or is it overriden by the last cg program in the shader?

Thanks again,
Gennadiy

Okay, using Blend SrcAlpha One and writing out Alpha value of 1.0 for the overlay shader works. No idea why :smile:

Actually Aras this reminds me of a question I had and never asked. Is is possible that the TBN matrix could be exposed properly in the surface shader? I have wanted to use it a few times but can’t because it is only properly defined for some of the internal permutations. It would only need to be there if INTERNAL_DATA was needed.

Another question I had is would it be possible to get access to the texture / material matricies in the pixel shader or allow the modified UV’s to be created in the pixel shader so they don’t eat up v2f interpolation slots (i.e you only need to pass the mesh UV and the pixel shader will calculate the per material UV)?

Also I think there are some issues with using the world2object and object2world matricies in the pixel shader… I raised a bug during beta but i’m not sure if it has been fixed yet. There is some other issues I have discovered as well, and raised bugs about them.