1
\$\begingroup\$

I'm trying to render a generated mesh using libGDX which acts as a java wrapper for openGL. The issue i'm having can be seen in the screenshot below. The faces of the mesh that should be hidden by closer faces are being drawn on top of the closer faces.

There is probably something really obvious that i'm missing but i cant quite work out what it is. How do i get the draw order/culling to be correct.

Weird face culling

The methods that generate my indices, vertices and normals can be found below.

public TerrainChunk(int width, int height, int vertexSize, String heightMapTexture) {

    if ((width + 1) * (height + 1) > Short.MAX_VALUE) {
        throw new IllegalArgumentException(
                "Chunk size too big, (width + 1)*(height+1) must be <= 32767");
    }

    this.heightMap = new float[(width + 1) * (height + 1)];
    this.width = (short) width;
    this.height = (short) height;
    this.vertices = new float[heightMap.length * vertexSize];
    this.indices = new short[width * height * 6];
    this.vertexSize = vertexSize;

    buildHeightmap(heightMapTexture);

    buildIndices();
    buildVertices();

    calcNormals(indices, vertices);

}

public void buildHeightmap(String pathToHeightMap) {

    FileHandle handle = Gdx.files.internal(pathToHeightMap);
    Pixmap heightmapImage = new Pixmap(handle);
    Color color = new Color();
    int idh = 0;

    for (int x = 0; x < this.width + 1; x++) {
        for (int y = 0; y < this.height + 1; y++) {
            Color.rgba8888ToColor(color, heightmapImage.getPixel(x, y));
            this.heightMap[idh++] = color.r;
        }
    }
}

public void buildVertices() {
    int heightPitch = height + 1;
    int widthPitch = width + 1;

    int idx = 0;
    int hIdx = 0;
    int strength = 100; // multiplier for height map

    float scale = 4f;

    for (int z = 0; z < heightPitch; z++) {
        for (int x = 0; x < widthPitch; x++) {

            // POSITION
            vertices[idx++] = scale * x;
            vertices[idx++] = heightMap[hIdx++] * strength;
            vertices[idx++] = scale * z;

            // NORMAL, skip these for now
            idx += 3;

            // COLOR
            vertices[idx++] = Color.WHITE.toFloatBits();

            // TEXTURE
            vertices[idx++] = (x / (float) width);
            vertices[idx++] = (z / (float) height);

        }
    }
}

private void buildIndices() {
    int idx = 0;
    short pitch = (short) (width + 1);
    short i1 = 0;
    short i2 = 1;
    short i3 = (short) (1 + pitch);
    short i4 = pitch;

    short row = 0;

    for (int z = 0; z < height; z++) {
        for (int x = 0; x < width; x++) {
            indices[idx++] = i1;
            indices[idx++] = i2;
            indices[idx++] = i3;

            indices[idx++] = i3;
            indices[idx++] = i4;
            indices[idx++] = i1;

            i1++;
            i2++;
            i3++;
            i4++;
        }

        row += pitch;
        i1 = row;
        i2 = (short) (row + 1);
        i3 = (short) (i2 + pitch);
        i4 = (short) (row + pitch);
    }
}

// Gets the index of the first float of a normal for a specific vertex
private int getNormalStart(int vertIndex) {
    return vertIndex * vertexSize + positionSize;
}

// Gets the index of the first float of a specific vertex
private int getPositionStart(int vertIndex) {
    return vertIndex * vertexSize;
}

// Adds the provided value to the normal
private void addNormal(int vertIndex, float[] verts, float x, float y, float z) {

    int i = getNormalStart(vertIndex);

    verts[i] += x;
    verts[i + 1] += y;
    verts[i + 2] += z;
}

/*
 * Normalizes normals
 */
private void normalizeNormal(int vertIndex, float[] verts) {

    int i = getNormalStart(vertIndex);

    float x = verts[i];
    float y = verts[i + 1];
    float z = verts[i + 2];

    float num2 = ((x * x) + (y * y)) + (z * z);
    float num = 1f / (float) Math.sqrt(num2);
    x *= num;
    y *= num;
    z *= num;

    verts[i] = x;
    verts[i + 1] = y;
    verts[i + 2] = z;
}

/*
 * Calculates the normals
 */
