Unity中用Mesh画一个圆环

Probuider

前几天在作一个小项目的时候,用到了Unity自带的一个包ProBuilder其中的Arch生成1/4圆。html

挺好玩的,能够在直接Unity中根据须要用Mesh定制生成图形,而不用建模软件。c#

可是存在一个小问题,就是在使用的时候他的中心点是在生成图形的左下角。ide

旋转的时候不符合个人需求,我想要的是生成的时候旋转中心在圆心的位置,因此准备本身定制一个。性能

目标

关于Mesh生成图形的原理能够参考这篇文章,讲得虽然不算很详细,但足够了解基本概念了。ui

目标是生成下面图中的一个1/4空心圆柱体spa

咱们切换到Wireframe模式下,能够看出它是有一个一个的顶点,并经过一条条的直线链接起来。那么咱们如何肯定这些顶点和线的位置呢?3d

小目标-生成一个面

其实很简单的,咱们一步一步慢慢来。一次生成一整个会有点麻烦,咱们能够一面一面来。只要生成了第一个面,其余的面也是相似的方法生成就好。code

在前面咱们提到了咱们要的是生成一个圆柱体,圆柱体一个的重要性质就是能够由一个圆形叠加产生,也就是只要咱们生成一个圆形,就完成了大部分的工做。orm

咱们知道3D建模就是由一个一个的三角形组合成的,因此咱们要用三角形来模拟来一个空心的圆。htm

