3
\$\begingroup\$

I am making a voxel game in OpenGL and I currently render 8 x 8 chunks to the screen. The first thing I do is generate the map using Simplex Noise and this is done once when the chunk is created. I store each block in a 3D array contained in each chunk. After the chunk is created I then call a function that checks each block to see if it is next to an air block, if it is then it is added to a separate array of visibleBlocks as is, otherwise an empty air block is added to its place (There is probably a better way of doing this). Once I have an array of blocks of which are visible, I call a function that returns a mesh which holds all of the vertices, colour data, normals, etc into 3 big VBOs (one for each) rather than rendering each block to the screen with its own vbo (of which I couldn't even render one chunk properly).

public static Mesh genChunkMesh(Block[][][] visibleBlocks)
{
    ArrayList<Float> vertexData = new ArrayList<Float>(); //Storage for the chunk vertices
    ArrayList<Float> colourData = new ArrayList<Float>(); //Storage for the colours
    ArrayList<Integer> indexData = new ArrayList<Integer>(); //Storage for the indices
    ArrayList<Float> normalData = new ArrayList<Float>(); //Storage for the normals

    int blockNum = 0; 
    for(int x = 0; x < CHUNK_WIDTH; x++)
    {
        for(int y = 0; y < CHUNK_HEIGHT; y++)
        {
            for(int z = 0; z < CHUNK_WIDTH; z++)
            {
                //Grab the mesh for a given BlockType from some statically defined blocks
                Mesh blockMesh = Chunk.getBlockMesh(visibleBlocks[x][y][z]);

                if(blockMesh != null)
                {
                    float[] vertices = blockMesh.getVertices();
                    float[] colours = blockMesh.getColours();
                    int[] indices = blockMesh.getIndices();
                    float[] normals = blockMesh.getNormals();

                    for(int i = 0; i < vertices.length; i += 3)//Add the next x, y, z position to the vertex data
                    {
                        vertexData.add(vertices[i] + visibleBlocks[x][y][z].getPosition().x);
                        vertexData.add(vertices[i + 1] + visibleBlocks[x][y][z].getPosition().y);
                        vertexData.add(vertices[i + 2] + visibleBlocks[x][y][z].getPosition().z);           
                    }

                    for(int i = 0; i < colours.length; i++)
                    {
                        colourData.add(colours[i]);
                    }

                    for(int i = 0; i < indices.length; i++)
                    {
                        indexData.add(indices[i] + (blockNum * 24)); //Indices range from 0-23, so every block 24 is added to each index
                    }

                    for(int i = 0; i < normals.length; i++)
                    {
                        normalData.add(normals[i]);
                    }
                    blockNum++;
                }
            }
        }
    }

    return new Mesh(Util.toFloatArray(vertexData), Util.toIntArray(indexData), Util.toFloatArray(colourData), Util.toFloatArray(normalData), grassMaterial);
}

I have a world object which holds these 8 x 8 chunks (the reason for 8 x 8 is because if I do anymore it begins to lag and each chunk being 16 * 16 * 128 blocks. Also I am implementing infinite terrain but at the moment I want to make sure a finite terrain of 8 x 8 works first). Each chunk has its own mesh which itself has one vao and 3 vbos (vertex, colour, normal) which is only generated once when the chunks are created and eventually will be regenerated everytime the chunk is updated (haven't got to this yet). In my render method the chunk array is looped through and each mesh is rendered through:

    //Bind the VAO
    glBindVertexArray(vaoID);
    glEnableVertexAttribArray(0); //Vertex Data
    glEnableVertexAttribArray(1); //Colour data
    glEnableVertexAttribArray(2); //Normal data

    glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_INT, 0);

    //Restore state
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glBindVertexArray(0);

Now that you have some context, my question is, why do I get consistent 60 fps when I am outside of the world

Terrain

However when I move inside a chunk below the terrain, I get stuttering frames that jump around 30 fps.

Inside the terrain I am unsure of what I am doing wrong that is causing this to happen, I have been searching for similar problems online but I do not know how to word it as it is weird. Also as you can see in the first image, weird lines appear at far distances between the blocks. I have a directional light that is pointing straight down and I don't know if it is because I am rendering faces between two solid cubes and the top face is brighter than the side faces. Another thing I should mention is that glfwSwapBuffers(1) is called at the end of each render call and it is causing a considerable amount of time to finish (around 0.01 to 0.05 seconds per call).

tldr: As shown in the pictures, I get consistent 60 fps when outside/above the terrain, but when I am inside of the terrain, the fps stutters around 20-30 fps and I don't know why.

\$\endgroup\$
  • \$\begingroup\$ Weird lines are called moi·re (/mwär/) and yes they're due to gaps between voxels which you can avoid by joining colocated vertices (suggest you write it alongside the existing approach, so you can revert to the older method for comparison / performance tests). Other than that, you should be getting a lot more than 60FPS at all times, though this doesn't explain the discrepancy. Also, have a look here for tools you can use to look into your performance drop. \$\endgroup\$ – Engineer Jun 26 '17 at 5:38
  • \$\begingroup\$ It is weird because it is as if the lag is proportional to the amount of blocks in view and how close I am to those blocks. :/ \$\endgroup\$ – AtomProgrammer Jun 26 '17 at 5:47
  • \$\begingroup\$ Hm. I saw this in the early days of my own work with voxels, but that was a long time ago and I cannot recall the reason. Are you using glEnable(GL_CULL_FACE)? You should not be using double-sided faces. Your chunks should ideally consist of nothing but the outward facing surfaces of the entire chunk, and you should have absolutely no interstitial faces when they anyway can't be seen from outside. I also suggest trying something like OpenGL dev performance tools to see what is happening with draw calls etc. \$\endgroup\$ – Engineer Jun 26 '17 at 5:49
  • \$\begingroup\$ Yeah I have enable GL_CULL_FACE enabled. So far I am rendering all of the visible cubes for each chunk and I am working on only drawing the visible faces so it would be interesting to see the difference that makes! It is really weird why distance to the blocks plays a factor, I can understand that the lag is proportional to the number of cubes in view that OpenGL is rendering but why distance plays a part. Would it be because the closer to the blocks I am the larger the triangles OpenGL has to draw are and that is what's causing it? \$\endgroup\$ – AtomProgrammer Jun 26 '17 at 6:02
  • \$\begingroup\$ I remember thinking the same - "how weird!". May have to do with near-plane culling. What is your camera's near plane distance set to? I think the default is generally either 1 (usually considered as 1 metre) or 0.1 (10cm). You'll probably find the moment it culls enough in front of you, many more surfaces have to render. If you put it down to 0.001 or so you can test this theory. The size of the triangles makes no difference; indeed viewing a couple of triangles close up generally speeds up rendering for the reason just given - it occludes the rest. \$\endgroup\$ – Engineer Jun 26 '17 at 6:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.