5
votes

I'm trying to draw a car for a game I'm making using OpenGL. The car object is stored in an .obj file with an accompanying .mtl file.

The car has 500+ vertices, 100+ faces and needs over 50 different textures applied to them, using my own vertex and fragment shader. I've gone about this by using buffer objects, which sends all information about my object at once. My problem is trying to apply different textures to different surfaces of the same object.

The answers I've found to this problem are to bind a texture to a certain texture number (i.e. GL_TEXTURE0, GL_TEXTURE1, etc...) and pass in a Sampler2D to the fragment shader referencing it. However, can I store over 50 textures in this way? As far as I can tell it goes to GL_TEXTURE32.

Also, how would I tell the shader to only apply a texture to a certain face of the car object?

3

3 Answers

10
votes

You cannot render the model this way, where you bind all the textures at once. A fragment shader, even for high-end GL 4.1-class hardware, can only access 16 textures. That is, it can only have 16 samplers.

The total texture count is higher because other shader stages can use textures as well.

The correct way to deal with it is to do one of the following:

  1. Return to your modeller and have them reduce the texture count of the model.
  2. Have a tool build a texture atlas (ie: put all of the images into a single texture), changing the model's texture coordinates appropriately. Depending on the hardware you plan to support, this can be a large 2D texture or a 2D texture array. The latter will require a 3rd value for the texture coordinate: the index of the image in the array.
  3. Sort the triangles by texture. For each texture, render all of the triangles that use that texture.

#3 is the only solution that doesn't require changing data. But be warned: if you render this car often (say, thousands of times per frame) the state change overhead can be a performance issue. Some combination of #1 and #2 is the generally preferred method.

BTW, if your model only has a few hundred faces, but 50 textures, then it's likely something went horribly wrong in the modelling process.

2
votes

It's not possible to use an arbitrary number of textures just like that, not in a straightforward way.

There are basically 3 solutions:

  • Use fewer textures
    • Pack several individual textures into an atlas. This requires you to adjust texture coordinates and take care at mipmap creation (though a decent tool will do that for you automatically). You may need to be careful to leave a sufficient border for anisotropic filtering too.
    • Pack several individual textures into one array texture. No problems with mipmaps and aniso, but needs hardware support (GL 3.1 cards)
  • Split up your model
    • More draw calls and many more state changes and pipeline stalls per car (bad for performance)
    • Solution: Draw e.g. all car bodies in one batch, all tires in one batch, and all lights in another to reduce state changes
  • Use detail texturing and fewer "overall low detail" textures
    • similar to how you do texturing on a terrain
    • combine in the pixel shader to give each car an individual appearance
0
votes

Switching 50 textures for a single object will quickly turn into a low-performance nightmare. Use a texture atlas.

Multitexturing is meant to provide multiple texture layers, like a colour layer, a bump layer, shinyness, etc. But they are not to be used for replacement of an atlas.

Also if your model consists of parts with different material, split it up. Each material in its own mesh.