2

I'm trying to create a Minecraft look-alike with opengl and have gotten to the point where I can draw a "chunk" of cubes clustered together. However, I can't seem to find a good method for removing all the unseen faces from inside the cluster of cubes (a 16x16x16 cube of cubes).

I've created the basic infrastructure for a single cube (with all face coordinates separated from each other), then copy all the cube information and draw it with glm::translate and drawVertexArrays.

e.g.: for the back face

float cubeMapTest1[] =
{
    // Back face
    -0.5f, -0.5, -0.5f,  0.0f, 0.0f, // Bottom-left
     0.5f, -0.5f, -0.5f,  1.0f, 0.0f, // bottom-right    
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f, // top-right              
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f, // top-right
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, // top-left
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, // bottom-left  
};

//...

unsigned int VBOs[6], VAOs[6];
glGenVertexArrays(6, VAOs);
glGenBuffers(6, VBOs);

glBindVertexArray(VAOs[0]);

glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeMapTest1), cubeMapTest1, GL_STATIC_DRAW);

// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

//...

//^^^repeat for all 6 faces^^^

//then create a cube of values from 0 to 16^3 to be drawn to later

std::vector<glm::vec3> cubePositions;
int i = 0;
int chunkSize = 16;
int chunkVolume = chunkSize * chunkSize * chunkSize;

for (size_t j = 0; j < chunkSize; j++)
{
    for (size_t k = 0; k < chunkSize; k++)
    {
        for (size_t h = 0; h < chunkSize; h++)
        {
            i++;
            //order of h j k affect layers
            cubePositions.resize(i + 1);
            cubePositions[i] = { h, j, k };
        }
    }
}

//start while loop

//draw

//...

With this, should I be using normals to determine which inner faces to "cull"? What's the best culling method given my goal? I know it's messy and my question is kinda vague, but I don't know where to start with this problem. I'll optimize it later with instancing and indices as well.

  • How about focusing on the cubes instead of the faces? I.e. just do not draw faces of a cube which touches eight other cubes. Or more precisely but possibly slower, not draw those face which touch other cubes. – Yunnosch Jul 09 '19 at 06:23
  • 2
    For each face of a block, if it's neighbor is an opaque block, than the face is culled. – ph3rin Jul 09 '19 at 06:27
  • The reason I do each face individually is so that I can toggle the individual face off if it's in contact with another block, I just don't know how to detect a face is touching another block/face. – HeartUnder8lade Jul 09 '19 at 06:30
  • sort the cubes spatialy so you can convert cube position to its index in the array ... then checking neighbor position will be easy.... binary search is your friend ... you can even make LUT marking the start index of each new row, column, slice to speed up even more. If your world is small or you got huge amount of memory you can use 3D texture to store your world instead ... there checking neighbor position is easy (just +/-1 texel in the texture coordinate) see [volumetric ray trace](https://stackoverflow.com/a/48092685/2521214) for big worlds you need sparse encoding and or BVH or Octtree – Spektre Jul 09 '19 at 07:24
  • @HeartUnder8lade: "*repeat for all 6 faces*" Are you *really* putting each individual face in its own buffer object? ignoring the *massive* memory overhead of creating and using such tiny buffers, that means you need a new rendering command *for each face*. That's going to kill your performance. – Nicol Bolas Jul 09 '19 at 13:25
  • @Spektre: "*sort the cubes spatialy so you can convert cube position to its index in the array*" Why would you need to sort cubes for this? Sparse arrays aren't a great storage mechanism for this kind of data, especially when you're already relying on chunking for partitioning. Each chunk should just have a 3D array of cubes. – Nicol Bolas Jul 09 '19 at 13:33
  • @NicolBolas the example is already spatially ordered in 3D array mapped into 1D I know but its high likely its just simplified example for an MCVE and the real deal might not be sorted at all... – Spektre Jul 09 '19 at 13:37
  • https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/ – genpfault Jul 09 '19 at 14:14
  • @genpfault: Considering that about 80% of that page is spent on an algorithm that assumes things that all blocks are the same (which is a dumb assumption to make if you're actually trying to render like Minecraft), and that the same 80% of the page also produces geometry that may well have cracks due to bad T-junctions, I'd stick with what works. – Nicol Bolas Jul 09 '19 at 16:30

1 Answers1

1

The algorithm itself is pretty simple. For each cube in a chunk, for each face of the cube, check the neighboring cube in that direction (within the chunk. Don't bother culling based on adjacent chunks). If that neighboring cube exists, then cull the face.

To put this in another way, what you're doing is building the array of vertex data for a specific chunk. For each cube, you add some number of vertices to this array, based on which faces of that cube are not culled. So you check the 6 adjacent cubes (again, within the same chunk) and add vertices for that face if there is no cube there.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982