Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I render multiple textures in modern OpenGL?

Tags:

opengl

I am currently writing a 2d engine for a small game.

The idea was that I could render the whole scene in just one draw call. I thought I could render every 2d image on a quad which means that I could use instancing.

I imagined that my vertex shader could look like this

...
in vec2 pos;
in mat3 model;
in sampler2d tex;
in vec2 uv;
...

I thought I could just load a texture on the gpu and get a handle to it like I would do with a VBO, but it seems it is not that simple.

It seems that I have to call

glActiveTexture(GL_TEXTURE0..N);

for every texture that I want to load. Now this doesn't seem as easy to program as I thought. How do modern game engines render multiple textures?

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?

It seems that I would have to render the first 45 textures and delete all the texture from the gpu and load the other 45 textures from the hdd to the gpu. That doesn't seem very reasonable to do every frame. Especially when I want to to animate a 2D image.

I could easily think that a simple animation of a 2d character could consist of 10 different images. That would mean I could easily over step the texture limit.

A small idea of mine was to combine multiple images in to one mega image and then offset them via uv coordinates.

I wonder if I just misunderstood how textures work in OpenGL.

How would you render multiple textures in OpenGL?

like image 658
Maik Klein Avatar asked Dec 07 '14 17:12

Maik Klein


People also ask

What is a sampler OpenGL?

A sampler is a set of GLSL variable types. Variables of one of the sampler types must be uniforms or as function parameters. Each sampler in a program represents a single texture of a particular texture type. The type of the sampler corresponds to the type of the texture that can be used by that sampler.

How do textures work in OpenGL?

A texture is an OpenGL Object that contains one or more images that all have the same image format. A texture can be used in two ways: it can be the source of a texture access from a Shader, or it can be used as a render target.

How do you implement a texture map?

To get texture mapping working you need to do three things: load a texture into OpenGL, supply texture coordinates with the vertices (to map the texture to them) and perform a sampling operation from the texture using the texture coordinates in order to get the pixel color.


2 Answers

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.

like image 96
Reto Koradi Avatar answered Oct 18 '22 01:10

Reto Koradi


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.
like image 45
datenwolf Avatar answered Oct 18 '22 02:10

datenwolf