(九)球体

1.概述

球体比较复杂,涉及到极点位置会出现聚集的问题,本文采用常规方法绘制球体,然后借鉴他人的方法,通过正八面体拆分的方法生成球体mesh。

2.常规方法

常规方法就是通过极坐标系,分别计算球体表面的坐标,然后依次生成三角形。问题在于当划分较细时,球体两端的网格会比较细,比较聚集。

2.1 基类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshFilter),typeof(MeshRenderer))]
public class CreateMeshBase : MonoBehaviour
{
    MeshFilter meshFilter;

    protected Mesh mesh;

    protected virtual Vector3[] Vertices { get; }
    protected virtual int[] Triangles { get; }
    protected virtual Vector3[] Normals { get; }
    protected virtual Vector4[] Tangents { get; }
    protected virtual Vector2[] Uvs { get; }
    protected virtual string MeshName { get; }

    protected virtual void Start()
    {
        GetMeshFilter();
    }

    protected virtual void Reset()
    {
        GetMeshFilter();
    }

    protected virtual void OnValidate()
    {
        GetMeshFilter();
    }

    void GetMeshFilter()
    {
        if (meshFilter == null)
        {
            meshFilter = GetComponent<MeshFilter>();
            mesh = new Mesh();            
        }

        mesh.triangles = null;
        mesh.uv = null;
        mesh.vertices = null;
        mesh.tangents = null;

        mesh.name = MeshName;
        mesh.vertices = Vertices;
        mesh.triangles = Triangles;
        mesh.uv = Uvs;
        mesh.normals = Normals;
        mesh.tangents = Tangents;

        meshFilter.mesh = mesh;
    }

    private void OnDrawGizmos()
    {
        if (Vertices == null) return;

        Gizmos.color = Color.red;
        Gizmos.DrawSphere(Vector3.zero, 0.5f);

        Gizmos.color = Color.blue;

        for (int i = 0; i < Vertices.Length; i++)
        {
            Gizmos.DrawSphere(Vertices[i], 0.3f);
        }
    }
}

2.2 球体mesh

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CreateSphere : CreateMeshBase
{
    public float radius = 10;
    public int horizontalSize = 10;
    public int verticalSize = 10;

    protected override string MeshName
    {
        get
        {
            return "Sphere mesh";
        }
    }

    protected override Vector3[] Vertices
    {
        get
        {
            Vector3[] vertices = new Vector3[(horizontalSize + 1) * (verticalSize + 1)];

            float horizontalDelta = 2 * Mathf.PI / horizontalSize;
            float verticalDelta = 2 * Mathf.PI / verticalSize;

            for (int i = 0; i < verticalSize + 1; i++)
            {
                float rad = i * verticalDelta;
                float subRadius = radius * Mathf.Sin(rad);
                float y = radius * Mathf.Cos(rad);

                for (int j = 0; j < horizontalSize; j++)
                {
                    int index = i * (horizontalSize + 1) + j;
                    float x = subRadius * Mathf.Cos(j * horizontalDelta);
                    float z = subRadius * Mathf.Sin(j * horizontalDelta);

                    vertices[index] = new Vector3(x, y, z);
                }
            }
            return vertices;
        }
    }

    protected override int[] Triangles
    {
        get
        {
            int[] triangles = new int[horizontalSize * verticalSize * 2 * 3];

            for (int i = 0; i < verticalSize; i++)
            {
                for (int j = 0; j < horizontalSize; j++)
                {
                    int index = (i * horizontalSize + j) * 6;

                    triangles[index] = i * (horizontalSize + 1) + j + 1;
                    triangles[index + 2] = i * (horizontalSize + 1) + j;
                    triangles[index + 1] = (i + 1) * (horizontalSize + 1) + j;

                    triangles[index + 3] = i * (horizontalSize + 1) + j + 1;
                    triangles[index + 5] = (i + 1) * (horizontalSize + 1) + j;
                    triangles[index + 4] = (i + 1) * (horizontalSize + 1) + j + 1;
                }
            }
            return triangles;
        }
    }
}

3.正八面细分法

正八面体球则是通过正八面体,对边长进行划分,生成一系列的点,将这些点归一化即可得到单位球体,原文再此

