How do I render multiple textures in modern OpenGL?

You need to learn about the difference of texture units and texture objects.

Texture units are like "texture cartridges" of the OpenGL rasterizer. The rasterizer has a limited amount of "cartridge" slots (called texture units). To load a texture into a texture unit you first select the unit with glActiveTexture, then you load the texture "cartridge" (the texture object) using glBindTexture.

The amount of texture object you can have is only limited by your systems memory (and storage capabilities), but only a limited amount of textures can be "slotted" into the texture unit at the same time.

Samplers are like "taps" into the texture units. Different samplers within a shader may "tap" into the same texture unit. By setting the sampler uniform to a texture unit you select which unit you want to sample from.

And then you can also have the same texture "slotted" into multiple texture units at the same time.

Update (some clarification)

I read that the texture limit of GL_TEXTURE is dependent on the GPU but it is at least 45. What if I want to render an image that consists of more than 45 textures for example 90?

Normally you don't try to render the whole image with a single drawing call. It's practically impossible to catch all variations on which textures to use in what situation. Normally you write shaders for specific looks of a "material". Say you have a shader simulating paint on some metal. You'd have 3 textures: Metal, Paint and a modulating texture that controls where metal and where paint is visible. The shader would then have 3 sampler uniforms, one for each texture. To render the surface with that appearance you'd

  • select the shader program to use (glUseProgram)
  • for each texture activate in turn the texture unit (glActiveTexture(GL_TEXTURE_0+i) and bind the texture ('glBindTexture`)
  • set the sampler uniforms to the texture units to use (glUniform1i(…, i)).
  • draw the geometry.

The question is somewhat broad, so this is just a quick overview of some options for using multiple textures in the same draw call.

Bind to multiple texture units

For this approach, you bind each texture to a different texture unit, using the typical sequence:

glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, tex[i]);

In the shader, you can have either a bunch of separate sampler2D uniforms, or an array of sampler2D uniforms.

The main downside of this is that you're limited by the number of available texture units.

Array textures

You can use array textures. This is done by using the GL_TEXTURE_2D_ARRAY texture target. In many ways, a 2D texture array is similar to a 3D texture. It's basically a bunch of 2D textures stacked on top of each other, and stored in a single texture object.

The downside is that all textures need to have the same size. If they don't, you have to use the largest size for the size of the texture array, and you waste memory for the smaller textures. You'll also have to apply scaling to your texture coordinates if the sizes aren't all the same.

Texture atlas

This is the idea you already presented. You store all textures in a single large texture, and use the texture coordinates to control which texture is used.

While a popular approach, there are some technical challenges with this. You have to be careful at the seams between textures so that they don't bleed into each other when using linear sampling. And while this approach, unlike texture arrays, allows for different texture sizes without wasting memory, allocating regions within the atlas gets a little trickier with variable sizes.

Bindless textures

This is only available as an extension so far: ARB_bindless_texture.

Tags:

Opengl