Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[Solved] Texture2DArray Shader

Discussion in 'Shaders' started by Magister, Jul 19, 2019.

  1. Magister

    Magister

    Joined:
    Mar 29, 2013
    Posts:
    34
    I'm trying to use Texture2DArray in place of a texture atlas. The problem is the Standard shader doesn't support Texture2DArray and I'm terrible at shaders. I want something that mimics all the same functionality as the Standard shader, but uses Vector3 in place of Vector2 for the UVs so the Z of the UV can be used to pick the texture from the Texture2DArray. I'm using mesh.SetUVs(0, List<Vector3>(...)) to pass in the 3D UVs. I tried following https://medium.com/@calebfaith/how-to-use-texture-arrays-in-unity-a830ae04c98b, but my simple test mesh is showing up plain white, no texture.
     
  2. Magister

    Magister

    Joined:
    Mar 29, 2013
    Posts:
    34
    Here's the shader that I have so far;
    Code (CSharp):
    1. Shader "Custom/Voxel Standard Array"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Color", Color) = (1,1,1,1)
    6.         _MainTex("Albedo (RGB)", 2DArray) = "white" {}
    7.         _Glossiness("Smoothness", Range(0,1)) = 0.5
    8.         _Metallic("Metallic", Range(0,1)) = 0.0
    9.         _ZOffset("Z Buffer Offset", Float) = 0
    10.     }
    11.  
    12.     SubShader
    13.     {
    14.         Tags { "RenderType" = "Opaque" }
    15.         LOD 200
    16.         Offset[_ZOffset],[_ZOffset]
    17.  
    18.         CGPROGRAM
    19.         // Physically based Standard lighting model, and enable shadows on all light types
    20.         #pragma surface surf Standard fullforwardshadows
    21.  
    22.         // Use shader model 3.0 target, to get nicer looking lighting
    23.         #pragma target 3.0
    24.  
    25.         // texture arrays are not available everywhere,
    26.         // only compile shader on platforms where they are
    27.         #pragma require 2darray
    28.  
    29.         UNITY_DECLARE_TEX2DARRAY(_MainTex);
    30.         //sampler2D _MainTex;
    31.  
    32.         struct Input {
    33.             float4 color: COLOR;
    34.             float3 uv_MainTex;
    35.         };
    36.  
    37.         half _Glossiness;
    38.         half _Metallic;
    39.         fixed4 _Color;
    40.  
    41.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    42.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    43.         // #pragma instancing_options assumeuniformscaling
    44.         UNITY_INSTANCING_BUFFER_START(Props)
    45.             // put more per-instance properties here
    46.         UNITY_INSTANCING_BUFFER_END(Props)
    47.  
    48.         void surf(Input IN, inout SurfaceOutputStandard o) {
    49.             // Albedo comes from a texture tinted by color
    50.             fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, IN.uv_MainTex) * _Color;
    51.             o.Albedo = c.rgb * IN.color; // Combine normal color with the vertex color
    52.  
    53.             // Metallic and smoothness come from slider variables
    54.             o.Metallic = _Metallic;
    55.             o.Smoothness = _Glossiness;
    56.             o.Alpha = c.a;
    57.         }
    58.         ENDCG
    59.     }
    60.  
    61.     FallBack "Diffuse"
    62. }
    63.  
    But I get the following errors in Unity;
    Shader error in 'Custom/Voxel Standard Array': cannot implicitly convert from 'const float2' to 'float3' at line 93 (on d3d11)

    Compiling Vertex program with UNITY_PASS_DEFERRED LIGHTPROBE_SH UNITY_HDR_ON
    Platform defines: UNITY_ENABLE_REFLECTION_BUFFERS UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING UNITY_ENABLE_DETAIL_NORMALMAP SHADER_API_DESKTOP UNITY_COLORSPACE_GAMMA UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_LIGHTMAP_FULL_HDR

    And;
    Shader error in 'Custom/Voxel Standard Array': cannot implicitly convert from 'const float2' to 'float3' at line 152 (on d3d11)

    Compiling Vertex program with UNITY_PASS_FORWARDBASE DIRECTIONAL
    Platform defines: UNITY_ENABLE_REFLECTION_BUFFERS UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING UNITY_ENABLE_DETAIL_NORMALMAP SHADER_API_DESKTOP UNITY_COLORSPACE_GAMMA UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_LIGHTMAP_FULL_HDR

    But this confuses me as my shader is only 62 lines long, yet it's stating errors on lines 93 and 152.

    If I change line 34 to
    float2 uv_MainTex;
    and line 50 to
    fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(IN.uv_MainTex, 0)) * _Color;
    it fixes the errors and then I can run my shader, but everything is stuck to only using the first texture in the texture array. So what I'm looking for is how do you use Vector3 for UVs in this shader instead of Vector2? Or is there a better way to pass the third UV value for specifying the texture index in the texture array?
     
    Last edited: Jul 20, 2019
  3. r3eckon

    r3eckon

    Joined:
    Jul 3, 2015
    Posts:
    506
    The 0 you added is the array index of the element you want to fetch from the texture array. That's why you always use the first element. In texture arrays your "sampling" UV has a third component, and that component is the index of the image you want to sample from the array.

    Code (CSharp):
    1. fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(IN.uv_MainTex, ARRAY_INDEX)) * _Color;
     
  4. Magister

    Magister

    Joined:
    Mar 29, 2013
    Posts:
    34
    I know that the third component of the UV is the array index and that by using 0 it's always the first element. With your code example I assume ARRAY_INDEX is a property you can assign on the material, but that would mean the same array index is used for the entire mesh. What I'm asking is how do I make it so a different array index can be assigned to every vertex of the mesh, or in other words how can you use UVWs (Vector3s) in the shader. I can assign them to the mesh using
    mesh.SetUVs(0, List<Vector3>(...))
    but I can't get them to work in the shader. Or is there a different better way to do this?
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    The problem is Surface Shaders predate the support for texture arrays by half a decade, and were never updated to add support for 3 component UVs. That’s not to say you can’t access the full float3 or even float4 of a UV set, just that the uv_TexName variables in the Input struct are always only a float2.

    The solution is to use a custom vertex function to get the z component and pass it to the Input struct as a custom variable.
    Code (csharp):
    1. #pragma surface surf ... vertex:vert
    2.  
    3. struct Input
    4. {
    5.     float2 uv_MainTex;
    6.     float arrayIndex; // cannot start with “uv”
    7. };
    8.  
    9. void vert(inout appdata_full v, out Input o)
    10. {
    11.     o.arrayIndex = v.texcoord.z;
    12. }
    13.  
    14. // in the surf function
    15. fixed4 col = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(IN.uv_MainTex, IN.arrayIndex));
     
  6. Magister

    Magister

    Joined:
    Mar 29, 2013
    Posts:
    34
    That's perfect. Thank you very much. For anyone it may help, here's the finished shader.
    Code (CSharp):
    1. Shader "Custom/Voxel Standard Array"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Color", Color) = (1,1,1,1)
    6.         _MainTex("Albedo (RGB)", 2DArray) = "white" {}
    7.         _Glossiness("Smoothness", Range(0,1)) = 0.5
    8.         _Metallic("Metallic", Range(0,1)) = 0.0
    9.         _ZOffset("Z Buffer Offset", Float) = 0
    10.     }
    11.  
    12.     SubShader
    13.     {
    14.         Tags { "RenderType" = "Opaque" }
    15.         LOD 200
    16.         Offset[_ZOffset],[_ZOffset]
    17.  
    18.         CGPROGRAM
    19.         // Physically based Standard lighting model, and enable shadows on all light types
    20.         #pragma surface surf Standard fullforwardshadows vertex:vert
    21.  
    22.         // Use shader model 3.5 target, to get nicer looking lighting and texture array support
    23.         #pragma target 3.5
    24.  
    25.         // texture arrays are not available everywhere,
    26.         // only compile shader on platforms where they are
    27.         #pragma require 2darray
    28.  
    29.         UNITY_DECLARE_TEX2DARRAY(_MainTex);
    30.  
    31.         struct Input
    32.         {
    33.             float2 uv_MainTex;
    34.             float arrayIndex; // cannot start with “uv”
    35.             float4 color: COLOR; // TODO could remove this if not using VertexColor and Texture2DArray together
    36.         };
    37.  
    38.         half _Glossiness;
    39.         half _Metallic;
    40.         fixed4 _Color;
    41.  
    42.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    43.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    44.         // #pragma instancing_options assumeuniformscaling
    45.         UNITY_INSTANCING_BUFFER_START(Props)
    46.             // put more per-instance properties here
    47.         UNITY_INSTANCING_BUFFER_END(Props)
    48.  
    49.         void vert(inout appdata_full v, out Input o)
    50.         {
    51.             o.uv_MainTex = v.texcoord.xy;
    52.             o.arrayIndex = v.texcoord.z;
    53.             o.color = v.color;
    54.         }
    55.  
    56.         void surf(Input IN, inout SurfaceOutputStandard o)
    57.         {
    58.             // Albedo comes from a texture tinted by color
    59.             fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(IN.uv_MainTex, IN.arrayIndex)) * _Color;
    60.             o.Albedo = c.rgb * IN.color; // Combine normal color with the vertex color
    61.  
    62.             // Metallic and smoothness come from slider variables
    63.             o.Metallic = _Metallic;
    64.             o.Smoothness = _Glossiness;
    65.             o.Alpha = c.a;
    66.         }
    67.         ENDCG
    68.     }
    69.  
    70.     FallBack "Diffuse"
    71. }
    72.  
     
    ModLunar and flogelz like this.
  7. flogelz

    flogelz

    Joined:
    Aug 10, 2018
    Posts:
    141
    I just implemented this like 3 hours ago too- So were probably both on the same track here. I'm not sure if you eventually will stumble upon the same problem, but I'm just gonna link this thread here:

    https://forum.unity.com/threads/materialpropertyblock-changes-in-editor.715901/

    Basically what my problem with arrays is, that i can't preview them in edit mode (along with other values i set via code, but this probably doesn't affect you as much).
     
  8. Skotrap7

    Skotrap7

    Joined:
    May 24, 2018
    Posts:
    125
    Does anyone know how to sample the tex2darray from the vertex function? I keep getting compile errors trying to use the macro "UNITY_SAMPLE_TEX2DARRAY"

    Error:
    cannot map expression to vs_4_0 instruction
     
  9. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    IIRC you can do this via using the LOD version of the macro -- you'll need to calculate/supply the LOD yourself.
     
  10. Skotrap7

    Skotrap7

    Joined:
    May 24, 2018
    Posts:
    125
    Cool. thanks, that did get rid of the compile error. I'm a bit new at this, but does the LOD level equate to the mip map to use? i.e. If I have an array with 0 mip maps I would just always sample LOD 0?
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
  12. z3nth10n

    z3nth10n

    Joined:
    Nov 23, 2013
    Posts:
    55
    Hello, sorry for the late response.

    I want to implement something similar that you achieved here.

    I pass vertex attributes into a custom shader that allows per parameter a Texture2DArray.

    But I'm unsure how to achieve it.

    This is the shader code:

    Code (CSharp):
    1. Shader "MultiTriplanar"
    2. {
    3. Properties
    4. {
    5.   _Top("Top Main Texture", 2DArray) = "" { }
    6.   _Color("Color", Color) = (1,1,1,1)
    7.   _Glossiness("Smoothness", Range(0,1)) = 0.5
    8.   _Metallic("Metallic", Range(0,1)) = 0.0
    9.   _ZOffset("Z Buffer Offset", Float) = 0
    10. }
    11.    
    12. SubShader
    13. {
    14.   Tags { "RenderType" = "Opaque" }
    15.   LOD 200
    16.   Offset[_ZOffset],[_ZOffset]
    17.  
    18.    CGPROGRAM
    19.   #pragma surface surf Standard fullforwardshadows vertex:vert
    20.   #pragma vertex vert
    21.   #pragma require 2darray
    22.  
    23.   struct Input
    24.   {
    25.    float2 uv_MainTex;
    26.    float arrayIndex;
    27.   };
    28. UNITY_DECLARE_TEX2DARRAY(_Top);
    29.  
    30.   half _Glossiness;
    31.   half _Metallic;
    32.   fixed4 _Color;
    33.  
    34.   void vert(inout appdata_full v, out Input o)
    35.   {
    36.    o.uv_MainTex = v.texcoord.xy;
    37.    o.arrayIndex = v.texcoord.z;
    38.   }
    39.  
    40.   void surf(Input IN, inout SurfaceOutputStandard o)
    41.   {
    42.    fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_Top, float3(IN.uv_MainTex, IN.arrayIndex)) * _Color;
    43.    o.Albedo = c.rgb;
    44.  
    45.    o.Metallic = _Metallic;
    46.    o.Smoothness = _Glossiness;
    47.    o.Alpha = c.a;
    48.   }
    49.   ENDCG
    50. }
    51.   FallBack "Diffuse"
    52. }
    This is how I pass the data into the mesh buffer data, I need help with this part:

    Code (csharp):
    1.  
    2. public class PlaneVerticesTest : MonoBehaviour
    3. {
    4.     [StructLayout(LayoutKind.Sequential)]
    5.     private struct BiomeVertexLayout
    6.     {
    7.         public Vector3 texcoord;
    8.     }
    9.  
    10.     public Texture2DArray texArray;
    11.  
    12.     private void Start()
    13.     {
    14.         var textureCount = texArray.depth;
    15.  
    16.         var layout = new[]
    17.         {
    18.             new VertexAttributeDescriptor(VertexAttribute.TexCoord0)
    19.         };
    20.         mesh.SetVertexBufferParams(mesh.vertexCount, layout);
    21.  
    22.         var data = Enumerable.Range(0, mesh.vertexCount).Select(i => new Vector3(mesh.uv[i].x, mesh.uv[i].y, i % textureCount)).ToArray();
    23.         mesh.SetVertexBufferData(data, 0, 0, mesh.vertexCount);
    24.     }
    25. }
    26.  
    This is the project: https://drive.google.com/file/d/1wJ9QyvkSA_rBAXfN1rFowDpWY5EhqEDV/view?usp=sharing