将具有变换矩阵的多个网格合并为 OpenGL 中的单个网格

Merging multiple meshes with transformation matrix into a single mesh in OpenGL

我尝试将具有变换矩阵的多个网格合并为一个网格。 每个网格有4个数据集。

  1. 顶点
  2. 指数
  3. 纹理坐标
  4. 法线

我尝试这样做的方式应该是懒惰的并且不会花费那么多 CPU。 这是一个 3 步过程。

  1. 将每个顶点和法线与变换矩阵相乘。
  2. 将每个网格的顶点、纹理坐标和法线合并成3个大数组。
  3. 将每个网格的索引合并到一个数组中,但使用先前网格的总和作为偏移量。例如:如果网格 1 有 800 个索引,则必须将 800 添加到网格 2 的所有索引中。

这个方法有两个大问题。

  1. 不共享重复顶点
  2. 由于裁剪不可见的部分不会被删除

但这没关系,因为这应该是一个没有太多 CPU 用法的惰性方法。它已经是为草和灌木创建网格的最佳选择。

我已尝试实现此方法,如下所示:

public static final MeshData mergeLazy(List<MeshData> meshes, List<Matrix4f> transformations) {

    int lengthVertices = 0;
    int lengthNormals = 0;
    int lengthTexCoords = 0;
    int lengthIndices = 0;
    ArrayList<Integer> indexLengths = new ArrayList<>();

    for(MeshData mesh : meshes) {

        lengthVertices += mesh.getVertices().length;
        lengthNormals += mesh.getNormals().length;
        lengthTexCoords += mesh.getTextureCoordinates().length;
        int length = mesh.getIndices().length;
        lengthIndices += length;
        indexLengths.add(length);
    }

    float[] vertices = new float[lengthVertices];
    float[] texCoords = new float[lengthTexCoords];
    float[] normals = new float[lengthNormals];
    int[] indices = new int[lengthIndices];

    int iv = 0;
    int ivt = 0;
    int ivn = 0;
    int i = 0;
    int indexLength = 0;

    for(int im = 0; im < meshes.size(); im++) {

        MeshData mesh = meshes.get(im);
        float[] mVertices = mesh.getVertices();
        float[] mTexCoords = mesh.getTextureCoordinates();
        float[] mNormals = mesh.getNormals();
        int[] mIndices = mesh.getIndices();
        Matrix4f transformation = transformations.get(im);

        for(int index = 0; index < mVertices.length; index += 3) {

            Vector3f vertex = MatrixUtil.multiply(transformation, mVertices[index], mVertices[index + 1], mVertices[index + 2]);
            vertices[iv++] = vertex.x;
            vertices[iv++] = vertex.y;
            vertices[iv++] = vertex.z;

            Vector3f normal = MatrixUtil.multiply(transformation, mNormals[index], mNormals[index + 1], mNormals[index + 2]);
            normals[ivn++] = normal.x;
            normals[ivn++] = normal.y;
            normals[ivn++] = normal.z;
        }

        for(int index = 0; index < mTexCoords.length; index++) {

            texCoords[ivt++] = mTexCoords[index];
        }

        for(int index = 0; index < mIndices.length; index++) {

            indices[i++] = indexLength + mIndices[index];
        }

        indexLength += indexLengths.get(im);
    }

    MeshData data = new MeshData();
    data.setIndices(indices);
    data.setNormals(normals);
    data.setTextureCoordinates(texCoords);
    data.setVertices(vertices);

    return data;
}

最后我实际上只有一个网格,变换的乘法也有效....用于旋转和缩放,但问题来了。

与转换相乘对翻译不起作用。 我将矩阵与向量相乘的方法如下所示:

public static final Vector3f multiply(Matrix4f matrix, float x, float y, float z) {

    Vector3f result = new Vector3f();
    result.x = x * matrix.m00 + y * matrix.m01 + z * matrix.m02;
    result.y = x * matrix.m10 + y * matrix.m11 + z * matrix.m12;
    result.z = x * matrix.m20 + y * matrix.m21 + z * matrix.m22;
    return result;
}

第二个问题是第二个网格的纹理有点不对。

这是一张图片:

如您所见,第二个网格只有实际纹理的大约 1/4。

