Texture arrays

Hi,

I’ve tried Unity 5.4 beta yesterday and its Texture array support in shaders to see the shader finally compiles when using them.

However I’ve been unable to set a texture array from script (I’m just trying to deinterleave/reinterleave a sample texture). Will there be a Material.SetTextureArray method or something like that ?

Texture2DArray inherits from Texture, so you should be able to use Material.SetTexture with it.

1 Like

I’ll try this. However I’m dubious as Material.SetTexture is expecting a Texture…
Should I pass it the first texture from the array ?

public static void loadTextures(){
        mainTexture = new Texture2DArray(MyProject.BASE_TILE_SIZE, MyProject.BASE_TILE_SIZE, MyProject.ATLAS_IMAGECOUNT*MyProject.ATLAS_IMAGECOUNT, TextureFormat.RGBA32, false);
        mainTexture.filterMode = FilterMode.Point;
        mainTexture.wrapMode = TextureWrapMode.Clamp;

        List<string> keyList = new List<string>(textureBank.Keys);
        Texture2D badTex = Resources.Load ("Textures/invalid_texture") as Texture2D;
        for(int i = 0; i<keyList.Count; i++){
            Texture2D smallTex = Resources.Load ("Textures/"+keyList[i]) as Texture2D;
            if(smallTex==null){
                Debug.Log("Textures/"+keyList[i]+" not found.");
                mainTexture.SetPixels32(badTex.GetPixels32(), textureBank[keyList[i]]);
            } else {
                mainTexture.SetPixels32(smallTex.GetPixels32(), textureBank[keyList[i]]);
            }
        }

        mainTexture.Apply();
        terrainMaterial.SetTexture("_MainTex",mainTexture);
    }

I use this in my project - creates the texture array, fills it with images loaded from resources(have to be readable) and sets the texture to be used by the terrain.

1 Like

I appreciate the help! thank you :slight_smile:

However in my case I output a RenderTexture array from a shader pass:

  private void RenderMRT (RenderBuffer[] colorBuffers, RenderBuffer depthBuffer, int pass) {

     Graphics.SetRenderTarget (colorBuffers, depthBuffer);
     GL.Clear (false, true, Color.clear);
     GL.PushMatrix ();
     GL.LoadOrtho ();
     _ShaderMaterial.SetPass (pass);

     // Render the full screen quad manually.
     GL.Begin (GL.QUADS);
     GL.TexCoord2 (0.0f, 0.0f);
     GL.Vertex3 (0.0f, 0.0f, 0.1f);
     GL.TexCoord2 (1.0f, 0.0f);
     GL.Vertex3 (1.0f, 0.0f, 0.1f);
     GL.TexCoord2 (1.0f, 1.0f);
     GL.Vertex3 (1.0f, 1.0f, 0.1f);
     GL.TexCoord2 (0.0f, 1.0f);
     GL.Vertex3 (0.0f, 1.0f, 0.1f);
     GL.End ();

     GL.PopMatrix ();
   }

But doing the following is not allowed :

  RenderTexture[] mrt = new RenderTexture[8];
  RenderBuffer[] mrtRB = new RenderBuffer[8];
  for (int i = 0; i < mrtRB.Length; i++) {
       mrt[i] = RenderTexture.GetTemporary (IntDivUp(source.width, 4), IntDivUp(source.height, 4), 0, RenderTextureFormat.RFloat);
       mrtRB [i] = mrtTex [i].colorBuffer;
  }
  ...
  RenderTexture oldRT = RenderTexture.active;
  RenderMRT (mrtRB, mrt[0].depthBuffer, 2); // pass 2 outputs 8 render textures.
  RenderTexture.active = oldRT;
  ...
  _ShaderMaterial.SetTexture ("_TexArray", mrt); // DOES NOT WORK !!!

My goal is to deinterleave a texture from a shader pass (this part works as expected) then reinterleave it in another pass (this is where I’m unable to pass back the RenderTexture array to the shader)

