Hello everyone, this is my OpenGL ES advanced advanced series of articles, there is a corresponding project on my Github, welcome to follow, link: github.com/kenneycode/…

Texture array is a new feature introduced in OpenGL ES 3.0. It allows you to pass textures into a shader as an array. Let’s look at a normal Fragment shader:

#version 300 es
precision mediump float;
layout(location = 0) out vec4 fragColor;
layout(location = 0) uniform sampler2D u_texture;
in vec2 v_textureCoordinate;
void main() {
    fragColor = texture(u_texture, v_textureCoordinate);
}
Copy the code

In this shader we declare a uniform sampler2D variable, that is, we can only pass one texture. If we want to pass multiple textures, we declare multiple Uniform sampler2D variables:

#version 300es precision mediump float; . layout(location =0) uniform sampler2D u_texture0;
layout(location = 1) uniform sampler2D u_texture1;
layout(location = 2) uniform sampler2D u_texture2; .Copy the code

On the other hand, once the shader is specified, the number of textures supported in the shader is also determined, which is not conducive to any number of textures unless you generate the shader yourself. The appearance of a texture array allows us to pass textures to the shader as if it were an array. Let’s look at the shader using a texture array:

// vertex shader
#version 300 es
precision mediump float;
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec3 a_textureCoordinate;
out vec3 v_textureCoordinate;
void main() {
    v_textureCoordinate = a_textureCoordinate;
    gl_Position = a_Position;
}

// fragment shader
#version 300 es
precision mediump float;
precision mediump sampler2DArray;
layout(location = 0) out vec4 fragColor;
in vec3 v_textureCoordinate;
layout(location = 0) uniform sampler2DArray u_texture;
void main() {
    fragColor = texture(u_texture, v_textureCoordinate);
}
Copy the code

Fragment shader = sampler2D = sampler2DArray = array sampler2DArray = array sampler2DArray = array sampler2DArray = array sampler2DArray Does it look exactly the same as it did without the texture array? In fact, v_textureCoordinate is now a VEC3 instead of a VEC2. We know that texture coordinates are two-dimensional. Here, the third dimension of vec3 is to take the corresponding texture, which can be understood as the subscript of the array. We can also see that the texture array does not need to be declared in the shader first. It is controlled in the code, which makes it very flexible. Let’s see how the code is written. Android OpenGL ES 2.0 texture tutorial (6), the difference is that the first binding texture type is different:

// Create an image texture array
// Create texture for image
val textures = IntArray(1)
GLES30.glGenTextures(textures.size, textures, 0)
imageTexture = textures[0]

// Notice GL_TEXTURE_2D_ARRAY
// Note that the type is GL_TEXTURE_2D_ARRAY
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D_ARRAY, textures[0])
Copy the code

Normal textures are bound to the GL_TEXTURE_2D type, in this case GL_TEXTURE_2D_ARRAY. In addition, we also allocate the size of the texture array as we allocate the space of the array:

GLES30.glTexStorage3D(GLES30.GL_TEXTURE_2D_ARRAY, 1, GLES30.GL_RGBA8, 390.270.2)
Copy the code

For example, in our case, we created a texture array of size 2, each texture is 390 by 270, and the API we’re using here is called glTexStorage3D, and there’s actually another texture called 3D texture, which is a little bit like a texture array, and when we use a texture array, Some apis use the same API for manipulating 3D textures.

Next we load two images into our texture array:

// Specify the texture of each layer with glTexSubImage3D
// Specify the texture of each layer via glTexSubImage3D
for (i in 0 until 2) {
    val bitmap = Util.decodeBitmapFromAssets("image_$i.jpg")
    val b = ByteBuffer.allocate(bitmap.width * bitmap.height * 4)
    bitmap.copyPixelsToBuffer(b)
    b.position(0)
    GLES30.glTexSubImage3D(GLES30.GL_TEXTURE_2D_ARRAY, 0.0.0, i, bitmap.width, bitmap.height, 1, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, b)
    bitmap.recycle()
}
Copy the code

This step is similar to using a single texture using glTexImage2D, in this case glTexSubImage3D and specifying which layer of the texture array is currently loaded.

As we mentioned earlier, the v_textureCoordinate is now a vec3 instead of a VEC2, so the texture coordinates we pass will change accordingly:

// Texture coordinates
// The texture coordinate
private val textureCoordinateData = floatArrayOf(
                                        0f, 1f, 0f, 
                                        0f, 0f, 0f, 
                                        1f, 0f, 0f, 
                                        0f, 1f, 0f, 
                                        1f, 0f, 0f, 
                                        1f, 1f, 0f,
                                        0f, 1f, 1f, 
                                        0f, 0f, 1f, 
                                        1f, 0f, 1f, 
                                        0f, 1f, 1f, 
                                        1f, 0f, 1f, 
                                        1f, 1f, 1f
                                    )
Copy the code

Also, here I set the vertex coordinates to the lower left and upper right corner, so I assign the 0 and 1 texture in the render texture array to the lower left and upper right corner, as follows:

The code is in my Github OpenGLESPro project, this article corresponds to SampleTextureArray, project link: github.com/kenneycode/…

Thanks for reading!