3.1 代码

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class DrawOctahedronSphere : MonoBehaviour
{
    public Material mat;

    public int subdivisions;
    public int radius;

    private static Vector3[] directions = {
        Vector3.left,
        Vector3.back,
        Vector3.right,
        Vector3.forward
    };

    void Start()
    {
        DrawSphere(subdivisions, radius);
    }

    private void OnValidate()
    {
        DrawSphere(subdivisions, radius);
    }

    public void DrawSphere(int subdivisions = 0, float radius = 1)
    {
        if (subdivisions > 4)
        {
            subdivisions = 4;
        }

        //gameObject.GetComponent<MeshRenderer>().material = mat;

        Mesh mesh = GetComponent<MeshFilter>().mesh;
        mesh.Clear();

        int resolution = 1 << subdivisions;
        Vector3[] vertices = new Vector3[(resolution + 1) * (resolution + 1) * 4 - 3 * (resolution * 2 + 1)];
        int[] triangles = new int[(1 << (subdivisions * 2 + 3)) * 3];
        CreateOctahedron(vertices, triangles, resolution);

        if (radius != 1f)
        {
            for (int i = 0; i < vertices.Length; i++)
            {
                vertices[i] *= radius;
            }
        }

        Vector3[] normals = new Vector3[vertices.Length];
        Normalize(vertices, normals);

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.normals = normals;

    }

    private static void CreateOctahedron(Vector3[] vertices, int[] triangles, int resolution)
    {
        int v = 0, vBottom = 0, t = 0;

        vertices[v++] = Vector3.down;

        for (int i = 1; i <= resolution; i++)
        {
            float progress = (float)i / resolution;
            Vector3 from, to;
            vertices[v++] = to = Vector3.Lerp(Vector3.down, Vector3.forward, progress);
            for (int d = 0; d < 4; d++)
            {
                from = to;
                to = Vector3.Lerp(Vector3.down, directions[d], progress);
                t = CreateLowerStrip(i, v, vBottom, t, triangles);
                v = CreateVertexLine(from, to, i, v, vertices);
                vBottom += i > 1 ? (i - 1) : 0;
            }
            vBottom = v - 1 - i * 4;
        }

        for (int i = resolution - 1; i >= 1; i--)
        {
            float progress = (float)i / resolution;
            Vector3 from, to;
            vertices[v++] = to = Vector3.Lerp(Vector3.up, Vector3.forward, progress);
            for (int d = 0; d < 4; d++)
            {
                from = to;
                to = Vector3.Lerp(Vector3.up, directions[d], progress);
                t = CreateUpperStrip(i, v, vBottom, t, triangles);
                v = CreateVertexLine(from, to, i, v, vertices);
                vBottom += i + 1;
            }
            vBottom = v - 1 - i * 4;
        }

        vertices[vertices.Length - 1] = Vector3.up;

        for (int i = 0; i < 4; i++)
        {
            triangles[t++] = vBottom;
            triangles[t++] = v;
            triangles[t++] = ++vBottom;
        }
    }

    private static int CreateVertexLine(Vector3 from, Vector3 to, int steps, int v, Vector3[] vertices)
    {
        for (int i = 1; i <= steps; i++)
        {
            vertices[v++] = Vector3.Lerp(from, to, (float)i / steps);
        }
        return v;
    }

    private static int CreateLowerStrip(int steps, int vTop, int vBottom, int t, int[] triangles)
    {
        for (int i = 1; i < steps; i++)
        {
            triangles[t++] = vBottom;
            triangles[t++] = vTop - 1;
            triangles[t++] = vTop;

            triangles[t++] = vBottom++;
            triangles[t++] = vTop++;
            triangles[t++] = vBottom;
        }
        triangles[t++] = vBottom;
        triangles[t++] = vTop - 1;
        triangles[t++] = vTop;
        return t;
    }

    private static int CreateUpperStrip(int steps, int vTop, int vBottom, int t, int[] triangles)
    {
        triangles[t++] = vBottom;
        triangles[t++] = vTop - 1;
        triangles[t++] = ++vBottom;
        for (int i = 1; i <= steps; i++)
        {
            triangles[t++] = vTop - 1;
            triangles[t++] = vTop;
            triangles[t++] = vBottom;

            triangles[t++] = vBottom;
            triangles[t++] = vTop++;
            triangles[t++] = ++vBottom;
        }
        return t;
    }


    private static void Normalize(Vector3[] vertices, Vector3[] normals)
    {
        for (int i = 0; i < vertices.Length; i++)
        {
            normals[i] = vertices[i] = vertices[i].normalized;
        }
    }

}
posted @ 2020-02-03 16:32  81192  阅读(621)  评论(0编辑  收藏  举报