LibGDX:如何使用OpenGL 2.0实现平面阴影外观?

Jah*_*udZ 3 java opengl glsl libgdx opengl-2.0

所以我试图用LibGDX实现"Polygon Art/Low Poly"风格.我首先构建一个由三角形组成的模型.

1

然后使用顶点着色器,我根据高度计算每个顶点的颜色.

2

问题是,当我希望它是平面阴影时,地形是Gouraud阴影:

3

我知道,对于更高版本的OpenGL,glsl中有一个"平面"关键字,它将禁用顶点之间的颜色插值.从我在网上和这篇文章中读到的内容:http://i.stack.imgur.com/DrNx9.jpg,我认为我需要让地形中的每个三角形彼此分开?我还需要计算每个三角形的法线?我无法理解其他StackOverflow中的代码,但这是我试图做的:

原版的

public Model getWorld(){
    returnWorld = new Model();
    modelBuilder = new ModelBuilder();
    modelBuilder.begin();
    worldMeshBuilder = modelBuilder.part("worldPart", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, new Material());
    pieceMeshBuilder = new MeshBuilder();
    meshPiece = new Mesh(false, 3, 3, 
            new VertexAttribute(Usage.Position, 3, "a_position"), 
            new VertexAttribute(Usage.Normal, 3, "a_normal"), 
            new VertexAttribute(Usage.ColorPacked, 4, "a_color"));
    Vector3 vectorCopy = new Vector3();
    for(int i = 0; i < world.length - 1; i++){
        for(int j = 0; j < world[0].length - 1; j++){
            if((i + j) % 2 == 0){
                pieceMeshBuilder.begin(Usage.Position | Usage.Normal, renderType);
                pieceMeshBuilder.triangle(
                    vectorCopy = verticies[i][j],
                    vectorCopy = verticies[i][j + 1],
                    vectorCopy = verticies[i + 1][j + 1]
                );
                worldMeshBuilder.addMesh(pieceMeshBuilder.end());
                pieceMeshBuilder.begin(Usage.Position | Usage.Normal, renderType);
                pieceMeshBuilder.triangle(
                        vectorCopy = verticies[i + 1][j + 1],
                        vectorCopy = verticies[i + 1][j],
                        vectorCopy = verticies[i][j]
                );
                worldMeshBuilder.addMesh(pieceMeshBuilder.end());
            } else {
                pieceMeshBuilder.begin(Usage.Position | Usage.Normal, renderType);
                pieceMeshBuilder.triangle(
                    vectorCopy = verticies[i][j],
                    vectorCopy = verticies[i][j + 1],
                    vectorCopy = verticies[i + 1][j]
                );
                worldMeshBuilder.addMesh(pieceMeshBuilder.end());
                pieceMeshBuilder.begin(Usage.Position | Usage.Normal, renderType);
                pieceMeshBuilder.triangle(
                        vectorCopy = verticies[i + 1][j + 1],
                        vectorCopy = verticies[i + 1][j],
                        vectorCopy = verticies[i][j + 1]
                );
                worldMeshBuilder.addMesh(pieceMeshBuilder.end());
            }
        }
    }
    returnWorld = modelBuilder.end();
    return returnWorld;
}
Run Code Online (Sandbox Code Playgroud)

现在:

