【问题标题】:LibGDX: How can I achieve a flat shaded look with OpenGL 2.0?LibGDX:如何使用 OpenGL 2.0 实现平面阴影效果?
【发布时间】:2015-10-13 00:54:59
【问题描述】:

所以我正在尝试使用 LibGDX 实现“多边形艺术/低多边形”风格。我首先构建一个由三角形组成的模型。

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

问题是,当我希望地形像这样平坦的阴影时,地形是 Gouraud 阴影:

我知道对于更高版本的 OpenGL,glsl 中有一个“flat”关键字,它将禁用顶点之间的颜色插值。根据我在网上和这篇文章中阅读的内容: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;
}

现在:

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

问题是新代码没有渲染任何东西...我查看了 ModelBuilder、MeshBuilder、Mesh 和 VertexAttribute/s 的 API,但我不知道为什么它不起作用。任何帮助都会很棒,因为这是非常令人沮丧的一天。非常感谢!

【问题讨论】:

标签: java opengl libgdx glsl opengl-2.0


【解决方案1】:

平坦和平滑的阴影通常由顶点法线以及颜色在整个面部的插值方式决定。

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

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

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

下图显示了平面法线和平滑法线的区别。较深的蓝色线代表平均平滑法线,较浅(青色)线代表粗糙面法线。

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

【讨论】:

  • 如果每个顶点的法线都需要与面的法线相匹配,那么多个面共享的顶点如何与所有面共享法线呢?感谢您的快速回复:)
  • @JahrudZ 这是个好问题,你是对的。如果您使用 glDrawElements 样式的数据结构,那么它会被组织为共享顶点数据,并且您无法在每个顶点处指定唯一的法线,但是对于此模型,您也无法指定大多数 3D 模型通常需要的唯一 UV 坐标。您可以改为为每个面复制顶点和法线数据并使用 glDrawArrays 进行绘制。如果这没有意义,我可以详细说明。
  • @JahrudZ 在您的示例中,您似乎已经在使用非索引 glDrawArrays 样式的数据结构,因此您应该能够修改 CalcNormal 函数。
  • CalcNorm 函数只返回 3 个点的 Vector3(三角形上的 3 个点)。问题是我的“新”代码没有渲染任何内容:(它只是一个空白屏幕
  • @JahrudZ 是的,没问题。让我更清楚地重新解释共享顶点三角形的概念。现在在您的代码中,您为每个三角形定义了 3 个顶点。这意味着您实际上在相同位置有多个顶点(每个相邻三角形一个顶点)以及每个顶点处的多个法线。对于平均平滑法线,共享位置的所有顶点的法线相同。对于平面法线,它们对于每个面都会有所不同。
猜你喜欢
  • 2016-06-03
  • 2012-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-01
  • 2012-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多