private void calcNormals(short[] indices, float[] verts) {

    for (int i = 0; i < indices.length; i += 3) {
        int i1 = getPositionStart(indices[i]);
        int i2 = getPositionStart(indices[i + 1]);
        int i3 = getPositionStart(indices[i + 2]);

        // p1
        float x1 = verts[i1];
        float y1 = verts[i1 + 1];
        float z1 = verts[i1 + 2];

        // p2
        float x2 = verts[i2];
        float y2 = verts[i2 + 1];
        float z2 = verts[i2 + 2];

        // p3
        float x3 = verts[i3];
        float y3 = verts[i3 + 1];
        float z3 = verts[i3 + 2];

        // u = p3 - p1
        float ux = x3 - x1;
        float uy = y3 - y1;
        float uz = z3 - z1;

        // v = p2 - p1
        float vx = x2 - x1;
        float vy = y2 - y1;
        float vz = z2 - z1;

        // n = cross(v, u)
        float nx = (vy * uz) - (vz * uy);
        float ny = (vz * ux) - (vx * uz);
        float nz = (vx * uy) - (vy * ux);

        // normalize(n)
        float num2 = ((nx * nx) + (ny * ny)) + (nz * nz);
        float num = 1f / (float) Math.sqrt(num2);
        nx *= num;
        ny *= num;
        nz *= num;

        addNormal(indices[i], verts, nx, ny, nz);
        addNormal(indices[i + 1], verts, nx, ny, nz);
        addNormal(indices[i + 2], verts, nx, ny, nz);
    }

    for (int i = 0; i < (verts.length / vertexSize); i++) {
        normalizeNormal(i, verts);
    }
}

The draw method can also be found below:

modelView.set(camera.view);

    terrainTexture.bind();

    shader.begin();

    //Gdx.gl.glEnable(GL20.GL_CULL_FACE);
    //Gdx.gl.glCullFace(GL20.GL_BACK);
    Gdx.gl.glDepthFunc(GL20.GL_LESS);

    shader.setUniformMatrix("u_MVPMatrix", camera.combined);
    shader.setUniformMatrix("u_normalMatrix", normalMatrix.set(modelView).inv().transpose());

    shader.setUniform3fv("u_lightPosition", new float[]{0,100,0}, 0, 3);
    shader.setUniform4fv("u_ambientColor", new float[]{1,1,1,1}, 0, 4);
    shader.setUniform4fv("u_diffuseColor", new float[]{1,1,1,1}, 0, 4);
    shader.setUniform4fv("u_specularColor", new float[]{1,1,1,1}, 0, 4);

    shader.setUniformi("u_texture", 0);

    mesh.render(shader, GL20.GL_TRIANGLES);

    shader.end();

Vertex Shader:

attribute vec4 a_position;  +
attribute vec3 a_normal;  
attribute vec2 a_texCoord;  
attribute vec4 a_color;  

uniform mat4 u_MVPMatrix;  
uniform mat3 u_normalMatrix;  

uniform vec3 u_lightPosition;  

varying float intensity;  
varying vec2 texCoords;  
varying vec4 v_color;  

void main() {  
vec3 normal = normalize(u_normalMatrix * a_normal);  
vec3 light = normalize(u_lightPosition);  
intensity = max( dot(normal, light) , 0.0);  

v_color = a_color;  
texCoords = a_texCoord;  

gl_Position = u_MVPMatrix * a_position;  
};

Fragment Shader:

#ifdef GL_ES  
precision mediump float;  
#endif  

uniform vec4 u_ambientColor;  
uniform vec4 u_diffuseColor;  
uniform vec4 u_specularColor;  

uniform sampler2D u_texture;  
varying vec2 texCoords;  
varying vec4 v_color;  

varying float intensity;  

void main() {  
    gl_FragColor = v_color * intensity * texture2D(u_texture, texCoords);  
};
\$\endgroup\$
-1
\$\begingroup\$

I'd comment but I dont have the rep yet. I've not worked with libGDX but I presume somewhere that you have to enable Z writes on your terrain draw.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the recommendation, i'll have a look into this. \$\endgroup\$
    – Lex Webb
    Sep 16 '15 at 10:26
  • \$\begingroup\$ Downvote for not having rep but potentially helping? eesh \$\endgroup\$
    – RobM
    Oct 2 '16 at 19:55

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.