[Unity]３Ｄ？いいえ、一枚絵です。頂点変形でイラストを立体的に見せるネタ

**[グレンジ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/grenge)** 17日目担当の、flankidsです。
普段は操作、アニメーション、カメラワークなどで遊び心地を作ることを主にやっています。楽しいものを作るのが好きです！

今回は、一枚絵を立体的に見せるネタについて書いてみました。
**もしこの記事がお役に立てたら「いいね」を押してもらえると、とても励みになります！**

# 一枚絵を立体的に見せる

最近、デザイナーさんとの雑談でSpineを使ってイラストを立体的にアニメーションさせる作品が面白いよ〜と下記のページを共有してもらいました。

https://www.behance.net/gallery/91007627/UI-Animation-Spine


下記のSpineウェビナーを見たところ、立体表現用のボーンを割り当てて長方体を回すような要領で画像を頂点変形させることで、一枚絵をあたかも立体物かのように見せていることがわかりました。

- [Spiners MeetUp vol.3 ～Spineによる立体表現の紹介・解説～ | Youtube](https://www.youtube.com/live/dCACZegnaQw?t=899s)

考え方はシンプルなのでUnityだけでもこの表現なら再現できそうだと思い、挑戦してみました。

# Unityでやってみた

![Movie_023.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/42586f73-dd7c-ee68-9f53-9894e9d6d37c.gif)

こんな感じでそれっぽく再現できました！
１枚の画像を切り分けたりせず、そのままで立体的に見せることができています。

元画像には下記のイラストを使用させて頂きました。
- [魔導書 星形 | アイコンまてりあるず](https://icon.game-materials.com/magicbook/)


# 仕組み

イラストとCubeを並べて、正面から見た時に立体的な形状が一致するようにCubeの角度やスケールを調整します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/a26f3a35-daac-92f1-e6a4-3d8b7b3ea9a5.png)

Cubeの頂点座標を平面に転写して、これらの座標からMeshを生成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/19fe0602-4624-69bf-6638-77fa4aa0269b.png)

※法線方向はZの正方向になっていますが、2D表示物は大抵Zの負の方向を向いているのでとりあえずシーン上でZスケールを-1にしています


```CreateMesh.cs
using System.Collections.Generic;
using UnityEngine;
using TriangleNet.Geometry;

private void CreateMesh() {
    // cubeの頂点から、Mesh用の情報を作成
    _vertices = new Vector3[_cubeVertices.Length];
    _uvs = new Vector2[_cubeVertices.Length];
    for(var i = 0; i < _cubeVertices.Length; i++) {
        var vertex = (Vector2)_cubeVertices[i];
        _vertices[i] = vertex;
        // FIXME: 頂点は-1~1の範囲内に収める前提の処理
        _uvs[i] = vertex + new Vector2(0.5f, 0.5f);
    }

    if(_mesh == null) {
        _mesh = new Mesh();
        _meshFilter.mesh = _mesh;
    }

    // 生成したMeshに頂点・UVを登録
    _mesh.SetVertices(_vertices);
    _mesh.SetUVs(0, _uvs);

    
    // Triangle.NETを使って_verticesからtrianglesを作成
    // Triangle.NET: 不規則な点集合に対してDelaunay三角形分割を行うC#ライブラリ
    var polygon = new Polygon();
    foreach (var vertex in _vertices) {
        polygon.Add(new Vertex(vertex.x, vertex.y));
    }

    var triangulatedMesh = polygon.Triangulate();
    var triangles = new List<int>();
    foreach (var triangle in triangulatedMesh.Triangles) {
        for(var i = 0; i < 3; i++) {
            triangles.Add(triangle.GetVertexID(i));
        }
    }

    _mesh.SetTriangles(triangles, 0);
}
```

これで生成したMeshにスプライトが収まるように表示できるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/745e9ab8-16a8-f414-1180-7a693408d430.png)


さらに、ここからCubeの回転に合わせてUVは維持してVertexを更新することで立体的にテクスチャが歪み、イラストが立体物のように見えるようになります。

![book_roll.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184294/ddec7390-929b-169b-e433-f5ea0a74de26.gif)


```UpdateVertices.cs
private void UpdateVertices() {
    // Cubeの上の座標を取得（zは0にする）
    GetVertices();

    // 頂点を更新。UVはそのまま
    for(var i = 0; i < _cubeVertices.Length; i++) {
        var vertex = (Vector2)_cubeVertices[i];
        _vertices[i] = vertex;
    }
    _mesh.SetVertices(_vertices);
}
```

# 所感

想像以上にそれっぽく動いて楽しいです。
仕組み上複雑な形状には対応できませんし、じっくり見ると歪みが目についてしまいますが、冒頭に記載したような参考元のアニメーションのようにプリミティブな形状をしたイラストの短い尺の演出には十分対応できそうです。
かなりリッチな見栄えになりそう。

そういう用途を考えると、UGUIとして表示するためにいろいろ手直しが必要ですね。
また、形状を決めてアニメーションを作るところまでをデザイナーさんが自由に作れるまでにはまだまだユーザーフレンドリーな形にはなっていないので、そのあたりもエディタ拡張を考えたいところ。

Spine含め、この手のツールやライブラリは今やたくさんありそうなので、いろいろ触って参考にしたいです。

# 参考資料
- [【Unity】頂点にUV座標を書き込んでメッシュに画像（テクスチャ）を貼り付ける方法 | 渋谷ほととぎす通信](https://shibuya24.info/entry/unity-mesh-dynamic-apply-texture)
    - Meshを0から作ったの初めてだったんですが、こちらの記事のUVの説明に助けられました
- [NuGetForUnityを使うときの注意点メモ | Qiita](https://qiita.com/toRisouP/items/6c735e5fc28522853e5b)
    - Triangle.NETをインポートするためにNuGetが必要でした