2018年3月12日

Unity メッシュをねじる


Quaternionを使ってメッシュの頂点をねじってみるテスト…



/r/Unity3Dを眺めていると、Blenderでおなじみチンパンジーのスザンヌちゃんの頭がぐにゃぁ~と捻じれる動画が投稿されていました。なんだか面白そう!

TIL I should have taken the time to learn about matrices a long time ago

行列(Matrix)と四元数(Quaternion)を組み合わせると「ねじれの処理がたった3行のコードで書けるよ!」ということで具体的なコードが示されていたのですが、すかさず「(別に行列を使わなくても) transform.TransformPoint と transform.InverseTransformPoint でいい。本当にすごいのは四元数の掛け算のほう」というコメントが。


というわけで試しにクォータニオンでねじれの処理を実装してみました。

using UnityEngine;

[RequireComponent(typeof(MeshFilter))]
public class MeshTwist : MonoBehaviour {

 public Vector3 axis = new Vector3(0, 1, 0);
 public float angle;

 MeshFilter meshFilter;
 private Vector3[] originalVerts;
 private Vector3[] newVerts;
 
 void Start ()
 {
  meshFilter = GetComponent<MeshFilter>();
  originalVerts = meshFilter.mesh.vertices;
  newVerts = new Vector3[originalVerts.Length];
 }

 void Update()
 {
  for (int i = 0; i < originalVerts.Length; i++)
  {
   newVerts[i] = Quaternion.Euler(axis * angle * originalVerts[i].z) * originalVerts[i];
  }
  meshFilter.mesh.vertices = newVerts;
 }
}

完成!
「いや、そこまでねじって投げへんのかーい!」

実際に頂点の位置を計算しているのは24行目のみ。確かにたった1行のQuaternionの処理だけでこんな面白い効果が実現できています。…あれ、じゃあやっぱり行列は必要ない?

一応スレ主による反論としては「transform.TransformPoint が別スレッドで使えない(スレッドセーフでない?)」ことと「軸のtransformの位置とスケールを無視したい」ことが挙げられていました。そういえば投稿された動画をよく見ると、途中からお猿さんとは別のオブジェクト(軸?)をぐりぐり回転させています。自分が書いたコードとは変形の挙動も違うっぽいので、たぶんなんかうまいこと行列を使ってるんだろうな~☆(適当)

あと、メッシュの頂点位置をひとつひとつ変更するというのは場合によってはかなり重い処理になるので、上の例みたいに何も考えずUpdateで毎フレーム実行したりするのはやめましょう…

あ、それから今回利用したディスコボロスの3Dモデルはコペンハーゲン国立美術館でスキャンされたデータを使わせていただきました。こちらの公式サイトか、Sketchfabのページから誰でも無料でダウンロード可能です。

0 件のコメント:

コメントを投稿