1. 程式人生 > >[引擎]之三:在Unity中自定義CubeMesh

[引擎]之三:在Unity中自定義CubeMesh

之前的文章mesh在Unity中的簡單使用說過,會建立CubeMesh。嗯,來了。

之前已經知道如何建立一個簡單的片mesh,那麼同理,CubeMesh的建立,同樣是從頂點、三角面、uv座標三個方面生成。

(1) 頂點的生成

一個cube只需要8個頂點就可以表示了。以中心點為(0,0,0),邊長為1,按前面頂點index為0,1,2,3,後面頂點index為4,5,6,7順時針順序新增頂點,順序如下圖所示:
頂點順序

Mesh m = new Mesh();
// 前面
_verticesList.Add(new Vector3(0.5f, -0.5f, -0.5f)); // index 0
_verticesList.Add(new
Vector3(-0.5f, -0.5f, -0.5f)); // index 1 _verticesList.Add(new Vector3(-0.5f, 0.5f, -0.5f)); // index 2 _verticesList.Add(new Vector3(0.5f, 0.5f, -0.5f)); // index 3 // 後面 _verticesList.Add(new Vector3(0.5f, -0.5f, 0.5f)); // index 4 _verticesList.Add(new Vector3(-0.5f, -0.5f, 0.5f)); // index 5 _verticesList.Add(new
Vector3(-0.5f, 0.5f, 0.5f)); // index 6 _verticesList.Add(new Vector3(0.5f, 0.5f, 0.5f)); // index 7 // mesh賦值 m.SetVertices(_verticesList);
(2) 生成三角面

和前面一樣,按照順時針方向,頂點index儘量由小到大排列,新增:

            // 前面
            _triList.Add(0);
            _triList.Add(1);
            _triList.Add(2);


            _triList.Add
(0); _triList.Add(2); _triList.Add(3); // 後面 _triList.Add(4); _triList.Add(7); _triList.Add(5); _triList.Add(5); _triList.Add(7); _triList.Add(6); // 左面 _triList.Add(1); _triList.Add(5); _triList.Add(6); _triList.Add(1); _triList.Add(6); _triList.Add(2); // 右面 _triList.Add(0); _triList.Add(3); _triList.Add(4); _triList.Add(3); _triList.Add(7); _triList.Add(4); // 上面 _triList.Add(2); _triList.Add(6); _triList.Add(3); _triList.Add(3); _triList.Add(6); _triList.Add(7); // 下面 _triList.Add(1); _triList.Add(4); _triList.Add(5); _triList.Add(0); _triList.Add(4); _triList.Add(1); // 賦值給mesh m.SetTriangles(_triList,0);
(3) 生成uv座標

按照之前的想法,每個頂點對應一個uv座標,所以,也是隻需要8個uv座標就可以了。先讓uv座標為0或1,看看效果:

            _uvList.Add(new Vector2(1,0));          
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            _uvList.Add(new Vector2(1,0));
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            // 賦值給mesh
            m.SetUVs(0,_uvList);
(4) 看看效果

完整程式碼如下,掛到GameObejct上就可以了:

using UnityEngine;
using System.Collections.Generic;
namespace YanCheZuo
{
    [RequireComponent(typeof(MeshFilter))]
    public class TestCubeMesh : MonoBehaviour
    {
        private MeshFilter _meshFilter;
        private Mesh _mesh;

        private List<Vector3> _verticesList = new List<Vector3>();
        private List<Vector2> _uvList = new List<Vector2>();
        private List<int> _triList = new List<int>();

        private void Start()
        {
            _meshFilter = this.GetComponent<MeshFilter>();
            _mesh = _meshFilter.mesh;

            RebuildMesh();

        }
        private void RebuildMesh()
        {
            _mesh = GetCubeMesh();
            _meshFilter.mesh = _mesh;
        }

