Setting texture array

I need to set an array of textures in my GLSL shader from Unity, but seems there is no support for this?

In my shader:

uniform sampler2D textureArray[512];

Then setting the array with this:

for (int i=0;i<512;i++)
        mySharderMaterial.SetTexture("textureArray"+i, textures[i]);

But this does not work, and I tried many other variants as well. How to do this in Unity?

I am aware of Texture2DArray / sampler2DArray, but those cannot be used in this case as the textures will need to be in many different formats (size and type). I also tried to use individual textures and then select them using a long range of if’s, but this also does not work as my shader is very complex and the GPU ran out of instructions. The shader works fine with arrays as described above, but I cannot find a way to set the array elements in Unity.

Will I need to write an OpenGL wrapper myself to do this, or is there a sensible way to set textures in an array in Unity?

You can’t do this even if you do write a wrapper. GLSL doesn’t support arrays of textures, at least not really.

You can write a shader with:

uniform sampler2D textureArray[16];

And that will compile for OpenGL targets, as long as the number there is no more than 16 (or less depending on the hardware). But that’s because the shader compiler is flattening that out to the equivalent of this at compile time:

uniform sampler2D textureArray0;
uniform sampler2D textureArray1;
uniform sampler2D textureArray2;
uniform sampler2D textureArray3;
// etc
uniform sampler2D textureArray15;

And you can’t dynamically iterate over the list in the shader, you can only use constant indices since it isn’t actually an array.

What you’re looking to do is bindless or dynamic indexing, which are features of Direct3D 12, Vulkan, and Metal. It’s also supported in OpenGL 4.4 on Nvidia GPUs via an extension. But afaik Unity does not support bindless or dynamic index resources, so you’d have to do all the work in a native plugin.

But if you’re targeting OpenGL … you’re probably out of luck.

@bgolus Thank you for your reply, and what you are saying is what I assumed when I went into this. But it does not seem to be true, at least not for the latest drivers / GPUs. Let me explain in more detail:

What I tried first is what you said:

uniform sampler2D textureArray0;
uniform sampler2DArray textureArray1;
...
uniform sampler2DArray textureArray31;

vec4 textureArrayLookup(vec2 p,int textureID)
{
    int slot=textureID>>16;
    int layer=textureID&0xffff;
    vec3 uvz=vec3(p.xy,layer);

    if (slot==0) return texture(textureArray0, uvz);
    if (slot==1) return texture(textureArray1, uvz);
...
    if (slot==31) return texture(textureArray31, uvz);
    return vec4(1,0,0,1);    //Undefined slot
}

Note I am actually using arrays of sampler2DArray, but the issue is the same as for sampler2D.

As my complete shader is very complex (it is a high end GPU raytracer), after the compiler has inlined the method above wherever it is used, it runs out of instructions and I get an error. Reducing the number of slots will fix this since it generates less code, as will reducing the number of references to the method. Unfortunately, this workaround is not possible as I need many slots and multiple references from the inner loop of the raytracer.

However, I noticed that this works without any errors:

uniform sampler2DArray textureArrays[128];

vec4 textureArrayLookup(vec2 p,int textureID)
{
    int slot=textureID>>16;
    int layer=textureID&0xffff;
    vec3 uvz=vec3(p.xy,layer);
    return texture(textureArrays[slot], uvz);
}

So, it tells me that the compiler somehow compiles this to less instructions, even though I am actually using a lot more slots in this example. The issue at hand is I have found no way to initialize the array of sampler2DArray from Unity.

I only need this to work on 2080TI cards by the way, so if it works on my computer it will work in our internal renderfarm. No need to consider compatibility with older GPUs or drivers.

My only guess is that Nvidia’s GLSL compiler is automatically enabling their bindless texture extension. But yeah, afaik there’s no way to assign those textures from within Unity directly.

mm, yes, this is why I turned to this forum to find some answers, on how to work around the problem.

Converting everything to a plugin might be the only way, but that is a huge amount of additional work for the purpose of just setting a shader uniform, especially since unity interfaces so poorly with external SDKs. You actually need to restart Unity for it to recognize a change in a newly compiled plugin dll, which makes it practically impossible to test plugins while developing. We use collaborate, which makes reloading a project take about ten minutes, as it has to use cloud services to check for changes.

Also, the plugin system is not very flexible in terms of when you can call a plugin for rendering, it can only be done in very limited places in the render pipeline, which is a problem in this case as I need to do a raytrace on demand from anywhere, like I can with the current implementation.

I have been considering using SharpGL within Unity, but that seems like a very hacky solution, and I have no idea if that would even work.

It is a bit frustrating as all I need is Material.SetTextureArray, just like Unity already provides Material.SetFloatArray, Material.SetMatrixArray and Material.SetVectorArray. :S

Except, again, this isn’t a feature that’s officially supported by OpenGL. It’s not just setting a material property, it’s having to upload the textures in a special way so the GPU knows it’s a bindless texture. But since Unity doesn’t (yet) support bindless textures, it’s not something it can do.

Here’s the OpenGL documentation on bindless textures:

https://www.khronos.org/opengl/wiki/Bindless_Texture

It’s an extension, so it’s not guaranteed to be available on every GPU and requires textures to be registered differently via the API.