回転変換を考察する

回転変換を考察する


<メモ>

3次元空間上の回転



<抜粋>
複素数を使って2次元空間上の(原点中心の)回転を表すことができました。 すなわち、複素数 a + ib を2次元ベクトル a, b とみなし、 cosθ + isinθ を掛けることで角度 θ の回転計算を行うことができます。 これと同様に、四元数を使うと、3次元空間上の(原点を含む軸中心の)回転を表すことができます。

まず、3次元空間上の回転というものがどういう式で表されるかについて説明します。 3次元空間上の回転を表すためには、回転軸ベクトル p と回転角度 θが必要になります。 回転軸ベクトルの絶対値は意味を持たないので、| p | = 1 であるものとしてます。

座標ベクトル u で表される点 A を、回転軸 p を中心に角度 θ 回転した点 A' の座標ベクトル u' は、以下のような計算で求めることができます。

u' = sinθ u×p + cosθ ( u - ( u・p )p ) + ( u・p )p

この数式をunityのC#に落とし込んでみる

  Vector3 p, u;
   Vector3 answer;
   
   void Start ()
   {
       p = Vector3.up;
       u = new Vector3 (1, 0, 1);
       float theta = -90.0f;
       
       answer = Mathf.Sin (theta * Mathf.Deg2Rad) * Vector3.Cross (u, p)
           + Mathf.Cos (theta * Mathf.Deg2Rad) * (u - Vector3.Dot (u, p) * p)
               + Vector3.Dot (u, p) * p;
   }

(0.9999, 0.0, -1)

このように自前で計算しても Quaternion.AngleAxis 関数のような事が可能となる

アナログインプットをベクトルとして考えてみる



以下の事が考察できる

  • アナログパッドの入力から、「なす角のθとしてのベクトル(正規化(normarized)されたベクトル値)」が直接拾える
  • 逆に考えれば-1~0~1の範囲のfloatの値がふたつあれば360度の角度を作れる

using UnityEngine;
using System.Collections;

public class InputDirectionTest : MonoBehaviour {
  
   float h,v;
   Vector3 inputVec;
   Transform _transform;
   
   void Start () {
       _transform = transform;
   }
   
   void Update () {
       h=Input.GetAxis("Horizontal");
       v=Input.GetAxis("Vertical");
       
       inputVec = new Vector3(h,0,v);
       
       _transform.rotation= Quaternion.LookRotation(inputVec);
   }
}


θからベクトルを求める


入力を単位円、なす角のθとして扱い一度ベクトルにして、その方向にLookRotationするサンプル

dot2.jpg

少し気が付いた事を何点か書くと…

  • Horizontal等のアナログ入力は標準設定でwasdキーなどで移動させると内部で値が遷移している
(例えばdキーで右に入力すると値がいきなり1になるわけでなく小数点の値が加算されてやがて1になる)

  • 左右や上下等の対となった反対入力には即座に対応している
(つまりa、dと素早く入力すると、値は補完されず切り替わっている。ストリートファイターのキャンセル入力みたいになっている)

  • アナログパッドなどで入力すると、0.5や0.8、1など「入力された位置情報」がダイレクトに反映される

アナログパッドとデジタルパッド入力両方をゲームに反映させる際はこの違いを認識しておいた方が良い
これは場合によりゲーム性が変わってしまうぐらい影響力があると思われる

  Vector3 vec1;
   float theta;
   
   void Update ()
   {
       theta = Input.GetAxis ("Horizontal");
       vec1 = new Vector3 (Mathf.Sin (theta * 90 * Mathf.Deg2Rad), 0, Mathf.Cos (theta * 90 * Mathf.Deg2Rad));
       
       transform.rotation = Quaternion.LookRotation (vec1);
       Debug.DrawLine (transform.position, transform.position + vec1, Color.white);
   }



こちらにすると360度カバーする。つまり一次元の値θで360度の回転を表現して、
長さ1のなす角、複素数の虚数iの軸を生成して二次元の値を生成できるようになる

赤い球のVector3が2次元の位置を指し示している

つまり1次元の値を2次元に拡張できる

using UnityEngine;
using System.Collections;

public class projectionTest1 : MonoBehaviour
{
  public Transform thetaPositionPref;        //大きさ0.2の赤色のsphereオブジェクトprefab 
   public Transform thetaText, vectorText;
   TextMesh thetaMes, vectorMes;
   Transform thetaPosition;
   Vector3 vec1;
   float theta;
   
   void Start ()
   {
       thetaPosition = Instantiate (thetaPositionPref, Vector3.zero, Quaternion.identity) as Transform;
       thetaMes = thetaText.GetComponent<TextMesh> ();
       vectorMes = vectorText.GetComponent<TextMesh> ();
   }
   
