tex2D inside UNITY_PROCEDURAL_INSTANCING_ENABLED in surf doesn't work.

Hi,
I want to use multiple textures on an Instanced rendered Object. I’ve tested it with plain Colors and it works but as soon as I add Textures the Texture will be displayed as just one color on the Object:

...
                #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                        StructuredBuffer<Particle> particleBuffer;
                #endif
...
void surf (Input IN, inout SurfaceOutputStandard o)
        {

            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED

                int colorID = particleBuffer[unity_InstanceID].colorID;

                if (colorID == 0) {
                    //this works:
                    o.Albedo = _Color.rgb;
                    //this doesn't work:
                    o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
                }
            #endif
           
        }

The UV mapping is correct, f.e. this Code works:

void surf (Input IN, inout SurfaceOutputStandard o)
        {

            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED

                int colorID = particleBuffer[unity_InstanceID].colorID;

                if (colorID == 0) {
                    o.Albedo = _Color.rgb;
                }
           
            #endif
                o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
           
        }

But doing it this way I can only get one Texture on the object. But I need a switch to change the texture.

So I’ve introduced a helper Variable like this:

void surf (Input IN, inout SurfaceOutputStandard o)
        {
            int textureSwitch = 0;
            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED

                int colorID = particleBuffer[unity_InstanceID].colorID;

                if (colorID == 0) {
                    o.Albedo = _Color.rgb;
                    textureSwitch = 1;
                }

            #endif

                if (textureSwitch) {
                    o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
                }
           
        }

textureSwitch is never set by what’s inside UNITY_PROCEDURAL_INSTANCING_ENABLED but it’s called.

What’s going on here?

Edit.
This guy is having the exact same problem:

So after doing some more research it seems like the UV coordinates inside the surf function are wrong. This doesn’t work as expected (Texture is one-colored):

void surf(Input IN, inout SurfaceOutputStandard o)
                  {
                      int k = 0;
                      #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                      k = 1;
                      #endif
                      o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb*k;

                  }

This doesn’t work.

Edit: this is the whole Shader:

Shader "ComputeShader/Cubes"
{
    Properties
    {
            _MainTex("Texture", 2D) = "white" {}
            _TextureTwo("_TextureTwo", 2D) = "white" {}

            _Color("Color", Color) = (1,1,1,1)
            _ColorTest("ColorTest", Color) = (1,1,1,1)

            _ColorCollisionLeft("ColorCollisionLeft", Color) = (1,1,1,1)
            _ColorCollisionRight("ColorCollisionRight", Color) = (1,1,1,1)
            _ColorCollisionTop("ColorCollisionTop", Color) = (1,1,1,1)
            _ColorCollisionBottom("ColorCollisionBottom", Color) = (1,1,1,1)
            _ColorCollisionFront("ColorCollisionFront", Color) = (1,1,1,1)
            _ColorCollisionBack("ColorCollisionBack", Color) = (1,1,1,1)

            _Glossiness("Smoothness", Range(0,1)) = 0.5
            _Alpha("Alpha", Range(0,1)) = 0.5

    }
        SubShader{
            Tags { "RenderType" = "Opaque" }
            LOD 200

                #pragma surface surf Standard vertex:vert addshadow
                #pragma multi_compile_instancing
                #pragma instancing_options procedural:setup
                #pragma target 3.0
                #include "TransformMatrix.cginc"

                half _Glossiness;
                half _Alpha;


                //debugging colorss
                half4 _Color;
                half4 _ColorTest;

                //collision colors
                half4 _ColorCollisionLeft;
                half4 _ColorCollisionRight;
                half4 _ColorCollisionTop;
                half4 _ColorCollisionBottom;
                half4 _ColorCollisionFront;
                half4 _ColorCollisionBack;

                struct Input {
                    float2 uv_MainTex;
                    float2 uv_TextureTwo;
                    half4 col;
                };

                //new particle struct
                struct Particle
                {
                    half3 position;
                    half3 positionToMoveTo;
                    half3 velocity;
                    half3 scale;
                    half3 rotation;
                    int isActive;
                    int isVisible;
                    int colorID;
                };
                        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                                StructuredBuffer<Particle> particleBuffer;
                        #endif

                void vert(inout appdata_full v)
                {
                  #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED

                                          half3 position = particleBuffer[unity_InstanceID].position;
                                          half3 scale = particleBuffer[unity_InstanceID].scale;
                                          half3 rotation = particleBuffer[unity_InstanceID].rotation;

                                          int isActive = particleBuffer[unity_InstanceID].isActive;
                                          int isVisible = particleBuffer[unity_InstanceID].isVisible;

                                          if (isVisible) {

                                              float4x4 object2world = (float4x4)0;
                                              object2world._11_22_33_44 = float4(scale.xyz, 1.0);

                                              float4x4 rotMatrix = eulerAnglesToRotationMatrix(rotation.xyz);

                                              object2world = mul(rotMatrix, object2world);
                                              object2world._14_24_34 += position.xyz;

                                              v.vertex = mul(object2world, v.vertex);
                                              v.normal = normalize(mul(object2world, v.normal));

                                          }
                                          else {
                                            //...
                                          }
                      #endif
                  }

                  void setup()
                  {

                  }

                  sampler2D _MainTex;
                  sampler2D _TextureTwo;
                

                  void surf(Input IN, inout SurfaceOutputStandard o)
                  {
                      int k = 0;
                      #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                      k = 1;
                      #endif
                      o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb*k;

                  }
        ENDCG
    }
    FallBack "Diffuse"
}

