Efficient method for rendering cubes with different textures on each side for a Minecraft-like game?

StackOverflow https://stackoverflow.com/questions/9152359

  •  22-04-2021
  •  | 
  •  

Question

I'm trying to decide what the most efficient way to render a bunch of cubes with different textures in a Minecraft-like game is.

I discovered instanced rendering. What I've done is I've created a single "cube model" which stores all the vertices, normals, and texture coordinates for a cube, I create an array buffer out of that and pass it to the GPU once. Then I created a array of (translation vector, texture index) structs and I use instanced rendering to redraw the same cube over and over, each time translated and with the appropriate texture.

(Hopefully Notch doesn't mind me using his textures until I make my own)

The problem is that not all 6 sides will always have the same texture, and I'm trying to figure out how I can get them to be different for each block type. The two solutions I've come up with are:

  1. Use a different Model for each block type. This way I can specify different texture coordinates on each of the vertices. I can still use instanced rendering, but I'd have do a separate pass for each block type.
  2. Pass 6 "texture indexes" (1 for each face) instead of 1 for every block.

The 2nd solution requires passing a lot more, possibly redundant, data. I'm not sure how great the benefits of instanced rendering are... so I don't know if it would be better to do, say, up to 256 "passes" (1 for each block type), or "one big pass" with all the data, and render every block in one shot.

Or perhaps there's another method I'm not aware of?

Was it helpful?

Solution

I don't think you can do it efficiently with instances. Vast majority of faces/cubes is never visible and you can save a lot of time by not rendering them. Unfortunately that makes every cube a different object.

The standard solution (and how it's done in Minecraft) is to divide your terrain into sectors . Compute which faces are visible and upload them to GPU. When a cube changes you just need to re-upload it's sector. When rendering a sector you just draw primitives without any other computations.

You can do something based on sparse voxel octrees. It's much more work, but you would be able to efficiently and accurately tell which parts of your world are visible.

OTHER TIPS

I know this question is almost two years old, but may I would make a 3D texture which stores all of the individual textures, where the z texture coordinate would be sorta like the block ID. With the 3D texture, you can now bind all of your individual block textures at once, meaning you can use instanced rendering to pass in you transformations along with a blockID to grab the correct block texture for the 3D sampler.

On my nVidia 8600M GT I found out, that instancing performs best "in the middle" with moderate vertex and instance counts, but I ended up instancing a couple of vertices thousands of times to eliminate redundant data along with the effort to update it.

I'd choose 2, using a texture array along with a single, instanced cube in the vertex array and select the face texture using your texture indices of your 'per instance array', where the 6 indices may even be packed into few integers. For supplying instance attribs, GL_ARB_instanced_arrays can also be of use, where one does not need to access a buffer using gl_InstanceID (predictable and therefore faster in most cases). If you need to have instance specific texture coords, I'd bind an additional per instance and vertex texture coord array, along with an accordingly modified shader.

Really late answer, but somebody's bound to need it.

Have a method in the main block class that returns the texture, with a parameter for the face. In the individual classes that need multiple textures, override this method and use a switch case or a series of if/else statements.

This is what the method would look like in the block class:

public int getBlockTexture(int face){
     if(face = top){
         return grass top
     } else if(face = bottom){
         return grass bottom
     } else {
         return grass side
     }
}

As for how you use this in the renderer, grab the texture before you render each face. Similar to how you do culling.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top