        private Mesh GetCubeMesh()
        {
            Mesh m = new Mesh();

            _verticesList.Add(new Vector3(0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, -0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, -0.5f));

            _verticesList.Add(new Vector3(0.5f, -0.5f, 0.5f));
            _verticesList.Add(new Vector3(-0.5f, -0.5f, 0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, 0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, 0.5f));

            m.SetVertices(_verticesList);

            _uvList.Add(new Vector2(1,0));          
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            _uvList.Add(new Vector2(1,0));
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            m.SetUVs(0,_uvList);


            // 前面
            _triList.Add(0);
            _triList.Add(1);
            _triList.Add(2);


            _triList.Add(0);
            _triList.Add(2);
            _triList.Add(3);


            // 後面
            _triList.Add(4);
            _triList.Add(7);
            _triList.Add(5);

            _triList.Add(5);
            _triList.Add(7);
            _triList.Add(6);


            // 左面
            _triList.Add(1);
            _triList.Add(5);
            _triList.Add(6);

            _triList.Add(1);
            _triList.Add(6);
            _triList.Add(2);


            // 右面
            _triList.Add(0);
            _triList.Add(3);
            _triList.Add(4);

            _triList.Add(3);
            _triList.Add(7);
            _triList.Add(4);

            // 上面
            _triList.Add(2);
            _triList.Add(6);
            _triList.Add(3);

            _triList.Add(3);
            _triList.Add(6);
            _triList.Add(7);

            // 下面
            _triList.Add(1);
            _triList.Add(4);
            _triList.Add(5);

            _triList.Add(0);
            _triList.Add(4);
            _triList.Add(1);

            m.SetTriangles(_triList,0);

            return m;

        }
    }
}

mesh生成ok,執行後的效果如下圖所示:
執行結果

好像不對!!前面和後面的uv是對的,其他的幾個面uv並不是我想想中的那樣!

(5) 理論和對比

在《3D遊戲程式設計大師技巧》中,有幾種型別的模型檔案解析,基本的結構是,一個完整的頂點列表,在三角面的定義中,指定需要的頂點index和uv座標。例如:

vertices:
v0,v1,v2,v3,v4,...,vn
faces:          // 每行代表一個三角面的定義
<0,uv0> <1,uv1> <2,uv2>     
<0,uv3> <2,uv4> <4,uv5>
....

Unity中,沒有面的定義。並且需要uv的數量和頂點的數量一樣。即一個頂點對應一個uv座標。
檢視Unity原生的CubeMesh,效果是這樣的:
Unity原生的CubeMesh

檢視其頂點資訊,如下圖所示:
Unity原生的CubeMesh資訊

可以看出,同樣的一個位置的頂點由三個。
因為6個面,每個面的uv都不相同,所以,其實需要8*3=24個頂點來表示一個CubeMesh
那麼,只能用24個頂點和24個uv來表示了。

(6) 修改

比較方便的一點是,利用之前的工具MeshViewer,可以很方便的找到頂點的對應順序。
按照 前面、後面、上面、下面、左面、右面,每個面頂點從右下角開始順時針的順序構建mesh,頂點順序分別為:

  • 前面 0,1,2,3
  • 後面 4,5,6,7
  • 上面 8,9,10,11
  • 下面 12,13,14,15
  • 左面 16,17,18,19
  • 右面 20,21,22,23
    有了檢視工具的幫助,耐心構建24個頂點,12個三角面,並按照順序給出24uv座標,完整程式碼如下:
using UnityEngine;
using System.Collections.Generic;
namespace YanCheZuo
{
    [RequireComponent(typeof(MeshFilter))]
    public class TestCubeMesh2 : MonoBehaviour
    {
        private MeshFilter _meshFilter;
        private Mesh _mesh;

        private List<Vector3> _verticesList = new List<Vector3>();
        private List<Vector2> _uvList = new List<Vector2>();
        private List<int> _triList = new List<int>();

        private void Start()
        {
            _meshFilter = this.GetComponent<MeshFilter>();
            _mesh = _meshFilter.mesh;

            RebuildMesh();

        }
        private void RebuildMesh()
        {
            _mesh = GetCubeMesh();
            _meshFilter.mesh = _mesh;
        }

