Jump to content

  • Log In with Google      Sign In   
  • Create Account

voodoodrul

Member Since 19 Jun 2012
Offline Last Active Nov 16 2013 12:45 PM

Topics I've Started

glTranslatef() very far from origin results in gaps between neighboring translates

12 November 2013 - 11:40 AM

It's a plain old Minecraft clone ("oh no.. not another one of these..."), chunked rendering, "infinite" terrain. I have a 2D plane of WorldChunk objects. Each chunk is converted to a minimal mesh of exposed faces and stored as a VBO. Once the chunk is ready for render, I might need to place it somewhere very far from the origin, because this chunk is at, say, (16384, 16384). What happens in my rendering is that gaps appear between the chunks and, as I pan my camera around, each chunk jitters a bit due to lack of precision in absolute placement. Each chunk might jitter around 1-2 pixels, sometimes landing exactly lined up with its neighbor, other times 2 pixels offset with a gap. The problem gets worse as you go farther from the origin. Near the origin, such as below (4096,4096) the problem is not obvious. At the origin is it not possible to spot the problem at all. I know there must be limited precision in translates, so I need to do things in a fundamentally different way.

 

For each chunk, when it has a VBO that is ready to draw, I do:

 

glPushMatrix();

glTranslatef(16384.0f, 0.0f, 16384.0f);

....

glDrawArrays(GL_TRIANGLES, 1, this.numVerts);

glPopMatrix();

 

I thought about making my translates more like a "local world" where I simply place them relative to the viewer, but it makes a few other things difficult. I'm sure I'm doing this wrong by trying to use very large glTranslatef() values relative to the origin. 

 

Any advice out there? 

 

The rendering test app is @ https://voodoo.arcanelogic.net/CYDI-latest.jar if anyone cares to look at it, but it would take a while to fly far enough away from the origin to notice big gaps and jitter

 

Thanks!


Procedural terrain generation - "stitching" noise together

14 July 2013 - 11:04 AM

I'm sure I'm coming at this from the wrong angle, but I wanted to know, if you use a chunked rendering scheme and Simplex or Perlin 2D or 3D noise, what's the best way to create a continuous map without ever building the entire world map? Should I offset the base seed by adding the chunk's position (x,y) to the seed and build a new map for that chunk? If I do that, the two neighboring maps won't blend with each other.

 

I thought about blending the two maps using another pass that takes map A's edge values and seeds the neighbor's edge map with the same ones, but I think I just don't know how to use noise maps properly. 

 

In my mind, I have a limited amount of space to store one complete 2D perlin noise map. I use that for the entire world, but clearly that's not going to get me to "infinite".. 

 

Any help is appreciated!


Java garbage collection - waits for idle time?

11 July 2013 - 08:37 PM

So I have a LWJGL renderer. It spends all of it time rendering. Imagine that. It seems that garbage collection waits until the last possible second to run and it's becoming problematic. Even triggering a System.gc() request to perform GC doesn't seem to help. If I stop moving around in my renderer, which won't need to create new large objects, it eventually seems to clean itself up.. 

 

Should I deliberately halt the renderer, for maybe 100 msec, to convince java to perform GC? 

 

Profiling my app doesn't seem to help much. I see a metric crapload of object allocations, but that's not "objects that are still alive" and looking at the heap doesn't show an obvious memory leak.. 


Reusing VBOs

05 July 2013 - 11:06 AM

I have an odd problem. My render() code does this

render() {
...
    if (rebuildMesh == true) {
        buildMesh();
    }
...
}


buildMesh() {
....
            if (this.vboVertexHandle == 0) {
                this.vboVertexHandle = glGenBuffers();
            } 
            glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
            glBufferData(GL_ARRAY_BUFFER, this.vbuffer, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);

...
}

It creates a buffer if one doesn't exist, binds it, and buffers the data. 

 

The problem comes later when another render() call decides to update the mesh. It creates the vertex buffer correctly and then tries to upload the data into the existing vboVertexHandle. This seems to result in a corrupted VBO with fragments of the original vertex data somehow "transposed" onto the existing buffer. 

 

I was under the impression that glBufferData(GL_ARRAY_BUFFER, this.vbuffer, GL_STATIC_DRAW) would replace the entire contents of the buffer, but it doesn't appear to. It seems to prepend the new data onto the buffer?

 