   void Update ()
   {
       theta = Input.GetAxis ("Horizontal");
       vec1 = new Vector3 (Mathf.Sin (theta * 180 * Mathf.Deg2Rad), 0, Mathf.Cos (theta * 180 * Mathf.Deg2Rad));
       //vec1 = new Vector3 (Mathf.Sin (theta * Mathf.PI), 0, Mathf.Cos (theta * Mathf.PI));
       
       thetaPosition.position = vec1;
       transform.rotation = Quaternion.LookRotation (vec1);
       
       thetaMes.text = theta.ToString ();
       vectorMes.text = string.Format ("({0:f4} , {1:f4} , {2:f4}", vec1.x, vec1.y, vec1.z);
       Debug.DrawLine (transform.position, transform.position + vec1, Color.white);
   }
}

ベクトルから角度を求める


<自前でやってみる>

  Vector3 vec1;
   float angle;
   
   void Start ()
   {
       vec1 = new Vector3 (0.5f, 0, 0.866f);
       angle = 180 / Mathf.PI * Mathf.Atan2 (vec1.z, vec1.x); 
   }


回転変換を考察する


transform.forwardを利用して単純に右に向かせたい場合

  transform.forward = Vector3.right;

とする。右45度なら transform.forward = new Vector3(1,0,1); となる
つまり、この方法では「ベクトル」を使って回転を指定できる

単純に「オイラー角」を指定して回転させたい場合は

  transform.Rotate(0,90,0);

とする。その他の方法としてtransform.rotationを利用する方法もある

  transform.rotation = Quaternion.Euler(0,90,0);

ここでrotationが受け付けている型が「クォータニオン」である事に注意
クォータニオンは直観的に扱うのが難しいので、ここではオイラー角から「クォータニオン」へ変換する関数を利用している

クォータニオンは複数のクォータニオンを掛け合わせることで回転の変換を合成でき、その際にジンバルロックが起きない点が優れている
(オイラー角の乗算ではジンバルロックが起きてしまうのでunityではオイラー角同士の掛算はクォータニオンに変換してからでなければ出来なくなっている)
つまり

  transform.rotation = transform.rotation * Quaternion.Euler(0,45,0) * Quaternion.Euler(-45,0,0);

このような回転の合成操作が出来る

コードの状況によってはベクトルからクォータニオンの角度を得たい場合もあるだろう
その場合は Quaternion.LookRotation や Quaternion.SetLookRotation を利用する

  Quaternion rot = Quaternion.LookRotation(new Vector3(1,0,1));
   transform.rotation = transform.rotation * rot;

値をtransformに渡す際に掛ければ回転が合成できる

この事から変換の関係は以下のようになっていることが理解できる

Euler ⇔ Quaternion ← Vector

?クォータニオンからベクトルにする手段がないようだが
クォータニオンを利用する目的は回転の合成と球面線形補間、オブジェクトの姿勢制御であり、ベクトルにする必要性はないらしい?
必要になる状況が出てくるかどうか、まだちょっとわからないので保留

下で書いてあるが実はクォータニオンにベクトルを掛けることでクォータニオンをベクトルに変換できる事が後で判った

<メモ>
(ベクトル、複素数、内積、外積、変換 → クォータニオンの理解 → 球面は二次元である 円は直線 となるっぽい)


クォータニオンにベクトルを掛ける


ベクトルにクォータニオンは掛けられないが
クォータニオンにベクトルは掛ける事が出来る

//Vector3 vec = Vector3.forward * Quaternion.LookRotation(new Vector3(1,0,1));    //NG! 
Vector3 vec = Quaternion.LookRotation(new Vector3(1,0,1)) * Vector3.forward;
print(vec);

(0.7 , 0.0 , 0.7)

これはとても重要な事で、例えば円錐等の立体的な位置と回転が合成された変換が可能となる
これはある意味、クォータニオンをベクトルに変換している

<円錐(コーン)的に考えてベクトルを作成してみる例>

float radius = 3.0f;
float height = 6.0f;
float degree = 45.0f;
float speed =500;

Vector3 directionY =
  (Quaternion.AngleAxis (degree, Vector3.up) * Vector3.right * radius + (Vector3.up * height)).normalized;
Vector3 directionZ =
  (Quaternion.AngleAxis (degree, Vector3.forward) * Vector3.up * radius + (Vector3.forward * height)).normalized;
       
rigidbody.AddForce (directionY * speed);

<directionYの図解>
cone.jpg

  • 最終更新:2013-11-19 00:44:45

このWIKIを編集するにはパスワード入力が必要です

認証パスワード