        private Mesh GetCubeMesh()
        {
            Mesh m = new Mesh();
            // 本來,只需要8個頂點就可以了。
            // 但是,由於6個四邊形面的紋理座標,不能共用頂點的紋理座標
            // 所以,需要8x3個頂點(同樣的頂點被3個四邊形面共用)


            // 前面 0,1,2,3
            _verticesList.Add(new Vector3(0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, -0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, -0.5f));

            // 後面 4,5,6,7
            _verticesList.Add(new Vector3(0.5f, -0.5f, 0.5f));
            _verticesList.Add(new Vector3(-0.5f, -0.5f, 0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, 0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, 0.5f));

            // 上面 8,9,10,11
            _verticesList.Add(new Vector3(0.5f, 0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, 0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, 0.5f));

            // 下面 12,13,14,15
            _verticesList.Add(new Vector3(0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f,-0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f, -0.5f, 0.5f));
            _verticesList.Add(new Vector3(0.5f, -0.5f, 0.5f));

            // 左面 16,17,18,19
            _verticesList.Add(new Vector3(-0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(-0.5f,-0.5f, 0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, 0.5f));
            _verticesList.Add(new Vector3(-0.5f, 0.5f, -0.5f));

            // 右面 20,21,22,23
            _verticesList.Add(new Vector3(0.5f, -0.5f, -0.5f));
            _verticesList.Add(new Vector3(0.5f,-0.5f, 0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, 0.5f));
            _verticesList.Add(new Vector3(0.5f, 0.5f, -0.5f));


            m.SetVertices(_verticesList);

            // 前面
            _uvList.Add(new Vector2(1,0));          
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            // 後面
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(1,0));          
            _uvList.Add(new Vector2(1,1));
            _uvList.Add(new Vector2(0,1));

            // 上面
            _uvList.Add(new Vector2(1,0));          
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            // 下面

            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(1,0));
            _uvList.Add(new Vector2(1,1));
            _uvList.Add(new Vector2(0,1));

            // 左面
            _uvList.Add(new Vector2(1,0));
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(0,1));
            _uvList.Add(new Vector2(1,1));

            // 右面
            _uvList.Add(new Vector2(0,0));
            _uvList.Add(new Vector2(1,0));
            _uvList.Add(new Vector2(1,1));
            _uvList.Add(new Vector2(0,1));

            m.SetUVs(0,_uvList);

            // 前面
            _triList.Add(0);
            _triList.Add(1);
            _triList.Add(2);


            _triList.Add(0);
            _triList.Add(2);
            _triList.Add(3);


            // 後面
            _triList.Add(4);
            _triList.Add(7);
            _triList.Add(5);

            _triList.Add(5);
            _triList.Add(7);
            _triList.Add(6);

            // 上面
            _triList.Add(8);
            _triList.Add(9);
            _triList.Add(10);

            _triList.Add(8);
            _triList.Add(10);
            _triList.Add(11);

            // 下面
            _triList.Add(14);
            _triList.Add(12);
            _triList.Add(15);

            _triList.Add(12);
            _triList.Add(14);
            _triList.Add(16);

            // 左面
            _triList.Add(16);
            _triList.Add(17);
            _triList.Add(18);

            _triList.Add(16);
            _triList.Add(18);
            _triList.Add(19);


            // 右面
            _triList.Add(20);
            _triList.Add(23);
            _triList.Add(21);

            _triList.Add(21);
            _triList.Add(23);
            _triList.Add(22);


            m.SetTriangles(_triList,0);

            return m;

        }
    }
}

執行結果如下圖所示:
CubeMesh2執行結果

結果正確,nice!

(7) 總結
  1. 共用頂點的面,如果uv座標不能共用的話,Unity中需要新增重複的頂點。
  2. 工欲善其事必先利其器。有了檢視Mesh資訊的工具,事半功倍。不容易出錯,出了錯也比較容易找到問題所在。
(8) 預告

接下來,將會給自定義的mesh貼上正確的mineCraft元素的貼圖。
期間,會修改檢視Mesh資訊的工具,顯示我們需要的資訊。