Flat shading in LibGDX

2020-08-01 07:03发布

问题:

I am currently working on a simple game and I would like to my terrain to be flat shaded. My terrain currently looks like the following:

As you can see the colours blend toghether depending on the vertice colours.

I would love for my end product to resemble the following:

So I was wondering how can I achieve this? What are the steps to go from gouraud shading to flat shading?

Here is how I go about creating the vertices I give to my mesh:

public TerrainChunk() {
    buildHeightmap();
    buildIndices();
    buildVertices();
    calcNormals(indices, vertices);

}   

public void buildHeightmap() {...}

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);
    }
}

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

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

            // POSITION
            vertices[idx++] = scale * x;
            vertices[idx++] = (float)Math.pow(1 + chunkDepths[x][z], strength));
            vertices[idx++] = scale * z;

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

            // COLOR
            vertices[idx++] = getColor(;

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

        }
    }
}

/*
 * 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 / VERTEX_SIZE); i++) {
        normalizeNormal(i, verts);
    }
}

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

    int i = getNormalStart(vertIndex);

    float rx = (float) ((x * Math.cos(180)) - (y * Math.sin(180)));
    float ry = (float) ((x * Math.sin(180)) + (y * Math.cos(180)));
    x = rx;
    y = ry;
    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) + (z * z)) + (y * y);
    float num = (float) Math.sqrt(num2);
    x *= num;
    y *= num;
    z *= num;

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


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

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

回答1:

In the end I changed the way I build the Mesh entirely. Now I build the mesh out of triangles. Here's what it looks like now: . it's not yet perfect as there are no diagonal colours but I'm happy with the general look.

Here is how I did it. I made a new class to hold my variables

private Vector3 corner1;
private Vector3 corner2;
private Vector3 corner3;
private Vector3 corner4;
private Vector3 leftNormal;
private Vector3 rightNormal;
private Color color1;
private Color color2;
private Color color3;
private Color color4;
private Vector2 texturePos;

and draw them like so:

// left triangle
VertexInfo v1 = new VertexInfo();
VertexInfo v2 = new VertexInfo();
VertexInfo v3 = new VertexInfo();

// right triangle
VertexInfo v4 = new VertexInfo();
VertexInfo v5 = new VertexInfo();
VertexInfo v6 = new VertexInfo();

v1.setPos(cell.getCorner1()).setNor(cell.getLeftNormal()).setCol(cell.getColor1()).setUV(cell.getTexturePos());
v2.setPos(cell.getCorner4()).setNor(cell.getLeftNormal()).setCol(cell.getColor1()).setUV(cell.getTexturePos());
v3.setPos(cell.getCorner2()).setNor(cell.getLeftNormal()).setCol(cell.getColor1()).setUV(cell.getTexturePos());

v4.setPos(cell.getCorner3()).setNor(cell.getRightNormal()).setCol(cell.getColor3()).setUV(cell.getTexturePos());
v5.setPos(cell.getCorner2()).setNor(cell.getRightNormal()).setCol(cell.getColor3()).setUV(cell.getTexturePos());
v6.setPos(cell.getCorner4()).setNor(cell.getRightNormal()).setCol(cell.getColor3()).setUV(cell.getTexturePos());

meshBuilder.triangle(v1, v2, v3);
meshBuilder.triangle(v4, v5, v6);

I calculate the face normals of the triangles like so:

private Vector3 calcNormal(Vector3 p1, Vector3 p2, Vector3 p3) {

    // u = p3 - p1
    float ux = p3.x - p1.x;
    float uy = p3.y - p1.y;
    float uz = p3.z - p1.z;

    // v = p2 - p1
    float vx = p2.x - p1.x;
    float vy = p2.y - p1.y;
    float vz = p2.z - p1.z;

    // 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;

    return new Vector3(nx, ny, nz);
}


标签: 3d libgdx