7.6K Views
September 09, 17
スライド概要
2017/9/10に開催されたunity道場スペシャル 2017大阪の講演スライドです。
講師:安原 祐二(ユニティ・テクノロジーズ・ジャパン合同会社)
講演動画:https://youtu.be/g7vsR0l7eBM
知ってはいるけれどピンとこない、そんなクォータニオンについて基本となる概念からたっぷりと、3DCGの基礎も交えて丁寧に説明していきます。高校で数学が苦手だった人でも理解できる内容になります。
こんな人におすすめ
・アニメーションを製作するアーティスト
・3Dゲームで回転を扱いたいエンジニア
得られる知見
・クォータニオンとオイラー角の違い
・CGにおける行列の役割
・複素数の不思議
Unityのイベント資料はこちらから:
https://www.slideshare.net/UnityTechnologiesJapan/clipboards
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
クォータニオン完全マスター ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二
Quaternion struct in UnityEngine Description クォータ二オンは回転を表すのに使用されます。 コンパクトであり、ジンバルロックの問題がなく、簡単に補間できます。 Unity はすべての回転を表現するのにクォータニオンを内部的に使用します。 しかし複雑な数字にもとづいており、直感的でない側面があります。 このため個別のクォータニオン成分 (x, y, z, w) をアクセスまたは修正することは殆どありません。 Unity スクリプトリファレンスより抜粋
本セッションの旅程 クォータニオン 複素数 CG オイラー角 座標変換
座標変換
CGの きほん たくさんの点を動かす! © Unity Technologies Japan/UCL
y 二次元座標 P(1, 2) x
y P(1, 2) P’(-2, 1) x
ある変換で点を移動 P(1, 2) 変換 P’(-2, 1) ある変換=なんらかのルール
同じ変換でたくさんの点を移動 P(1, 2) Q(2, 0) R(1, 1) 変換 変換 変換 P’(-2, 1) Q’(0, 2) R’(-1, 1)
P(1, 2) 変換 P’(x’, y’) = [変換]P(x, y) こういう式にしてみようx’ = ax + by y’ = cx + dy P’(-2, 1)
色々できそう! x’ = ax + by y’ = cx + dy a=1 b=0 c=0 d=1 なら x’ = 1x + 0y y’ = 0x + 1y x’ = x y’ = y 変化しない変換
色々できそう! x’ = ax + by y’ = cx + dy a=1 b=0 c=0 d=1 なら a=0 b=1 c=1 d=0 なら x’ = 1x + 0y x’ = 0x + 1y y’ = 0x + 1y y’ = 1x + 0y x’ = x x’ = y y’ = y 変化しない変換 y’ = x xy入れ替え変換
abcdをまとめてしまおう x’ = ax + by y’ = cx + dy ( ) a b c d これを行列と呼ぶ!
変換を行列Mとして P(1, 2) 変換 P’(-2, 1) P’(x’, y’) = [変換]P(x, y) P’=MP
変換を行列Mとして P(1, 2) P’(-2, 1) 変換 P’(x’, y’) = [変換]P(x, y) P’=MP P’= ( ) a b c d P
行列に点ベクトルを掛ける x’ y’ ( ) ( )( ) = a b c d x y x’ = ax + by y’ = cx + dy 例えば ( ) ( )( ) -2 = 1 a b c d 1 2 -2 = a1 + b2 1 = c1 + d2
行列に点ベクトルを掛ける -2 = a1 + b2 1 = c1 + d2
( ) ( )( ) -2 = 1 a +b c +d -2 = a1 + b2 1 = c1 + d2 1 2 2
三次元では3x3行列になる x’ = ax + by + cz y’ = dx + ey + fz z’ = gx + hy + iz ( ) a b c d e f g h i 概念は同じなので 以降も二次元で続けます
いろいろな行列 ( ) 2 0 0 2 2倍に拡大 スケーリング行列
いろいろな行列 ( ) 2 0 0 2 2倍に拡大 スケーリング行列 ( cosθ -sinθ sinθ cosθ ) θ回転する 回転行列
デモ
y あれ? 移動は? x 行列の値をどう工夫しても移動はできない!
移動するために式を変更 x’ = ax + by y’ = cx + dy 追加! x’ = ax + by + s y’ = cx + dy + t これで(s,t)で移動できる
移動を実現するために行列を拡張 ( ) a b c d 何かと都合がいいので 3x3 にする x’ = ax + by + s y’ = cx + dy + t ( ) a b s c d t ( ) a b s c d t 0 0 1
デモ
三次元でも同様にして移動を実現 x’ y’ z’ 1 ()( = a d g 0 b e h 0 c f i 0 s t u 1 x y z 1 )( ) つまり三次元では4x4行列を使う
二次元用3x3行列の 各部分の役割 回転 ( ) a b s c d t 0 0 1 移動
三次元用4x4行列の 各部分の役割 回転 ( a d g 0 b e h 0 c f i 0 s t u 1 ) 移動
三次元用4x4行列の 各部分の役割 回転 transform.rotation Quaternion クォータニオン で作る ( a d g 0 b e h 0 c f i 0 s t u 1 ) 移動 transform.position Vector3 ベクトル で作る
異なる変換をいくつも重ねる P’=(○(○(○(○P)))) これを点ごとに計算するのはたいへん…
いくつも変換する場合は事前に計算しておける P’=(○(○(○(○P)))) P’=○○○○P M=○○○○ を 事前に計算して… P’=MP 点ごとにMを掛ければ済む
クォータニオン 複素数 CG オイラー角 座標変換
CGに応用する行列
3つの変換行列 • モデル行列 • ビュー行列 • プロジェクション行列
すべての変換前 モデリングした物体の座標 データにはこの座標が 格納されている
Transformで 移動・回転 この変換が モデル行列
シーンにはカメラがある カメラ Transformで 移動・回転 z
カメラが原点・ゼロ回転 になるように変換 z 空間全体をモニタの位置に合わせる! この変換が ビュー行列
画角や near, far を処理 w に z を格納など… この変換が プロジェクション行列
Unity組み込みシェーダ UnityShaderVariables.cginc より抜粋 M:モデル V:ビュー P:プロジェクション
モニタに映すための 透視変換 遠くのものを小さく! この変換は 行列ではなく奥行きで割る
透視変換のようす
クォータニオン 複素数 CG オイラー角 座標変換
オイラー角方式
二次元の回転はシンプル 回転軸はひとつ ( cosθ -sinθ sinθ cosθ ) θ回転の 回転行列
y 三次元では3軸の 回転量をそれぞれ記述 直感的! x z あらゆる姿勢を表現可能 これがオイラー角方式
Unity の Inspector の Rotation は… 表示に関しては オイラー角方式
オイラー角方式の回転には順番がある Z X Z軸まわりの回転行列 Y Y軸まわりの回転行列 X軸まわりの回転行列 P’=YXZP Unityは ZYXの順番
プログラム例: オイラー角で作った回転を代入 transform.rotation = Quaternion.Euler(x, y, z); ① ② ③ 変換を3回重ねている
クォータニオン 複素数 CG オイラー角 座標変換
複素数と複素平面
クォータニオンは 複素数の拡張
問題
は虚数単位
虚数単位 虚数の定義
複素数 :実部 :虚部
y 普通の平面 2 P(1, 2) 1 x
虚軸 複素平面 2 1+2i 1 実軸
虚軸 半径1の円周上の点を 考える 実軸 単位円と呼ぶ
虚軸 1+0i つまり 1 実軸
虚軸 -1+0i つまり -1 1 実軸
虚軸 0+i つまり i -1 1 実軸
虚軸 i -1 1 0-i つまり -i 実軸
虚軸 i -1 1 -i 実軸
虚軸 i 45° -1 1 -i 実軸
虚軸 i sin45° 45° cos45° -1 -i 1 実軸
虚軸 i cos45°+isin45° 0.7+0.7iぐらい sin45° 45° cos45° -1 -i 1 実軸
虚軸 i cosθ+isinθ sinθ θ cosθ -1 -i 1 実軸
重大な事実 単位円上の点は回転だった
虚軸 i ゼロ回転 -1 1 -i 実軸
虚軸 i 180°回転 -1 1 -i 実軸
虚軸 i -1 90°回転 1 -i 実軸
虚軸 i -1 1 270°回転 -i 実軸
虚軸 i -1 1 実軸 もしくは -90°回転 -i 270°と-90°は 同じ意味
もっと重大な事実 点と点を掛けると回転する
虚数の性質
虚軸 i i は90°回転 -1 i 1 -i 実軸
虚軸 2 i は i をさらに 90°回転 i -1 i 1 つまり i 2 = -1 -i 実軸
虚軸 -1をさらに 180°回転 i -1 -1 1 実軸 つまり (-1) 2 = 1 -i
虚軸 i 0.7+0.7i -1 1 -i 実軸
虚軸 i 0.98i 0.7+0.7i -1 1 -i 実軸
虚軸 = 回転角を足す と 複素数を掛ける は 同じ意味! cos2θ+isin2θ i (cosθ+isinθ) cosθ+isinθ 2θ θ -1 1 -i 実軸 2
ところで… この方程式を解いてみよう
虚軸 i 1 -1 1 -i 実軸
虚軸 i 1 120° -1 1 -i 実軸 から正三角形!
クォータニオン 複素数 CG オイラー角 座標変換
クォータニオン
考案者はハミルトン (William Rowan Hamilton 1805-1865) 「複素平面の三次元版は 作れないものか…」
虚軸を3つにすればうまくいく! 複素数 クォータニオン i が虚数単位 i, j, kが虚数単位 i 2 =-1 i 2 =-1 ij=k ji=-k j 2 =-1 k 2 =-1 jk=i ki=j kj=-i ik=-j 驚愕のアイデア!
クォータニオンは複素数の三次元版 複素数 クォータニオン a+ib a+ib+jc+kd 複素数の要素はふたつ クォータニオンの要素は4つ
クォータニオン要素を x, y, z, w とする ix+jy+kz+w (x, y, z, w) 虚部 実部
複素平面の回転軸は 固定だった
ix+jy+kz+w 立体の場合 (x, y, z, w) (x, y, z) 回転軸が 虚部のベクトル 虚部 実部
複素数の回転と クォータニオンの回転 複素数 クォータニオン cosθ+isinθ θ θ cosー+nsinー 2 2 n=inx+jny+knz n(nx, ny, nz)は回転軸のベクトル
重大な事実 あらゆる回転は ひとつの軸回転で表現できる そしてクォータニオンは 軸回転を表現する
あらゆる回転は ひとつの軸回転で表現できる クォータニオンなら 一発で決まる!
便利なテクニックその1 LookRotation 目標位置 transform.rotation = Quaternion.LookRotation(target_position transform.position); 自分の位置
実演
便利なテクニックその2 Slerp (Spherical Linear Interpolation):球面線形補間 transform.rotation = Quaternion.Slerp(transform.rotation, target_rotation, 0.1f); 現在の値 目標の値 適当な率
実演
便利なテクニックその2 水平を維持しない回転 var rot = Quaternion.LookRotation(diff); 第二引数に Vector.up が省略されている var up = transform.TransformVector(Vector3.up); var rot = Quaternion.LookRotation(diff, up); 自分の姿勢から up ベクトルを作る
実演
回転の難しさ
3Dの回転は難しい • 「わかった!」とはならない(普通の人は) • 難しいという事実を知るのが大事 • クォータニオンは複素数で理解しよう
虚軸 逆変換について i -1 1 -i 実軸 逆変換は 虚部の符号を反転
応用テクニック 逆変換の自前実装 虚部を反転するだけで逆クォータニオン
おしまい