1
\$\begingroup\$

I'm working on a game that has to render a large amount of cubes (voxels) with OpenGL. All cubes have the same geometry (so I can re-use the vertex position VBO) and a single sprite sheet texture is used for all of them. This means the texture coordinates will have to vary for the cubes, depending on which sprite to use. All six sides of a cube will use the same sprite (in other words, the same texture coordinates).

Now, what is the best strategy to approach this with a modern OpenGL renderer?

I came up with two possibilities:

  1. Create as many VAOs as there are cube sprites in the sprite sheet. Bind the same vertex position VBO to all of them, but a different texture coordinate VBO (according to what sprite / part of the texture to show). In my specific case, this would make for about 600 different texture coordinate VBOs and therefore 600 VAOs.

  2. Create only one VAO. Set up the texture coordinate VBO in a generic way (e.g. 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0). In the rendering loop, send the actual texture coordinates (only 4 values) as uniform variable to the shaders. In the shader, the generic texture coordinates can be used to figure out which two of the four values from the uniform should be used.

Are there other possibilities that I'm missing? What is best regarding performance?

\$\endgroup\$
5
  • \$\begingroup\$ Are all textures in your sprite sheet the same size? \$\endgroup\$ Commented Aug 17, 2016 at 20:37
  • 1
    \$\begingroup\$ 3. Build a mesh from the cubes instead of trying to render each cube individually. \$\endgroup\$ Commented Aug 17, 2016 at 21:03
  • \$\begingroup\$ @Le Comte du Merde-fou - yes, they are. \$\endgroup\$
    – domsson
    Commented Aug 17, 2016 at 23:46
  • \$\begingroup\$ @domdom - so is there any reason to not use texture arrays then? Which are compatible with modern GL and which will make the problem go away. \$\endgroup\$ Commented Aug 18, 2016 at 8:54
  • 1
    \$\begingroup\$ Option 4: use gl_VertexID to create the generic texture coordinates and use those to adjust the 4 floats. \$\endgroup\$ Commented Aug 18, 2016 at 9:33

1 Answer 1

1
\$\begingroup\$

With modern OpenGL, you can solve this elegantly by using instancing and texture arrays.

Texture arrays
Please see https://www.opengl.org/wiki/Array_Texture for an overview.

Using a texture array you can retain the same texture coords for all faces in all cubes (i.e. 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0) with the only thing you need to specify any differently being which array slice to use. That's where instancing comes in.

Using a texture array also allows you to properly implement mipmapping and texture filtering without having to worry about adjacent textures bleeding into each other, or other issues you typically hit with sprite sheets (which are very OpenGL 1.1).

Instancing
Please see https://www.opengl.org/wiki/Vertex_Rendering#Instancing for an overview.

Because it's part of your design that "All six sides of a cube will use the same sprite (in other words, the same texture coordinates)" you're ideally set up to use instancing. Each cube is an instance, and the array slice is set up as a per-instance vertex attribute, then passed from the vertex shader to the fragment shader for to do the texture lookup.

As an alternative to instancing, you could also send the array slice as a standalone glVertexAttrib1f call, but at the expense of having to make a separate draw call per cube instead of being able to handle all cubes in a single draw call.

\$\endgroup\$
1
  • \$\begingroup\$ I completely forgot about Instancing and didn't know about Array Textures yet. I'm reading up on the latter right now and am already accepting this answer as it looks very promising indeed. Thanks! \$\endgroup\$
    – domsson
    Commented Aug 18, 2016 at 15:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .