OpenGL has array textures, denoted in shaders by specific sampler types:
sampler2DArray array_texture;
But GLSL also allows samplers to be aggregated into arrays:
sampler2D array_of_textures[10];
Are these two features related to each other? How are they different?
Let's understand the distinction by analogy. Samplers in GLSL are like pointers in C++; they reference some other object of a given type. So consider the following C++ code:
int* pi;
std::array<int, 5>* pai;
std::array<int*, 5> api;
pi
is a pointer to a single object of type int
(let's ignore the fact that technically it could be a pointer to an array of int
s).
pai
is also a pointer. But it doesn't point to an int
; it points to an array
of int
s. That "array
of int
s" is a single object, with a single contiguous allocation of storage.
api
is not a pointer; it is an array. Specifically, it is an array of pointers to int
s. Each individual pointer can point to individual int
objects. Each individual pointer is independent from the rest, and the objects they point to are completely unrelated, save for the fact that they all must be int
s.
What does this have to do with OpenGL and textures?
pi
is like sampler2D
.
pai
is like sampler2DArray
: a single pointer/sampler which references multiple int
/2D textures that all reside in a single object.
api
is like a sampler2D[]
: a single name which represents multiple pointers/samplers, each of which is independently bound, referencing objects that are unrelated save for the fact that they all must be int
/sampler2D
s.
An array texture is a distinct construct from a non-array texture. Array textures have a special texture target. Accessing array textures in GLSL requires explicitly using a distinct sampler type that matches the target. Texture accessing functions that take array textures take an extra texture coordinate component, providing the array layer to be accessed. When you assign a texture to an array texture sampler, you are assigning a single texture object, with whatever number of array layers it was created with.
By contrast, a sampler array is simply a compile-time sized collection of multiple independent samplers grouped under one name. Textures are assigned to locations in the array independently, with any texture of the appropriate type being able to be used. Each element of the array takes up a additional binding point.
The biggest difference besides resource consumption is this: you can pick a sampler from the array using a runtime index (in OpenGL 4.x; in pre-4.x, you had to use a compile-time constant. So sampler arrays were basically useless).
But not an arbitrary runtime index. You can only use an index which is a dynamically uniform expression. That is, for all invocations in the drawing command, each invocation that executes that instruction must result in the same index.
Array textures have no such indexing limitations; the array index you provide them in texture fetching commands can be any runtime value (within the array, of course).
But array textures do have other limitations. Every "texture" in an array texture has the same size; so if you create a 512x512x20 2D array texture, each sub-texture is 512x512. For sampler arrays, the size of each texture in the array can vary. Of course, the fact that each sampler array index takes up a binding point is also important; you only have 16 of those per-stage (though possibly more; 16 is the minimum requirement).