[引擎]之三:在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,效果是這樣的:
檢視其頂點資訊,如下圖所示:
可以看出,同樣的一個位置的頂點由三個。
因為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;
}
}
}
執行結果如下圖所示:
結果正確,nice!
(7) 總結
- 共用頂點的面,如果uv座標不能共用的話,Unity中需要新增重複的頂點。
- 工欲善其事必先利其器。有了檢視Mesh資訊的工具,事半功倍。不容易出錯,出了錯也比較容易找到問題所在。
(8) 預告
接下來,將會給自定義的mesh貼上正確的mineCraft元素的貼圖。
期間,會修改檢視Mesh資訊的工具,顯示我們需要的資訊。