细分的 icosphere 渲染不正确。顶点顺序不正确?

Subdivided icosphere not rendering right. Incorrect vertices order?

我目前正在对我的 icosphere 进行细分,结果看起来很疯狂(见下文)。如果我根本不细分它,它工作正常,所以我相信错误是在我的循环递归或 getMiddlePoint 方法中(见下文)。我对为什么会发生这种情况的想法是我以错误的顺序添加了顶点和索引。如果是这种情况,我应该按什么顺序添加它们?关于如何解决这个问题有什么想法吗?

结果:

循环递归:

for (int i = 0; i < RECURSION_LEVEL; i++) {
            List<Vector3i> indices2 = new ArrayList<Vector3i>();
            for (Vector3i face : indices) {
                int a = getMiddlePoint(face.x, face.y);
                int b = getMiddlePoint(face.y, face.z);
                int c = getMiddlePoint(face.z, face.x);

                indices2.add(new Vector3i(face.x, a, c));
                indices2.add(new Vector3i(face.y, b, a));
                indices2.add(new Vector3i(face.z, c, b));
                indices2.add(new Vector3i(a, b, c));
            }
            indices = indices2;
        }

getMiddlePoints 方法:

private int getMiddlePoint(int p1, int p2) {
        boolean firstIsSmaller = p1 < p2;
        int smallerIndex = firstIsSmaller ? p1 : p2;
        int greaterIndex = firstIsSmaller ? p2 : p1;
        int key = (smallerIndex << 32) + greaterIndex;

        Integer ret = middlePointIndexCache.get(key);
        if (ret != null) {
            return ret;
        }

        Vector3f point1 = vertices.get(p1);
        Vector3f point2 = vertices.get(p2);
        Vector3f middle = new Vector3f(
                (point1.x + point2.x) / 2.0f,
                (point1.y + point2.y) / 2.0f,
                (point1.z + point2.z) / 2.0f
                );

        int i = addVertex(middle);
        middlePointIndexCache.put(key, i);
        return i;
    }

我的 icosphere 生成器的所有代码: (忽略纹理坐标,它们是临时的,以防止我的渲染器崩溃。)

package com.robert.game.planet;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;

import com.robert.engine.loader.Loader;
import com.robert.engine.models.Mesh;

public class BasePlanetGenerator {

    private List<Vector3f> vertices = new ArrayList<Vector3f>();
    private List<Vector3f> normals = new ArrayList<Vector3f>();
    private List<Vector3i> indices = new ArrayList<Vector3i>(); // think of this as faces
    private List<Vector2f> textureCoords = new ArrayList<Vector2f>();
    private Map<Integer, Integer> middlePointIndexCache = new HashMap<Integer, Integer>();

    private static final int RECURSION_LEVEL = 2;

    private int index = 0;

    float t = (float) ((1.0 + Math.sqrt(5.0)) / 2);

    // Initial vertices
    {
//      vertices.add(new Vector3f(-1,  t,  0));
//      vertices.add(new Vector3f(1,  t,  0));
//      vertices.add(new Vector3f(-1, -t,  0));
//      vertices.add(new Vector3f(1, -t,  0));
//      
//      vertices.add(new Vector3f(0, -1,  t));
//      vertices.add(new Vector3f(0,  1,  t));
//      vertices.add(new Vector3f(0, -1, -t));
//      vertices.add(new Vector3f(0,  1, -t));
//      
//      vertices.add(new Vector3f(t,  0, -1));
//      vertices.add(new Vector3f(t,  0,  1));
//      vertices.add(new Vector3f(-t,  0, -1));
//      vertices.add(new Vector3f(-t,  0,  1));

        addVertex(new Vector3f(-1,  t,  0));
        addVertex(new Vector3f(1,  t,  0));
        addVertex(new Vector3f(-1, -t,  0));
        addVertex(new Vector3f(1, -t,  0));

        addVertex(new Vector3f(0, -1,  t));
        addVertex(new Vector3f(0,  1,  t));
        addVertex(new Vector3f(0, -1, -t));
        addVertex(new Vector3f(0,  1, -t));

        addVertex(new Vector3f(t,  0, -1));
        addVertex(new Vector3f(t,  0,  1));
        addVertex(new Vector3f(-t,  0, -1));
        addVertex(new Vector3f(-t,  0,  1));
    }