public Model getWorld(){
    returnWorld = new Model();
    modelBuilder = new ModelBuilder();
    modelBuilder.begin();
    worldMeshBuilder = modelBuilder.part("worldPart", GL20.GL_LINES, Usage.Position | Usage.Normal, new Material());

    for(int i = 0; i < world.length - 1; i++){
        for(int j = 0; j < world[0].length - 1; j++){

            Vector3 normal1 = calcNormal(verticies[i][j], verticies[i + 1][j], verticies[i + 1][j + 1]);
            Vector3 normal2 = calcNormal(verticies[i][j], verticies[i + 1][j + 1], verticies[i][j + 1]);

            if((i + j) % 2 == 0){
                meshPiece = new Mesh(false, 18, 3, 
                        new VertexAttribute(Usage.Position, 3, "a_position"), 
                        new VertexAttribute(Usage.Normal, 3, "a_normal")//, 
                        //new VertexAttribute(Usage.ColorPacked, 4, "a_color")
                        );
                worldMeshBuilder.addMesh(meshPiece.setVertices(new float[] {
                        verticies[i][j].x, verticies[i][j].y, verticies[i][j].z, normal1.x, normal1.y, normal1.z,
                        verticies[i + 1][j].x, verticies[i + 1][j].y, verticies[i + 1][j].z, normal1.x, normal1.y, normal1.z, 
                        verticies[i + 1][j + 1].x, verticies[i + 1][j + 1].y, verticies[i + 1][j + 1].z, normal1.x, normal1.y, normal1.z,
                    }));
                meshPiece = new Mesh(false, 18, 3, 
                        new VertexAttribute(Usage.Position, 3, "a_position"), 
                        new VertexAttribute(Usage.Normal, 3, "a_normal")//, 
                        //new VertexAttribute(Usage.ColorPacked, 4, "a_color")
                        );
                worldMeshBuilder.addMesh(meshPiece.setVertices(new float[] {
                        verticies[i][j].x, verticies[i][j].y, verticies[i][j].z, normal2.x, normal2.y, normal2.z,
                        verticies[i + 1][j + 1].x, verticies[i + 1][j + 1].y, verticies[i + 1][j + 1].z, normal2.x, normal2.y, normal2.z, 
                        verticies[i][j + 1].x, verticies[i][j + 1].y, verticies[i][j + 1].z, normal2.x, normal2.y, normal2.z,
                    }));
            } else {
                meshPiece = new Mesh(false, 18, 3, 
                        new VertexAttribute(Usage.Position, 3, "a_position"), 
                        new VertexAttribute(Usage.Normal, 3, "a_normal")//, 
                        //new VertexAttribute(Usage.ColorPacked, 4, "a_color")
                        );
                worldMeshBuilder.addMesh(meshPiece.setVertices(new float[] {
                        verticies[i][j].x, verticies[i][j].y, verticies[i][j].z, normal1.x, normal1.y, normal1.z,
                        verticies[i + 1][j].x, verticies[i + 1][j].y, verticies[i + 1][j].z, normal1.x, normal1.y, normal1.z, 
                        verticies[i][j + 1].x, verticies[i][j + 1].y, verticies[i][j + 1].z, normal1.x, normal1.y, normal1.z,
                    }));
                meshPiece = new Mesh(false, 18, 3, 
                        new VertexAttribute(Usage.Position, 3, "a_position"), 
                        new VertexAttribute(Usage.Normal, 3, "a_normal")//, 
                        //new VertexAttribute(Usage.ColorPacked, 4, "a_color")
                        );
                worldMeshBuilder.addMesh(meshPiece.setVertices(new float[] {
                        verticies[i + 1][j].x, verticies[i + 1][j].y, verticies[i + 1][j].z, normal2.x, normal2.y, normal2.z,
                        verticies[i + 1][j + 1].x, verticies[i + 1][j + 1].y, verticies[i + 1][j + 1].z, normal2.x, normal2.y, normal2.z, 
                        verticies[i][j + 1].x, verticies[i][j + 1].y, verticies[i][j + 1].z, normal2.x, normal2.y, normal2.z,
                    }));
            }
        }
    }
    returnWorld = modelBuilder.end();
    return returnWorld;
}
Run Code Online (Sandbox Code Playgroud)

问题是新代码没有呈现任何内容......我已经查看了ModelBuilder,MeshBuilder,Mesh和VertexAttribute/s的API,但我无法弄清楚它为什么不起作用.任何帮助都会很棒,因为这是非常令人沮丧的一天.非常感谢!

Jus*_*ers 6

平坦和平滑的阴影通常由顶点法线以及如何在面部内插颜色来确定.

通常在平滑模型中,每个顶点的法线对于相同位置处的每个面点进行平均.每个共享顶点位置的法线对于每个面都是相同的.这使得照明平滑,因为边缘没有突然变化.

在平面着色模型中,顶点法线与相邻面不匹配,而是每个顶点法线与面法线相同.这会在边缘处产生正常值的突然变化.

这通常可以在您使用的任何3D建模包中进行修改,或者在程序生成地形时稍微修改您的正常生成代码.

下图显示平坦法线和平滑法线之间的差异.较深的蓝线代表平均的平滑法线,而较浅的(青色)线代表粗糙的脸法线.

顶点vs面

您应该能够在一次绘制调用中将所有三角形绘制在一起.您不需要拆分网格.

  • @JahrudZ这是一个很好的问题,你是对的.如果使用glDrawElements样式数据结构,则它被组织为共享顶点数据,并且您无法在每个顶点指定唯一法线,但是使用此模型时,您也无法指定大多数3D模型通常所需的唯一UV坐标.您可以改为复制每个面的顶点和法线数据,并使用glDrawArrays进行绘制.如果这没有意义,我可以详细说明. (2认同)