Most efficient way to use multiple textures with instancing

i personally favour array textures, there is only 1 obvious “problem”: constant resolution for all layers
1 way to get around that problem is re-texturing of all models you use, and make them fit into (let’s say) 1024 x 1024 RGBA
(that would be ~4MB per texture layer)

with that appproach, you can even switch textures within 1 mesh (instance):
–> stream a “int mapKd” as vertex attribute to the vertex shader, pass it to the fragment shader and access your array texture with that index

i draw all of my meshes as GL_TRIANGLES, the vertices look like this:
struct Vertex {
vec3 Position;
vec2 TexCoord;
vec3 Normal;
int MaterialIndex;
};

i’m using “Material” structs in my fragmentshader, all materials (put into a buffer) are bound to an uniform block
materials look like that: (GLSL)

struct Material {
    vec4 Ka, Kd, Ks;
    float Ns;
    float d;
    int map_Kd;
};

layout (std140, binding = 1) uniform MaterialBlock
{
    Material Materials[MAX_MATERIALS];
};

uniform sampler2DArray textureKd;

void main()
{
// ...

int map_Kd = Materials[vs_out.materialindex].map_Kd;
vec3 Kd = Materials[vs_out.materialindex].Kd.rgb * texture(textureKd, vec3(vs_out.texcoord, map_Kd)).rgb;
}

the streamed vertex attribute “int MaterialIndex” is used to access the correct material
that material itself has then the texture index “int map_Kd” which is use to access the correct texture (diffuse)
that way i can enable / disable textures within 1 mesh, just by avoiding a uniform “switch ON/OFF” variable
if a certain face doesnt need textures at all, the texture index will be 0 and the first layer of the tarray texture is completely white

if you want to use different textures for the same instanced rendered mesh, stream another instanced “int override_mapKd” to the vertex shader and pass it instead of the “int MaterialIndex”

RESTRICTIONS:
– of course all textures have the same resolution
– maximum number of materials per drawcall is limited (GL_MAX_UNIFORM_BLOCK_SIZE bytes per block)
(but i think openGl requires at least 16KB, that is ~250 different materials per drawcall)
(my [old] NVIDIA GT 640 has 65536 bytes, that means about 1000 different materials / drawcall)