本日はMRMT枠です。
今回はUV情報をスクリプトベースでメッシュに組み込む方法を調べていきます。
〇UnityC#でUVを含むメッシュを生成する
C#で頂点情報に加えUV情報を基にメッシュを構築する方法は次のようになります。
using System.Collections.Generic; using UnityEngine; public class CubeMeshGenerator : MonoBehaviour { // メッシュの頂点座標 private Vector3[] vertices = new Vector3[] { // 前面 (Front) new Vector3(-0.5f, -0.5f, 0.5f), // 左下 new Vector3(0.5f, -0.5f, 0.5f), // 右下 new Vector3(0.5f, 0.5f, 0.5f), // 右上 new Vector3(-0.5f, 0.5f, 0.5f), // 左上 // 後面 (Back) new Vector3(0.5f, -0.5f, -0.5f), // 右下 new Vector3(-0.5f, -0.5f, -0.5f), // 左下 new Vector3(-0.5f, 0.5f, -0.5f), // 左上 new Vector3(0.5f, 0.5f, -0.5f) // 右上 }; // メッシュの三角形インデックス private int[] triangles = new int[] { // 前面 (Front) 0, 1, 2, 2, 3, 0, // 後面 (Back) 4, 5, 6, 6, 7, 4, // 左側 (Left) 5, 0, 3, 3, 6, 5, // 右側 (Right) 1, 4, 7, 7, 2, 1, // 上面 (Top) 3, 2, 7, 7, 6, 3, // 底面 (Bottom) 0, 5, 4, 4, 1, 0 }; // メッシュのUV座標 private Vector2[] uvs = new Vector2[] { // 前面 (Front) new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(0f, 1f), // 後面 (Back) new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(1f, 1f) }; private void Start() { // メッシュを作成して割り当てる Mesh mesh = new Mesh(); mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = uvs; // メッシュを表示するオブジェクトに割り当てる MeshFilter meshFilter = GetComponent<MeshFilter>(); MeshRenderer meshRenderer = GetComponent<MeshRenderer>(); meshFilter.mesh = mesh; } }
頂点の配列とUVの配列していることを確認しMeshクラスのuvにデータを代入することでuvを持ったメッシュを生成できます。
例えば上記の例の場合は最初の頂点であるVector3(-0.5f, -0.5f, 0.5f)に対応するUVはVector2(0f, 0f)となります。
上記のコードを実行した場合前面と背面にUVが持たれており、ほかの面にUV情報を持たせるためにはUV情報を与える必要があります。
しかし頂点数とUV数は一致している必要があるため面ごとにUVを追加した場合はOutOfRangeのエラーが出てしまいます。
では一度UVと頂点と面について関係を負荷彫りしていきます。
〇UV情報と頂点情報の関連
UV情報は一般的に頂点1つに対して1つ(U,Vの2次元ベクトル)のデータが格納されます。
しかし実際のUVはBlenderのUVEditorを見るとわかるのですが、各面に依存しており、面をシェアしている頂点で複数のUVを持つようなことになります。
つまり実際のUVは頂点ではなく面に依存しています。
これを処理するためには次のようにfor文でcubeTrianglesの数=メッシュのインデックスの数実行します。
この時、Unityではメッシュを三角ポリゴンとして処理するため3の倍数でfor文を回していきます。
正しくUVと頂点を適応するには次のような処理になります。
// UV座標を計算して設定 for (int i = 0; i < cubeTriangles.Length; i += 3) { //頂点とインデックスの対応 =メッシュの作成 Vector3 v0 = cubeVertices[cubeTriangles[i]]; Vector3 v1 = cubeVertices[cubeTriangles[i + 1]]; Vector3 v2 = cubeVertices[cubeTriangles[i + 2]]; //メッシュの法線を取得 Vector3 normal = Vector3.Cross(v1 - v0, v2 - v0).normalized; //それぞれの頂点に格納されているuv情報 Vector2 uv0 = new Vector2(0f, 0f); Vector2 uv1 = new Vector2(1f, 0f); Vector2 uv2 = new Vector2(1f, 1f); // List<Vector2> uvs = new List<Vector2>()のリストに追加 uvs.Add(uv0); uvs.Add(uv1); uvs.Add(uv2); vertices.Add(v0); vertices.Add(v1); vertices.Add(v2); triangles.Add(i); triangles.Add(i + 1); triangles.Add(i + 2); } //メッシュにuvを適応 mesh.uv = uvs.ToArray();
これを実行すると次のように6面すべてにUVが適応され画像が張られるようになります。
以上でC#でUVが適応されるキューブを生成できました。
〇コード一覧
今回使用したコードは次のようになります。
using System.Collections.Generic; using UnityEngine; public class CubeMeshGenerator : MonoBehaviour { private void Start() { Mesh mesh = new Mesh(); GetComponent<MeshFilter>().mesh = mesh; List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); List<Vector2> uvs = new List<Vector2>(); // 頂点座標 Vector3[] cubeVertices = new Vector3[] { new Vector3(-0.5f, -0.5f, 0.5f), new Vector3(0.5f, -0.5f, 0.5f), new Vector3(0.5f, 0.5f, 0.5f), new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0.5f, -0.5f, -0.5f), new Vector3(-0.5f, -0.5f, -0.5f), new Vector3(-0.5f, 0.5f, -0.5f), new Vector3(0.5f, 0.5f, -0.5f) }; // 三角形インデックス int[] cubeTriangles = new int[] { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 5, 0, 3, 3, 6, 5, 1, 4, 7, 7, 2, 1, 3, 2, 7, 7, 6, 3, 5, 4, 1, 1, 0, 5 }; // UV座標を計算して設定 for (int i = 0; i < cubeTriangles.Length; i += 3) { Vector3 v0 = cubeVertices[cubeTriangles[i]]; Vector3 v1 = cubeVertices[cubeTriangles[i + 1]]; Vector3 v2 = cubeVertices[cubeTriangles[i + 2]]; Vector3 normal = Vector3.Cross(v1 - v0, v2 - v0).normalized; Vector2 uv0 = new Vector2(0f, 0f); Vector2 uv1 = new Vector2(1f, 0f); Vector2 uv2 = new Vector2(1f, 1f); uvs.Add(uv0); uvs.Add(uv1); uvs.Add(uv2); vertices.Add(v0); vertices.Add(v1); vertices.Add(v2); triangles.Add(i); triangles.Add(i + 1); triangles.Add(i + 2); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.uv = uvs.ToArray(); } }
なお上記コードでわかるように頂点数が元から増える形となり、これによって頂点数=uv数が守られるようになっています。