Texture Arrays in Unity 2017

Hello,

I’m trying to create a surface shader that will work on Unity 2017.2.0f3 (latest afaik as of writing this).

I’ve read that it should just work now but I’ve been at it for the whole day with no success. I can’t find much info on the error.

Whenever I call UNITY_SAMPLE_TEX2DARRAY I get

Shader error in

I know there’s a define related to the surface alalysis I can use but according to this post, I should not need it anymore: Texture arrays

If I add that it does compile but the uvs seem to always be fixed.

Here’s the full shader:

Shader "Custom/NewSurfaceShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2DArray) = "" {}
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
       
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 4.0

        UNITY_DECLARE_TEX2DARRAY( _MainTex);

        struct Input
        {
            float3 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        half _Slice;
        half _Mult;
        fixed4 _Color;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Could anyone help me out? I would really appreciate it.

Thanks!

uv_MainTex should be float2. A new float3 UV would then be required for the texture array with its index in Z.

But MainTex is a 2DArray Texture and I’m calling this on the c# side m_mesh.SetUVs(0, uvs); where uvs is declared as List uvs. I actually need to pass the Z index in the UV.

Understand in Surface Shaders the “uv_” prefix must be a float2. Internally this is letting the Surface Shader code know you want to use the first UV set and apply the offset and scale from the _MainTex. When it generates the real shader code (Surface Shaders are vertex fragment shader generators) it is adding a line like this.

o.uv_MainTex = TRANSFORM_TEX(v.texcoord.xy, _MainTex);

That TRANSFORM_TEX macro explicitly returns a float2 value. What you likely need is something like this:

o.uv_MainTex = float3(TRANSFORM_TEX(v.texcoord.xy, _MainTex), v.texcoord.z);

But, Unity doesn’t know that’s what you want, and using an Input variable with the “uv_” prefix will always add the first bit of code. The solution is to use a different variable name and calculate the value on your own with a custom vertex function.

#pragma surface ... vertex:vert

...

Input {
    float3 texcoord_MainTex; // not using uv_ prefix
}

float4 _MainTex_ST; // the scale offset data, the uv_ prefix adds this for you too, gets used by that TRANSFORM_TEX macro

...

void vert (inout appdata_full v, out Input o) {
    UNITY_INITIALIZE_OUTPUT(Input,o);
    o.texcoord_MainTex = float3(TRANSFORM_TEX(v.texcoord.xy, _MainTex), v.texcoord.z);
}

Then use IN.texcoord_MainTex when sampling your texture array. You could also just skip the TRANSFORM_TEX part entirely if you don’t need or want the scale and offset. Just use:

o.texcoord_MainTex = v.texcoord.xyz;

You can name that Input variable anything you want really, btw, just as long as it doesn’t start with “uv” and isn’t one of the other special variable names.

Oh, I see. I didn’t know you can add a custom vertex function on the surface shader.
I’ll try this when I get home from work.

Thank you!