我用来生成这个网格的代码如下所示:

    Material grassMaterial = new Material();
    grassMaterial.setMinBrightness(0.1F);
    grassMaterial.setColorMap(new Texture(new XImgTextureReader().read(new FileInputStream("res/textures/grass2.ximg"))));
    grassMaterial.setAffectedByLight(true);
    grassMaterial.setTransparent(true);
    grassMaterial.setUpwardsNormals(true);
    grassMaterial.setFog(fog);

    MeshData quad = Quad.generateMeshData(
        new Vector3f(0.0F, 1F, 0.0F),
        new Vector3f(0.0F, 0.0F, 0.0F),
        new Vector3f(1F, 0.0F, 0.0F),
        new Vector3f(1F, 1F, 0.0F)
    );

    StaticMesh grassMesh = new StaticMesh(MeshUtil.mergeLazy(Arrays.asList(quad, quad), Arrays.asList(
        MatrixUtil.createTransformationMatrx(
            new Vector3f(0.0F, 0.0F, 0.0F),
            new Vector3f(0.0F, 0.0F, 0.0F),
            new Vector3f(1.0F, 1.0F, 1.0F)
        ),
        MatrixUtil.createTransformationMatrx(
            new Vector3f(0F, 0.0F, -0F),
            new Vector3f(0.0F, 90.0F, 0.0F),
            new Vector3f(1.0F, 1.0F, 1.0F)
        )
    )));
    grassMesh.setCullMode(StaticMesh.CULLING_DISABLED);

    Entity grass = new Entity();
    grass.setShaderPipeline(shaderPipeline);
    grass.setMaterial(grassMaterial);
    grass.setMesh(grassMesh);
    grass.setTranslation(0, 0, 1);

我现在的问题是:我做错了什么?为什么纹理如此奇怪,为什么与变换的乘法对平移不起作用?

如果您需要更多代码,我这里有一个 Eclipse 项目的 GitHub 回购协议:https://github.com/RalleYTN/Heroica-Fabulis

感谢@Rabbid76,我更接近我的答案,现在我终于找到了问题所在。 翻译不起作用的第一个问题是通过垂直而不是水平乘以变换来解决的。再次感谢@Rabidd76 .

而纹理如此奇怪的原因是因为我错误地合并了索引。我不应该把网格中所有索引的总和作为偏移量,而是顶点的总和。

现在的工作方法是:

public static final MeshData mergeLazy(List<MeshData> meshes, List<Matrix4f> transformations) {

    ArrayList<Float> vertices = new ArrayList<>();
    ArrayList<Float> texCoords = new ArrayList<>();
    ArrayList<Float> normals = new ArrayList<>();
    ArrayList<Integer> indices = new ArrayList<>();
    int offset = 0;
    int m = 0;

    for(MeshData mesh : meshes) {

        Matrix4f transformation = transformations.get(m);
        float[] mVertices = mesh.getVertices();
        float[] mNormals = mesh.getNormals();

        for(int index = 0; index < mesh.getVertices().length; index += 3) {

            Vector3f vertex = MatrixUtil.multiply(transformation, mVertices[index], mVertices[index + 1], mVertices[index + 2]);
            vertices.add(vertex.x);
            vertices.add(vertex.y);
            vertices.add(vertex.z);

            Vector3f normal = MatrixUtil.multiply(transformation, mNormals[index], mNormals[index + 1], mNormals[index + 2]);
            normals.add(normal.x);
            normals.add(normal.y);
            normals.add(normal.z);
        }

        ListUtil.addFloatArray(texCoords, mesh.getTextureCoordinates());
        int[] mIndices = mesh.getIndices();

        for(int index : mIndices) {

            indices.add(index + offset);
        }

        offset += mVertices.length / 3;
        m++;
    }

    MeshData mesh = new MeshData();
    mesh.setIndices(ListUtil.toPrimitiveIntArray(indices));
    mesh.setNormals(ListUtil.toPrimitiveFloatArray(normals));
    mesh.setTextureCoordinates(ListUtil.toPrimitiveFloatArray(texCoords));
    mesh.setVertices(ListUtil.toPrimitiveFloatArray(vertices));

    return mesh;
}