Redefinition of '_MainTex_ST' error if I use tex2D with second UV

I’m trying to use surface shader for one of my precedural mesh and having a problem to compile the code if I try to sample the texel color from second UV.

The code :

fixed4 c = tex2D(_MainTex, input.uv2_MainTex);
fixed4 c2 = tex2D(_MainTex, input.uv_MainTex);
output.Albedo = c.rgb * c2.rgb;

It looks like each call of tex2D would generate ‘_ST’. It works fine if I just call ‘tex2D(_MainTex, input.uv_MainTex)’ or ‘tex2D(_MainTex, input.uv2_MainTex)’ but not for both.

Is there a way to get texel color from a same texture multiple time from different UV?

Could you copy it first?

fixed4 c = tex2D(_MainTex, input.uv2_MainTex);
float2 copyUV = input.uv2_MainTex;
fixed4 c2 = tex2D(_MainTex, copyUV);
output.Albedo = c.rgb * c2.rgb;

Thanks for your response. I tried but it still gives the same compilation error.

Yeah I suspect the issue is somewhere else in the shader. Can you post the whole thing?

It’s the fact you have both uv_MainTex and uv2_MainTex that’s the problem. Unity’s Surface Shader code generation isn’t smart enough to handle the fact you’re using the same texture name twice on two different UV coordinates. For each uv_ or uv#_ value in the Input struct it’ll try to create a _ST value for it, so tries to create a _MainTex_ST for both, causing the error.

The reason why removing one usage of the tex2D() fixes the problem is the code generation is smart enough to remove code that isn’t being used. If your tex2D(_MainTex, input.uv2_MainTex) isn’t being used, it doesn’t include the uv2_MainTex, and doesn’t try to add a second _MainTex_ST.

The fix is to not call it uv2_MainTex, but something else. Really anything else. You could use uv2_Dummy, or uv2_MainTex2 or whatever you want. The name of the uv Input variable doesn’t have to match the texture it’s being used with, it doesn’t even need to match a texture the shader has a property for. Really all the extra name does is tell it what _ST value to add, and that is used by the scale and offset values a texture property has. It would be even nicer if you could just do float2 uv2;, but alas they don’t handle that. If you want the scale and offset of the _MainTex to apply to the uv2_Dummy you can do this:

fixed4 c2 = tex2D(_MainTex, TRANSFORM_TEX(input.uv2_Dummy, _MainTex));

Alternatively you can pass the UV values manually using a custom vertex function.

1 Like

Thank you so much for the detailed explanation, it’s very helpful. I got rid of compilation error after I’ve renamed uv2_MainTex. But in order to get the correct value I need to add additional property(_DummyTex) in the surface shader :

                fixed4 c = tex2D(_MainTex, input.uv_MainTex);
                fixed4 c2 = tex2D(_MainTex, input.uv2_DummyTex);
                o.Albedo = c.rgb * c2.rgb;
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _DummyTex ("Albedo2 (RGB)", 2D) = "white" {}
    }

Without _DummyTex, c2 is always white color, adding _DummyTex property(even without setting any texture) fixed the problem. I’m a bit confused with the surface shader code generation, adding empty _DummyTex property feels more like a workaround, is there a more proper way for my case?

Yeah, _DummyTex_ST is probably getting initialized as a float4(0,0,0,0) if it doesn’t exist as a property. That’s annoying. You can add [HideInInspector] in front of the _DummyTex property definition to make it a little cleaner.

The “other” way I mentioned is to use a custom vertex function.

#pragma surface surf vertex:vert

struct Input {
    float2 uv_MainTex;
    float2 texcoord1; // name this anything, just can't start with "uv"
}

void vert(inout appdata_full v, out Input o)
{
    UNITY_INITIALIZE_OUTPUT(Input,o);
    // texcoords are 0 based, so uv2 == texcoord1
    o.texcoord1 = v.texcoord1.xy;
    // uv == texcoord, which is actually TEXCOORD0
}

// in surf function
fixed4 c2 = tex2D(_MainTex, IN.texcoord1);

They’re both work arounds, and neither are especially “cleaner” than the other in terms of code. The custom vertex function is ever so slightly more efficient. And by slightly I mean literally a single instruction in the total shader cost by skipping the unnecessary TRANSFORM_TEX call.

1 Like

Thank you. I really appreciate your help. Just tried the custom vertex function, it works well.

Hi,
I know this is an old thread but it was very helpful so far.
Is there anything specific to consider when _MainTex has a texture offset and scale applied and if I want to apply those values to the hidden dummy textures that are only there so that the other texcoords can be stored?
For example, when TRANSFORM_TEX is called for the _MainTex, it uses it’s _MainTex_ST with it’s scaling and offset, so is it required to do something like:

float2 adjustedTexcoord2 = IN.texcoord2 * _MainTex_ST.xy + _MainTex_ST.zw;

Since the dummy textures should have a scaling of 1 and offset of 0, meaning that the main texture scale and offset values have to be applied separately?