Anyone ?

You need to copy the contents of rendertextures into the texture array - texture array is NOT array of texture objects.
After rendering stuff to the textures, just copy those contents into the texture array by using GetPixels and SetPixels to a particular layer of the array texture.

EDIT: It looks like You’re using 8 textures here - can’t you pass 8 textures to a material? Never done more than 6 so that’s why I’m asking.

Sounds like an overkill. I’ll loose all benefit of interleaving…

I can pass the texture separately sure, but once again I’ll loose all benefit of interleaving for which the goal is to read the slice you need in the shader pass and hence optimize the cache utilization:
https://developer.nvidia.com/sites/default/files/akamai/gameworks/samples/DeinterleavedTexturing.pdf
This article is talking about 2x2 deinterleaving while I’m trying 4x4 deinterleaving but the method is the same.

In fact, I’m working on a SSAO post effect for which I’m deinterleaving the depth into 16 quarter resolution textures (requires 2 shader pass each giving 8 render texures), calculate AO for each layer (separable kernel) then reinterleave the result into a full resolution texture (this is where I need to read the slices).

Would be great if Material.SetTexture could set a RenderTexture array.

So as I understand it, You want to render to texture on GPU and put it into a texture array without it leaving the GPU.
I read somewhere a writeup about this, but it was “deep magic” as in - it was using raw OpenGL calls to do this and I would assume that’s what You’ll have to do.
I have no idea about the details of Your project, but I would try to use a simple Texture atlas for this, if possible.
If not, try to use the raw OpenGL route and see where that gets You. One guy implemented TextureArrays this way before it was introduced to Unity and the only thing he was missing was the GC compiler things responsible for the sampler. Try looking that up and it might give You an idea about how to solve Your issues. Beware of platform differences though.

Sorry I can’t help much more, but that’s as far as I’ve ever gone into Unity :slight_smile:

Cheers,
Siv.

1 Like

Hi,

I’ve been trying the whole morning to use the new Texture2DArrays, but though I am properly creating them in my scripts, I am not able to compile any Shader that uses them.
How are you guys declaring the samplers in your shader code?
I’ve tried with “sampler2DARRAY” but the compiler does not know what it means.
Same thing with the texture fetch function, I am not able to make the compiler recognize the “tex2DARRAY” function.

Thanks in advance,

Alejandro

I guess you can do:

UNITY_DECLARE_TEX2DARRAY(name)
UNITY_SAMPLE_TEX2DARRAY(name,coord) // coord is float3

source: https://gist.github.com/aras-p/a3aeac5c84ce233f6787

On my side I simply have declared it as follow:

Texture2DArray<float4> _MyTexArray

Thank you for your help!

In fact I already had a workaround for 2x2 interleaving, I used to pass 4 separate textures to the shader that needs to reinterleave the texture, and I used branching depending on the offset to read the texture slice accordingly.
This worked fine, however for 4x4 interleaving you get 16 textures to fetch so it’s an overkill to workaround and honestly I would prefer to use a regular texture array functionality which should be more efficient.

Would be far more easier if one could set a texture array from a RenderTexture array directly:

Material myShaderMaterial;
RenderTexture[] myRenderTextureArray;
....
myShaderMaterial.SetTexture("_MyTexArray", myRenderTextureArray);

This would open the texture arrays functionality to those who wants to use advanced techniques like deinterleaving instead of restricting it to texture atlas / terrain textures…

Unity guys, any word on this ?

Hi,

Thanks for replying but this isn’t helping.
The compiler still does not recognize any of those tokens.

In any case, what I am asking for is just a simple example where you use the native support for Texture2DArray that the new 5.4 beta version of Unity is suppose to give.
As I said, I am able to create the texture in C# scripts using the new Texture2DArray class, but there is no clue at all about how to use them in the shaders.

Thanks,

Alejandro