I need to flush and upload all new data to the VBO. The VBO holds a mesh that changes infrequently.

 

This code works:

buildMesh2() {
....
            if (this.vboVertexHandle == 0) {
                this.vboVertexHandle = GL15.glGenBuffers();
            } else {
                GL15.glDeleteBuffers(vboVertexHandle);
                this.vboVertexHandle = GL15.glGenBuffers();
            }
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboVertexHandle);
            GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.vbuffer, GL15.GL_STATIC_DRAW);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
...
}

But that seems wasteful to delete and gen a whole new buffer.. 


Optimization of many small glDrawElements() calls

30 June 2013 - 08:17 PM

So I realize there are many better ways to do this in modern OpenGL. But here's what I have: 

class World
....
function render() {
   foreach(chunk)
        if (chunk.isReady && chunk.isVisible) {
              chunk.render()
        }
}

class WorldChunk
public int[][][] blocks;
...
function render() {
    if (DISPLAY_LIST != 0) {
         glCallList(DISPLAY_LIST);
    } else {
         buildDisplayList();
    }
}
function buildDisplayList() {
        int list = GL11.glGenLists(1);   
        GL11.glNewList(list, GL11.GL_COMPILE);    
        pushVertexData();
        GL11.glEndList();  
}
function pushVertexData() {
        glPushMatrix();
        glTranslatef(x,y,z);

        glBindBuffer(GL_ARRAY_BUFFER, Block.vboVertexHandle);  //From a single static vbo stored in Block class - VBO built once on startup
        glVertexPointer(3, GL_FLOAT, 0, 0L);
        glBindBuffer(GL_ARRAY_BUFFER, Block.vboNormalHandle);  //From a single static vbo stored in Block class - VBO built once on startup
        glNormalPointer(GL_FLOAT, 0, 0L);
        
        GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
        GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);

        for (int i = 0; i < sizeX; i++) {
            for (int j = 0; j < sizeY; j++) {
                for (int k = 0; k < sizeZ; k++) {
                    //Determine exposed faces..
                    EXPOSED_FACES = determineExposedFaces(i,j,k);
                    //Contains boolean array [true, true, true, true, true, true] of faces to draw if they are not hidden
                    //
                    Block.render(i,j,k, EXPOSED_FACES);
                }
            }
        }
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);
        glPopMatrix();
}


class Block
...
function render(int x, int y, int z, int type, boolean[] faces) {
if (faces[0] || faces[1] || faces[2] || faces[3] || faces[4] || faces[5]) {
            glPushMatrix();                                        
            glTranslatef(x + size, y + size, z + size);     
            if (faces[0]) {
                glDrawElements(GL_TRIANGLES, frontIndicies);
            }
            if (faces[1]) {
                glDrawElements(GL_TRIANGLES, rightIndicies);
            }
            if (faces[2]) {
                glDrawElements(GL_TRIANGLES, topIndicies);
            }
            if (faces[3]) {
                glDrawElements(GL_TRIANGLES, leftIndicies);
            }
            if (faces[4]) {
                glDrawElements(GL_TRIANGLES, bottomIndicies);
            }
            if (faces[5]) {
                glDrawElements(GL_TRIANGLES, backIndicies);
            }

            glPopMatrix();
        }
}
 

As you can see, I'm basically just storing a single VBO of the cube and using indicies to decide which faces to actually draw. The problem is that I know this is not an optimal way to do things. I am currently:

 

1) Rendering only those chunks within +/- X, +/- Y units of the camera

2) Doing efficient frustum culling to decide which chunks to are outside the viewing area

3) Using display lists to "bake" a chunk's geometry

4) Using a single VBO for all cubes

 

In any given scene I might have only 20k-40k faces being drawn in actual on-GPU geometry, but even this is fairly slow. Even on a GTX 690, I can only sustain about 80fps with about 50k faces on frame.

 

I know I should switch to shaders and do this the true modern way, but is there a fundamental concept I'm abusing and causing such poor performance? I know that performance is not substantially consumed elsewhere in the code based on profiling the app. For example, I hold at 1650 fps if I just comment out each of the glDrawElements() calls above so nothing is drawn. I must be overwhelming the card with poorly scheduled draw calls on tiny vertex arrays...


PARTNERS