5.7K Views
June 16, 17
スライド概要
2017/6/3に開催されたUnity道場 博多スペシャルの講演スライドです。
講師:安原 祐二(ユニティ・テクノロジーズ・ジャパン合同会社)
講演動画:https://youtu.be/uKWLPU8gfIY
知ってはいるけれどピンとこない、知ってはいるけれどピンとこない、そんなクォータニオンについて基本となる概念からたっぷりと、丁寧に説明していきます。行列についても解説しますので、これからシェーダプログラミングに取り組もうとするエンジニアにも役に立つ内容です。数学に苦手意識のある人も、この機会にマスターしてしまいましょう!
こんな人におすすめ
・プログラマ全般および数学に興味のある人
受講者が得られる知見
・クォータニオンの意味
・行列とクォータニオンの関係
・数学的な背景
Unityのイベント資料はこちらから:
https://www.slideshare.net/UnityTechnologiesJapan/clipboards
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
クォータニオン完全マスター ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二
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’(x’, y’) = [変換]P(x, y) x’ = ax + by y’ = cx + dy
色々できそう! 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倍に拡大 スケーリング行列 ( 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 -i 実軸
虚軸 i 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)は回転軸のベクトル
重大な事実 あらゆる回転は ひとつの軸回転で表現できる そしてクォータニオンは 軸回転を表現する
あらゆる回転は ひとつの軸回転で表現できる クォータニオンなら 一発で決まる!
クォータニオンのテクニック
便利なテクニック Slerp (Spherical Linear Interpolation):球面線形補間 transform.rotation = Quaternion.Slerp(transform.rotation, target_rotation, 0.1f); 現在の値 目標の値 適当な率
凝ったテクニック バネトルク 目標との差分に比例したトルクをかける
基本戦略: 目標への差分を求めて、トルクに変換する
差分クォータニオンの求めかた 差分=目標の値×現在の値 -1
実演 バネトルク実装例
4要素の符号を反転しても 同じ回転になる (x, y, z, w) (-x, -y, -z, -w)
実は回転の方向が異なる (x, y, z, w) (-x, -y, -z, -w)
バネトルクを完成させる テクニック (x, y, z, w) (-x, -y, -z, -w) wが負のときに反転すると 短い方の回転になる
修正済みコード 追加
水平を維持しないテクニック var rot = Quaternion.LookRotation(diff); 第二引数に Vector.up が省略されている var up = transform.TransformVector(Vector3.up); var rot = Quaternion.LookRotation(diff, up); 自分の姿勢から up ベクトルを作る
修正済みコードその2 追加
デモ
このあと学習を続けるなら… • トルクや物理を詳しく知りたい→Unity道場札幌講演をぜひ! https://www.youtube.com/watch?v=FqjM9oujyNE&feature=youtu.be • クォータニオンの応用例を知りたい→Unite2017Tokyoをぜひ! https://www.youtube.com/watch?v=6EtTI5xC524 27分あたりから θ θ θ cosー+nsinー ー 2 2 と、 2 になるのか気になる→ ブログ「クォータニ • なんで オンで回転を表現する定義にθ/2が使用される理由」をぜひ! http://qiita.com/yuji_yasuhara/items/a5b7c489e1d521adbd72
参考:Inverse自前実装 虚部を反転するだけで逆クォータニオン
虚軸 共役複素数と呼ぶ i マメ知識 -1 1 -i 実軸 逆変換は 虚部の符号を反転
おしまい