Declaring a Texture2DArray in the shader SHOULD work as it’s what I’m using and shader is compiling fine.
Make sure you are targetting the proper Shader Model.

Declaring a shader input for texture array (in properties): _MainTex (“Texture”, 2DArray) = “white” {}
Declaring a sampler: UNITY_DECLARE_TEX2DARRAY(name)
Using a sampler: half4 baseColor = UNITY_SAMPLE_TEX2DARRAY(_MainTex, i.UV);

So what jimmikaelkael gave You was all that’s needed:)

Hi again,

Though I really appreciate your help (the only one I am finding right now), I think we are still having some missunderstanding or missing pieces.
The Macros you are telling me to use are not working at all (the compiler keeps complaining about syntax error with those lines).
So, for the sake of clarity and for future readers of this thread, can we summarize all that is needed in order to make Texture2DArray work in Unity 5.4.0b10 ?
What is the proper Shader Model to target? I am using 5.0.
What is the shader compiler needed and how to change it? I am writing in Cg.
What platforms are supported? I guess only d3d11, although OpenGL should work too…
And why the hell should we use this kind of macros? Shouldn’t they be built-in tokens such as “Sampler2DARRAY” and “tex2DARRAY” ?? I can’t understand what this is still not in Unity :frowning:

Thanks again in advance,

Cheers,

Alejandro

Hi,

I am also encountering issues with the Texture2DArray and shaders. I’ve been following the steps given by jimmikaelkael and sivael (thanks both for your valuable help) and the result is that I am able to declare a Texture2DArray in a custom vertex and fragment shader, but not in a surface shader. I mean, in the surface shader it gives a sintax error, so I guess this is the issue abeacco is refering to.

Please, find attached both shaders in case you want to try. The surface shader (not compiling):

Shader "Custom/Tex2DArray" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _Tex2DArray ("Tex2DArray (RGB)", 2DArray) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
       
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 4.0

        sampler2D _MainTex;
        UNITY_DECLARE_TEX2DARRAY(_Tex2DArray);

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

And the custom vertex and fragment shader (compiling fine):

Shader "Unlit/Tex2DArray2"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Tex2DArray ("Tex2DArray (RGB)", 2DArray) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
           
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            UNITY_DECLARE_TEX2DARRAY(_Tex2DArray);
            float4 _MainTex_ST;
           
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
           
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

So is there something I am missing in the case of the surface shader? Maybe an include or a pragma? Or it is simply a bug of the current beta version that will be adressed in a future release?

Thanks in advance!

@Ramon-Oliva
In the surface shader it seems you’re not including UnityCG.cginc

Hi,

I’ve already tried this, but it still does not work. Even I’ve tried to include the specific file where this macro is declared (HLSLSupport.cginc) and it says the same, sintax error at the line corresponding to UNITY_DECLARE_TEX2DARRAY(_Tex2DArray);

Also notice that I’ve forced to compile using pragma target 4.0, as I’ve readed in the releases notes that you need at least target 3.5 in order to get TextureArrays to work.

So anyone has been able to compile a surface shader with a Texture2DArray property? And if this is the case, could you put a simple example? Maybe an official Unity developer could shed light on this issue in order to know if it is a Unity bug or something am I missing.

Hm.
Doesn’t look like it’s working very well - the surface shader compilation pipeline seems to be breaking somewhere along the way if using texture arrays anywhere.
You could extract the final vertex and fragment shaders and use those with Your modifications, I guess.

I’ve seen the same issue of texture arrays not working under a surface shader. I can live with that for now but what’s more concerning to me is why I’m seeing half the FPS in my terrain shader now by just switching from 10 texture samples to 2 texture arrays of 5 diffuse & normal maps. I’m in the process of simplifying the shader to further troubleshoot and compare against, but was wondering if anyone else has noticed and can confirm a similar drop in performance by using the Texture2DArray?
Unity ver: Beta 10