You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中基于立方体生成体素网格球体的异常问题排查

立方体拼接球体的Mesh生成问题

我想生成由立方体拼接成的球体(效果如下):
立方体组成的球体

直接用独立GameObject创建每个立方体的话,大尺寸球体(尤其是实心款)会有严重性能问题,查资料后确定正确思路是只生成暴露在外的面,合并成单个Mesh。我参考教程写了自定义脚本,但不管设置多大的planetDia(比如30),最终都只生成2x2x2的立方体,完全不是预期的空心球体Mesh。

我怀疑问题可能出在:

  • 脚本挂载的GameObject类型错误
  • 缺失必要组件
  • 跳过了UV部分

附上我的代码:

public class PlanetGeneration : MonoBehaviour
{
    private int planetDia = 30;
    private int planetRadius;

    //array of bools to keep track if each space is solid(true) or void(false)
    private bool[,,] currentPlanet;

    void Start()
    {
        currentPlanet = new bool[planetDia, planetDia, planetDia];

        planetRadius = planetDia / 2 + 1;

        GeneratePlanet();
        GenerateMesh(); 
    }

    //define each block as void or solid
    public void GeneratePlanet()
    {
        Vector3 planetCenter = new(planetRadius, planetRadius, planetRadius);
        Vector3 currentPoint;

        for (int x = 0; x < planetDia; x++)
        {
            for (int y = 0; y < planetDia; y++)
            {
                for (int z = 0; z < planetDia; z++)
                {
                    //set the current position
                    currentPoint = new Vector3(x, y, z);

                    //check if this point is within the desired planet size
                    if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
                    {
                        //make it a solid block
                        currentPlanet[x, y, z] = true;
                    }
                    //make it void
                    else currentPlanet[x, y, z] = false;
                }
            }
        }
    }

    //generate mesh
    public void GenerateMesh()
    {
        List<Vector3> Vertices = new List<Vector3>();
        List<int> Triangles = new List<int>();

        for (int x = 0; x < planetDia; x++)
        {
            for (int y = 0; y < planetDia; y++)
            {
                for (int z = 0; z < planetDia; z++)
                {
                    //the 8 corners of a cube
                    Vector3[] VertexPos = new Vector3[8]
                    {
                        new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
                        new Vector3(1, 1, 1), new Vector3(1, 1, -1),
                        new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
                        new Vector3(1, -1, 1), new Vector3(1, -1, -1),
                    };

                    //first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction
                    int[,] Faces = new int[6, 7]{
                        {0, 1, 2, 3, 0, 1, 0},     //top
                        {7, 6, 5, 4, 0, -1, 0},   //bottom
                        {2, 1, 5, 6, 0, 0, 1},     //right
                        {0, 3, 7, 4, 0, 0, -1},   //left
                        {3, 2, 6, 7, 1, 0, 0},    //front
                        {1, 0, 4, 5, -1, 0, 0}    //back
                    };


                    //if this block is solid
                    if (currentPlanet[x, y, z] == true)
                    {
                        //for each face of the block
                        for (int i = 0; i < 6; i++)
                        {
                            //check if it is on the edge of the overall array
                            //This part is dumb and needs to be removed. Keeping for now to prevent the array out of bounds error.
                            if(x + Faces[i, 4] == -1 || y + Faces[i, 5] == -1 || z + Faces[i, 6] == -1 ||
                                x + Faces[i, 4] == planetDia || y + Faces[i, 5] == planetDia || z + Faces[i, 6] == planetDia)
                            {
                                //do nothing
                            }

                            //check if the block next to it is void
                            else if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
                            {
                                AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count);
                            }
                        }
                    }

                    void AddQuad(int ai, int bi, int ci, int di, int i)
                    {
                        Vector3 a = VertexPos[ai];
                        Vector3 b = VertexPos[bi];
                        Vector3 c = VertexPos[ci];
                        Vector3 d = VertexPos[di];

                        Vertices.AddRange(new List<Vector3>() { a, b, c, d });
                        Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 });
                    }
                }
            }
        }

        this.gameObject.GetComponent<MeshFilter>().mesh = new Mesh()
        {
            vertices = Vertices.ToArray(),
            triangles = Triangles.ToArray(),
        };

    }
}

问题排查与修复方案

1. 顶点未做空间偏移,所有面叠在原点

你的VertexPos只定义了单个立方体的局部顶点,没有加上当前方块的坐标偏移(x,y,z),导致所有生成的面都挤在原点附近,看起来就是个2x2x2的块。

修改AddQuad方法里的顶点计算:

void AddQuad(int ai, int bi, int ci, int di, int i)
{
    // 加上当前方块的坐标偏移
    Vector3 a = VertexPos[ai] + new Vector3(x, y, z);
    Vector3 b = VertexPos[bi] + new Vector3(x, y, z);
    Vector3 c = VertexPos[ci] + new Vector3(x, y, z);
    Vector3 d = VertexPos[di] + new Vector3(x, y, z);

    Vertices.AddRange(new List<Vector3>() { a, b, c, d });
    Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 });
}

2. 球体半径计算错误,实心区域过大

你写的planetRadius = planetDia / 2 + 1会让球体判定范围远超预期,比如直径30时半径算成16,整个30x30x30的空间几乎全是实心,只有最外层几个方块是空的,自然看不到球体形状。

修改半径计算:

planetRadius = planetDia / 2;

另外,Vector3.Distance计算距离效率很低,建议改用平方距离比较,优化性能:

// 替换GeneratePlanet里的距离判断逻辑
if (Vector3.SqrMagnitude(currentPoint - planetCenter) <= planetRadius * planetRadius)

3. 边界判断逻辑错误,边缘面未生成

你当前的代码直接跳过了边缘方块的面,但实际上边缘方块外侧没有相邻方块,这些面应该被生成。修改边界判断逻辑:

// 替换GenerateMesh里的面生成判断逻辑
for (int i = 0; i < 6; i++)
{
    int nx = x + Faces[i, 4];
    int ny = y + Faces[i, 5];
    int nz = z + Faces[i, 6];
    // 相邻方块不存在(超出边界)或者是空心,就生成当前面
    bool needGenerateFace = false;
    if (nx < 0 || nx >= planetDia || ny < 0 || ny >= planetDia || nz < 0 || nz >= planetDia)
    {
        needGenerateFace = true;
    }
    else
    {
        needGenerateFace = !currentPlanet[nx, ny, nz];
    }

    if (needGenerateFace)
    {
        AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count);
    }
}

4. 组件检查

确保脚本挂载的GameObject上有MeshFilter和MeshRenderer组件,后者必须添加材质才能看到生成的Mesh。UV缺失不会导致Mesh不显示,只是材质会拉伸,不是当前问题的核心。


内容的提问来源于stack exchange,提问作者plasmas222

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:40:34