    // Initial indices
    {
        indices.add(new Vector3i(0, 11, 5));
        indices.add(new Vector3i(0, 5, 1));
        indices.add(new Vector3i(0, 1, 7));
        indices.add(new Vector3i(0, 7, 10));
        indices.add(new Vector3i(0, 10, 11));

        indices.add(new Vector3i(1, 5, 9));
        indices.add(new Vector3i(5, 11, 4));
        indices.add(new Vector3i(11, 10, 2));
        indices.add(new Vector3i(10, 7, 6));
        indices.add(new Vector3i(7, 1, 8));

        indices.add(new Vector3i(3, 9, 4));
        indices.add(new Vector3i(3, 4, 2));
        indices.add(new Vector3i(3, 2, 6));
        indices.add(new Vector3i(3, 6, 8));
        indices.add(new Vector3i(3, 8, 9));

        indices.add(new Vector3i(4, 9, 5));
        indices.add(new Vector3i(2, 4, 11));
        indices.add(new Vector3i(6, 2, 10));
        indices.add(new Vector3i(8, 6, 7));
        indices.add(new Vector3i(9, 8, 1));

//      indices = Arrays.asList(new Integer[] {
//              0, 11, 5,
//              0, 5, 1,
//              0, 1, 7,
//              0, 7, 10,
//              0, 10, 11,
//              
//              1, 5, 9,
//              5, 11, 4,
//              11, 10, 2,
//              10, 7, 6,
//              7, 1, 8,
//              
//              3, 9, 4,
//              3, 4, 2,
//              3, 2, 6,
//              3, 6, 8,
//              3, 8, 9,
//              
//              4, 9, 5,
//              2, 4, 11,
//              6, 2, 10,
//              8, 6, 7,
//              9, 8, 1
//      });
    }

    // Inital textureCoords
    {
        textureCoords.add(new Vector2f(1, 1));
    }

    float[] verticesArray;
    int[] indicesArray;
    float[] normalsArray;
    float[] textureCoordsArray;

    public Mesh generateBasePlanet(Loader loader) {

        for (int i = 0; i < RECURSION_LEVEL; i++) {
            List<Vector3i> indices2 = new ArrayList<Vector3i>();
            for (Vector3i face : indices) {
                int a = getMiddlePoint(face.x, face.y);
                int b = getMiddlePoint(face.y, face.z);
                int c = getMiddlePoint(face.z, face.x);

                indices2.add(new Vector3i(face.x, a, c));
                indices2.add(new Vector3i(face.y, b, a));
                indices2.add(new Vector3i(face.z, c, b));
                indices2.add(new Vector3i(a, b, c));
            }
            indices = indices2;
        }

        for (int i = 0; i < vertices.size(); i ++) {
            Vector3f normal = vertices.get(i);
//          normal.normalize();
//          float length = normal.length();     
//          normals.add(new Vector3f(normal.x / length, normal.y / length, normal.z / length));
            normals.add(normal);
        }

        convertToArrays();

        return loader.createMesh(verticesArray, textureCoordsArray, normalsArray, indicesArray);
    }

    private int getMiddlePoint(int p1, int p2) {
        boolean firstIsSmaller = p1 < p2;
        int smallerIndex = firstIsSmaller ? p1 : p2;
        int greaterIndex = firstIsSmaller ? p2 : p1;
        int key = (smallerIndex << 32) + greaterIndex;

        Integer ret = middlePointIndexCache.get(key);
        if (ret != null) {
            return ret;
        }

        Vector3f point1 = vertices.get(p1);
        Vector3f point2 = vertices.get(p2);
        Vector3f middle = new Vector3f(
                (point1.x + point2.x) / 2.0f,
                (point1.y + point2.y) / 2.0f,
                (point1.z + point2.z) / 2.0f
                );

        int i = addVertex(middle);
        middlePointIndexCache.put(key, i);
        return i;
    }

    private void convertToArrays() {
        verticesArray = new float[vertices.size() * 3];
        indicesArray = new int[indices.size() * 3];
        normalsArray = new float[normals.size() * 3];
        textureCoordsArray = new float[textureCoords.size() * 2];

        for (int i = 0; i < vertices.size(); i ++) {        
            Vector3f vertex = vertices.get(i);
            verticesArray[(i * 3)] = vertex.x;
            verticesArray[(i * 3) + 1] = vertex.y;
            verticesArray[(i * 3) + 2] = vertex.z;
        }

        for (int i = 0; i < indices.size(); i ++) {
            Vector3i indice = indices.get(i);
            indicesArray[(i * 3)] = indice.x;
            indicesArray[(i * 3) + 1] = indice.y;
            indicesArray[(i * 3) + 2] = indice.z;
//          indicesArray[i] = indices.get(i);
        }

        for (int i = 0; i < normals.size(); i ++) {
            Vector3f normal = normals.get(i);
            normalsArray[(i * 3)] = normal.x;
            normalsArray[(i * 3) + 1] = normal.y;
            normalsArray[(i * 3) + 2] = normal.z;
        }

        for (int i = 0; i < textureCoords.size(); i ++) {
            textureCoordsArray[(i * 2)] = textureCoords.get(i).x;
            textureCoordsArray[(i * 2) + 1] = textureCoords.get(i).y;
        }
    }

    private int addVertex(Vector3f vector) {
        float length = vector.length();
        vertices.add(new Vector3f(vector.x / length, vector.y / length, vector.z / length));
        return index++;
    }
}

oracle java specification 明确表示:

The values of the integral types are integers in the following ranges:
- For int, from -2147483648 to 2147483647, inclusive

这意味着数据类型int是一个整数数据类型,大小为32位。

在函数 getMiddlePoint 中,行

int key = (smallerIndex << 32) + greaterIndex;

导致溢出。

改为

int key = (smallerIndex << 16) + greaterIndex;

您的代码将正常工作。