If I want to debug the vertex color using

                struct Input {
                    float4 color : COLOR;
                };

This will be red:

void surf(Input IN, inout SurfaceOutputStandard o)
                  {
                      #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                      o.Albedo = IN.color;
                      #endif
                  }

This will be white:

 void surf(Input IN, inout SurfaceOutputStandard o)
                  {
                      o.Albedo = IN.color;
                  }

I’ve managed to solve it this way:

void surf(Input IN, inout SurfaceOutputStandard o)
                  {
                     
                      float3 colorOne = tex2D(_MainTex, IN.uv_MainTex).rgb;
                      float3 colorTwo = tex2D(_TextureTwo, IN.uv_MainTex).rgb;
                      float3 colorThree = tex2D(_TextureThree, IN.uv_MainTex).rgb;

                      //default Color
                      float3 cubeColor = colorOne;

                      #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED

                          int colorID = particleBuffer[unity_InstanceID].colorID;

                          if (colorID == 0) {
                              cubeColor = colorTwo;
                          }
                          if (colorID == 1) {
                              cubeColor = colorThree;
                          }
                          if (colorID == 2) {
                              cubeColor = colorThree;
                          }

                          o.Smoothness = _Glossiness;
                          o.Alpha = _Alpha;
                      #endif
                          o.Albedo = cubeColor;

                  }

Some explanation of why you were having issues.

Unity’s Surface Shaders look over your surf function to see what values are and aren’t being used, but it does this with none of the built in defines. This means if you have a surf function that samples a texture, but only inside of #if UNITY_PROCEDURAL_INSTANCING_ENABLED, it’ll think you’re not using the texture UVs at all and skips setting up those values. By using them outside of the #if it lets the code generator see that you are actually using it.

Also, if you’re looking to use multiple textures with instancing, look into texture arrays.

1 Like

Ah ok, I understand, thanks for clarifying.
I have a custom set of UVs in IN.uv_MainTex which I can reuse for the other textures because the UVs of every object are the same.
Currently I am not encountering any problems, do I really need to use Texture arrays? In what cases do I use them.

With your current setup, for every texture you want to use, every instance has to sample from every texture. For 3 or 4 textures this isn’t a huge deal, but it does mean the shader is going to be slower than it could be. It also means more work if you want to add more textures.

Texture arrays let you put multiple textures into a “single texture” where you can choose which texture to access with a third uv component that selects the layer index. This means you only sample “one texture” for all of the instances, and it doesn’t require any shader changes to add or remove textures. Just pass the index as an instanced property, like you already are, and you’re done.

1 Like

Ah now I know what you mean. Yes I will keep that in mind for later thanks for the hint and the help, really appreciate it.