在Probuilder中生成这样一个空心圆柱体用的是Arch,它有几个参数,分别是
\(\color{#1E90FF}{Radius}\) 半径,圆心到最外圈的距离
\(\color{#1E90FF}{Thickness}\) 厚度,圆心到最外圈的距离-圆心到最内圈的距离
\(\color{#1E90FF}{Depth}\) 深度
\(\color{#1E90FF}{NumberOfSides}\) 由多少个面组成,面越多越平滑,性能也越差
\(\color{#1E90FF}{DrawArchDegrees}\) 总共绘制的角度

\(\color{#1E90FF}{NumberOfSides}\)中的面是指由两个三角形一头一尾拼成的梯形,多个头大脚小的梯形拼在一块儿便成了咱们须要的圆形。

原理已经知道了,那下一步只要肯定三角形顶点的位置就OK了。至于如何肯定三角形顶点的位置,咱们能够再看下这张图。
Mesh2.png

是否是瞬间清晰明了,红线的交汇处就是圆心的位置,数字则是每一个顶点的编号。

咱们假设圆心在原点,数字0-1所在的线为180度线。\(\color{#1E90FF}{Increment}\) = \(\color{#1E90FF}{DrawArchDegrees}\)/\(\color{#1E90FF}{NumberOfSides}\)就是线与线之间的角度。每条线的角度能够由\(\color{#1E90FF}{180-Increment*i}\)获得。i为第几条线。

线上的点能够由\(\color{#1E90FF}{y = r* sinθ, y = r* cosθ}\)获得。

//顶点坐标
        vertexList.Clear();
        float incrementAngle = DrawArchDegrees / NumberOfSides;
        //小于等因而由于n+1条线才能组成n个面
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float innerX = (Radius - Thickness) * Mathf.Cos(angle * Mathf.Deg2Rad);
            float innerY = (Radius - Thickness) * Mathf.Sin(angle * Mathf.Deg2Rad);
            vertexList.Add(new Vector3(innerX, innerY, 0));
            float outsideX = Radius * Mathf.Cos(angle * Mathf.Deg2Rad);
            float outsideY = Radius * Mathf.Sin(angle * Mathf.Deg2Rad);
            vertexList.Add(new Vector3(outsideX, outsideY, 0));
        }

在上面的代码中咱们已经计算出了顶点的位置,下一步咱们要作的是按顺序插入三角形顶点的位置。从Mesh这篇文章中咱们能够知道,只有是三角形是正面的状况下才会被渲染。

而正反面能够经过法线的朝向进行判断,向外的面就是正面,相反的就是背面。

在Unity中,法线的朝向能够由左手法则获得。拿出左手,伸直,拇指与其余四个指头垂直,而后四指弯曲,指尖朝向循环的方向,拇指就指向法线的方向。

也就是说在上图中,咱们想渲染三角形,顺序应该是相似这样的012,321, 234, 543。

//三角形索引
        triangleList.Clear();
        int direction = 1;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }

\(\color{#F08080}{getTriangleIndexs}\)代码以下

int[] getTriangleIndexs(int index, int direction)
    {
        int[] triangleIndexs = new int[3] { 0,1,2};
        for (int i = 0; i < triangleIndexs.Length; i++)
        {
            triangleIndexs[i] += index;
        }
        if (direction == -1)
        {
            int temp = triangleIndexs[0];
            triangleIndexs[0] = triangleIndexs[2];
            triangleIndexs[2] = temp;
        }
        return triangleIndexs;
    }

至于uv坐标就更简单了,把内圈顶点uv坐标中的Y固定为0,外圈顶点uv坐标中的Y固定为1,而x坐标由\(\color{#1E90FF}{1/NumberOfSides}\)获得:

//UV索引
    uvList.Clear();
    for (int i = 0; i <= NumberOfSides; i++)
    {
        float angle = 180 - i * incrementAngle;
        float littleX = (1.0f / NumberOfSides) * i;
        uvList.Add(new Vector2(littleX, 0));
        float bigX = (1.0f / NumberOfSides) * i;
        uvList.Add(new Vector2(bigX, 1));
    }

完整代码以下:

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

//[RequireComponent(typeof(MeshFilter))]
//[RequireComponent(typeof(MeshRenderer))]
//[ExecuteInEditMode]
public class DrawArch : MonoBehaviour
{
    public float Radius = 20.0f;                //外圈的半径
    public float Thickness = 10.0f;             //厚度,外圈半径减去内圈半径
    public float Depth = 1.0f;                  //厚度
    public float NumberOfSides = 30.0f;         //由多少个面组成
    public float DrawArchDegrees = 90.0f;       //要绘画多长
    public Material archMaterial = null;
    
    private List<Vector3> vertexList = new List<Vector3>();
    private List<int> triangleList = new List<int>();
    private List<Vector2> uvList = new List<Vector2>();

    // Start is called before the first frame update
    void Start()
    {
        GenerateVertex();
    }

    void GenerateVertex()
    {
        //顶点坐标
        vertexList.Clear();
        float incrementAngle = DrawArchDegrees / NumberOfSides;
        //小于等因而由于n+1条线才能组成n个面
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float innerX = (Radius - Thickness) * Mathf.Cos(angle * Mathf.Deg2Rad);
            float innerY = (Radius - Thickness) * Mathf.Sin(angle * Mathf.Deg2Rad);
            vertexList.Add(new Vector3(innerX, innerY, 0));
            float outsideX = Radius * Mathf.Cos(angle * Mathf.Deg2Rad);
            float outsideY = Radius * Mathf.Sin(angle * Mathf.Deg2Rad);
            vertexList.Add(new Vector3(outsideX, outsideY, 0));
        }

        //三角形索引
        triangleList.Clear();
        int direction = 1;
        for (int i = 0; i < NumberOfSides * 2; i++)
        {
            int[] triangleIndexs = getTriangleIndexs(i, direction);
            direction *= -1;
            for (int j = 0; j < triangleIndexs.Length; j++)
            {
                triangleList.Add(triangleIndexs[j]);
            }
        }

        //UV索引
        uvList.Clear();
        for (int i = 0; i <= NumberOfSides; i++)
        {
            float angle = 180 - i * incrementAngle;
            float littleX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(littleX, 0));
            float bigX = (1.0f / NumberOfSides) * i;
            uvList.Add(new Vector2(bigX, 1));
        }
        Mesh mesh = new Mesh()
        {
            vertices = vertexList.ToArray(),
            uv = uvList.ToArray(),
            triangles = triangleList.ToArray(),
        };

        mesh.RecalculateNormals();
        gameObject.AddComponent<MeshFilter>().mesh = mesh;
        gameObject.AddComponent<MeshRenderer>().material = archMaterial;
    }

    int[] getTriangleIndexs(int index, int direction)
    {
        int[] triangleIndexs = new int[3] { 0,1,2};
        for (int i = 0; i < triangleIndexs.Length; i++)
        {
            triangleIndexs[i] += index;
        }
        if (direction == -1)
        {
            int temp = triangleIndexs[0];
            triangleIndexs[0] = triangleIndexs[2];
            triangleIndexs[2] = temp;
        }
        return triangleIndexs;
    }
}

未完待续。。。

相关文章
相关标签/搜索