質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

3791閲覧

Quaternion.Eulerの掛け算は角度の足し算かどうか。

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

1グッド

0クリップ

投稿2019/01/03 13:48

編集2019/01/04 09:46

前提・実現したいこと

 質問1
transform.localRotationにQuaternion.Eulerを掛けると、
transform.localRotationにQuaternion.Eulerの角度が足されるという認識で合っていますか?

 質問2
Quaternion.Euler同士の掛け算は、角度の足し算という認識で合っていますか?

試したこと

 まず、transform.localRotationもQuaternion.Eulerも、共にQuaternionであると認識しています
(もし間違っていたらご指摘お願いします)。

そして、
transform.localRotation = Quaternion.Euler(X軸回りの回転角度, Y軸回りの回転角度, Z軸回りの回転角度)
とすると、インスペクタのtransformのRotationの各成分が、
Quaternion.Eulerの引数の各成分と一致すると認識しています(もし間違っていたらご指摘お願いします)。

この知識を前提として、
下記のコードを試してみました。

C#

1 void Update () { 2 this.transform.localRotation *= Quaternion.Euler (1f, 0f, 0f); 3 Debug.Log(this.transform.localRotation.eulerAngles); 4 }

 上記コードは、毎フレーム1度ずつX軸回りの回転だけが行われている
(毎フレーム1度ずつX軸回りの角度が足されて回転している)ものになると想定していたのですが、
これのログを取ると、
一見、インスペクタのtransformの角度(Rotation)が1度ずつ増えいてるように見えて、
途中からインスペクタのtransformの角度(Rotation)のZ成分が180度に切り替わったりしたので、
インスペクタ上の表示では純粋な角度の足し算にはならないということは認識しました。

 ただし、シーン上で実際の回転を見る限りでは、1度ずつ角度が加算されているような回転に見えました。

 質問3
これより、上記コードは、
「インスペクタの表示上では、各成分の純粋な足し算の表示にはならないが、
実際の回転では、引数で指定した成分の軸のみに純粋な角度の足し算が行われて回転する」という考え方で
合っていますか?

 質問4
「A *= B」は、「A = B * A」ではなく、「A = A * B」で合っていますか?

質問4は合っているみたいでした。

bochan2👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

質問1、2、3とも、肝になるのは「2つの回転クォータニオンをかけた結果のクォータニオンをオイラー角に直すと、その成分X、Y、Zはかける前のクォータニオンをそれぞれオイラー角に直した成分Xa、Ya、ZaとXb、Yb、Zbの和になるか...X = Xa + Xb、Y = Ya + Yb、Z = Za + Zbとなるか(360°で角度が一周することや計算誤差を考慮した上で)」ということのように思います。

これについては、必ずしもそうなるとは限らない(むしろ、そうならないケースの方が多いんじゃないでしょうか)と考えられます。ただし、X、Y、Zのどれか一つの軸だけで回している限りはご質問者さんのおっしゃるように単純な足し算が通用しそうです。

以下、「必ずしもそうなるとは限らない」についてご説明しようとしたのですが、なんだかごちゃごちゃしてあまり明解にできなかったかもしれません。適当に流し読みいただければけっこうです...

Quaternion.Euler(x, y, z)だと長いので代わりにE(x, y, z)と書くことにすると、もし仮に最初に申し上げた関係が成り立つのであれば、

E(Xa, Ya, Za) * E(Xb, Yb, Zb) == E(Xa + Xb, Ya + Yb, Za + Zb) == E(Xb + Xa, Yb + Ya, Zb + Za) == E(Xb, Yb, Zb) * E(Xa, Ya, Za)

という関係式が成立しないとつじつまが合わないはずです。
クォータニオンの乗算の順序を入れ替えると結果が変化することはおなじみかと思いますが、これでは乗算の順序を入れ替えても結果が変化しないということになってしまい、都合が悪いのではないでしょうか。

また、別の観点から見てみますと、Quaternion.Euler - Unity スクリプトリファレンスには...

Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis.

z軸周りにz度、x軸周りにx度、y軸周りにy度回転する回転を返します。

という記述があります。これには順序にも意味があって、E(x, y, z)E(0, y, 0) * E(x, 0, 0) * E(0, 0, z)と同等の回転になります。
すると...

E(Xa + Xb, Ya + Yb, Za + Zb) == E(0, Ya + Yb, 0) * E(Xa + Xb, 0, 0) * E(0, 0, Za + Zb)

と変形できます。
E(0, Ya + Yb, 0)にご注目いただくと、これはY軸周りに(Ya + Yb)°回転するということになります。
そしてそれはY軸周りにYa°回転してからYb°回転、またはYb°回転してからYa°回転するのと同じはずです。
同じ軸に対して連続して回転を行う場合に限り、「2回の連続回転」と「合計角度での1回の回転」は回転順序によらず同じ結果になるでしょう。
クォータニオン的にも、

式

となり、矛盾はないはずです(参考: 四元数加法定理)。
同じことがX軸、Z軸についても成立しますので、

E(Xa + Xb, Ya + Yb, Za + Zb) == E(0, Ya, 0) * E(0, Yb, 0) * E(Xa, 0, 0) * E(Xb, 0, 0) * E(0, 0, Za) * E(0, 0, Zb)

となり、「Z軸Zb°→Z軸Za°→X軸Xb°→X軸Xa°→Y軸Yb°→Y軸Ya°回転」と翻訳されます。

一方、

E(Xa, Ya, Za) * E(Xb, Yb, Zb) == E(0, Ya, 0) * E(Xa, 0, 0) * E(0, 0, Za) * E(0, Yb, 0) * E(Xb, 0, 0) * E(0, 0, Zb)

は「Z軸Zb°→X軸Xb°→Y軸Yb°→Z軸Za°→X軸Xa°→Y軸Ya°回転」で、

E(Xb, Yb, Zb) * E(Xa, Ya, Za) == E(0, Yb, 0) * E(Xb, 0, 0) * E(0, 0, Zb) * E(0, Ya, 0) * E(Xa, 0, 0) * E(0, 0, Za)

は「Z軸Za°→X軸Xa°→Y軸Ya°→Z軸Zb°→X軸Xb°→Y軸Yb°回転」となり、いずれも各軸の回転順序がE(Xa + Xb, Ya + Yb, Za + Zb)とは一致していません。

ただし、X軸だけしか回転しない場合は...

E(Xa + Xb, 0, 0) == E(Xa, 0, 0) * E(Xb, 0, 0) == E(Xb, 0, 0) * E(Xa, 0, 0)

となり、順序によらない単純な足し算にできるでしょう。

投稿2019/01/04 13:33

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/01/05 13:35 編集

ご回答ありがとうございます。 「~関係式が成立しないとつじつまが合わないはず」等、前半のご説明は理解できたのですが、 後半の方は理解が追い付かず、流し読みになってしまいました、すみません。 念の為、以下の認識で合っていますか? 「Quaternion.Euler同士の掛け算では、後から掛けるQuaternion.Eulerが1つの成分しか値を持っていない場合は、足し算が成り立つ(クォータニオンの掛け算の順序を入れ替えると結果が変化する)。」ということでしょうか? つまり、Quaternion.EulerをEと省略して書くとすると、下記が成り立つ。 E(X, Y, Z) * E(A, 0, 0) == E(X+A, Y, Z) E(X, Y, Z) * E(0, A, 0) == E(X, Y+A, Z)  E(X, Y, Z) * E(0, 0, A) == E(X, Y, Z+A)    下記は成り立たない掛け算の回転。 //掛ける順序が違う為、成り立たない。 E(A, 0, 0) * E(X, Y, Z) ≠ E(X+A, Y, Z)   //後から掛けるクォータニオンが2つ以上の成分で値を持っている為、成り立たない。 E(X, Y, Z) * E(A, B, 0) ≠ E(X+A, Y+B, Z) また、 E(x, y, z) == E(0, y, 0) * E(x, 0, 0) * E(0, 0, z) (掛ける順序にも意味がある) なので、この順序で掛けていっても足し算が成り立つ。 つまり、 E(x, y, z) * E(0, b, 0) * E(a, 0, 0) * E(0, 0, c) == E(x+a, y+b, z+c) が成り立つ。 ただし(ご説明の通り)、y→x→zの順の掛け算を2回行った場合は、 E(x, y, z) * E(0, b, 0) * E(a, 0, 0) * E(0, 0, c) * E(0, b, 0) * E(a, 0, 0) * E(0, 0, c) ≠ E(x+a+a, y+b+b, z+c+c) で成り立たない。 と、認識しているのですが、いかがでしょうか? あと、思い出したのですが、下記2行は同じ処理ということで合っていますか? transform.Rotate(X, Y, Z); transform.localRotation *= Quaternion.Euler(X, Y, Z);
Bongo

2019/01/05 19:03

まず、一つ目について... 成り立つ条件はもっと厳しくて、 E(X, 0, 0) * E(A, 0, 0) == E(X+A, 0, 0) ※ E(0, Y, 0) * E(0, A, 0) == E(0, Y+A, 0)  E(0, 0, Z) * E(0, 0, A) == E(0, 0, Z+A) のはずです。ただし、Zに限ってはE(X, Y, Z) * E(0, 0, A) == E(X, Y, Z+A)となるでしょう。 つまり、 E(X, Y, Z) == E(0, Y, 0) * E(X, 0, 0) * E(0, 0, Z) なので、 E(X, Y, Z) * E(0, 0, A) == E(0, Y, 0) * E(X, 0, 0) * E(0, 0, Z) * E(0, 0, A) となり、一番右にE(0, 0, Z) * E(0, 0, A)が出てきます。これをE(0, 0, Z+A)に置き換えることができ... E(0, Y, 0) * E(X, 0, 0) * E(0, 0, Z+A) == E(X, Y, Z+A) の形になります。 X軸(上の式の※を付けた部分)について補足しますと、すみませんが実はこの軸については追加で考慮するべき事項があります。 回答中では言及していませんでしたが、Xの角度が真上や真下を越えるケース(0°との角度差が90°を越える場合...ご質問者さんが「Z成分が180度に切り替わったりしたので...」とおっしゃる条件ではこうなっているかと思います)では符号が逆転して、 E(X, 0, 0) * E(A, 0, 0) == E(X-A, 0, 0) となってしまうでしょう。クォータニオンからオイラー角を取り出す際の致し方ない問題でして、X軸について計算する場合はご注意ください。 次に、「下記は成り立たない掛け算の回転。」について... E(A, 0, 0) * E(X, Y, Z) ≠ E(X+A, Y, Z)についてはおっしゃる通りです。ただしY軸に限り、先に申し上げたZ軸の時と同様に、 E(0, A, 0) * E(X, Y, Z) == E(0, A, 0) * E(0, Y, 0) * E(X, 0, 0) * E(0, 0, Z) と変形でき、今度は一番左に E(0, A, 0) * E(0, Y, 0)の形が現れます。これをE(0, A+Y, 0)に置き換え... E(0, A+Y, 0) * E(X, 0, 0) * E(0, 0, Z) == E(X, A+Y, Z) == E(X, Y+A, Z) とすることは可能なはずです。 E(X, Y, Z) * E(A, B, 0) ≠ E(X+A, Y+B, Z)についてはその通りですね(もちろんA、Bが0°の場合は==になりますが...)。 その次のE(x, y, z) * E(0, b, 0) * E(a, 0, 0) * E(0, 0, c) == E(x+a, y+b, z+c)については、これは成り立たないでしょう。 E(0, b, 0) * E(a, 0, 0) * E(0, 0, c)はE(a, b, c)を分解しただけであって、結局E(x, y, z)に掛ける際の順序はそのままです。つまりE(x, y, z) * E(0, b, 0) * E(a, 0, 0) * E(0, 0, c)はE(x, y, z) * E(a, b, c)と同等ですので、E(x+a, y+b, z+c)にはならないということになります。 式を成り立たせるには、E(x, y, z)も E(a, b, c)も両方分解して、さらに各因子を互い違いに掛け... E(0, y, 0) * E(0, b, 0) * E(x, 0, 0) * E(a, 0, 0) * E(0, 0, z) * E(0, 0, c) == E(x+a, y+b, z+c) としてやる必要があるはずです(実際には、さらに先に申し上げたX軸の符号反転も考慮しなくてはならないですが...)。各因子を軸ごとにまとめて並べ替えるわけですね。 最後に、 transform.Rotate(X, Y, Z); transform.localRotation *= Quaternion.Euler(X, Y, Z); の二つは同じ処理と言えるでしょう。 https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Transform/ScriptBindings/Transform.bindings.cs でRotateの中身がどうなっているか見ることができますが... // Applies a rotation of /eulerAngles.z/ degrees around the z axis, /eulerAngles.x/ degrees around the x axis, and /eulerAngles.y/ degrees around the y axis (in that order). public void Rotate(Vector3 eulers, [UnityEngine.Internal.DefaultValue("Space.Self")] Space relativeTo) { Quaternion eulerRot = Quaternion.Euler(eulers.x, eulers.y, eulers.z); if (relativeTo == Space.Self) localRotation = localRotation * eulerRot; else { rotation = rotation * (Quaternion.Inverse(rotation) * eulerRot * rotation); } } となっており、まさしくtransform.localRotation *= Quaternion.Euler(X, Y, Z);を行っているようです。
退会済みユーザー

退会済みユーザー

2019/01/06 05:23 編集

ご回答ありがとうございます。 なるほど、さらに厳しい条件だったのですね。 そして、X軸に関してはさらに注意が必要だったのですね。 理解できました。 こちらも成り立たないということで理解できました。 E(x, y, z) * E(0, b, 0) * E(a, 0, 0) * E(0, 0, c) ≠ E(x+a, y+b, z+c) transform.Rotate(X, Y, Z); transform.localRotation *= Quaternion.Euler(X, Y, Z); の2つが同じ処理のご教示ありがとうございます。 なるほど、Rotateの内部処理で、transform.localRotation *= Quaternion.Euler(X, Y, Z); を呼んでいたのですね。理解できました。 1点、他に気になっていたことがありまして、 こちらのサイトなのですが、 http://tsubakit1.hateblo.jp/entry/2014/08/02/030919#Quaternion%E3%81%AE%E5%8A%A0%E7%AE%97 transform.rotation = targets[0].rotation * targets[1].rotation; で、Quaternionの加算ができると掲載されているのですが、 自分としては、これを 「Quaternion.Euler同士の乗算もrotation同士の乗算も、Quaternion同士の乗算には変わりないので、  結局これも、一軸のみに値があるQuaternion同士の乗算のみに角度の加算となる  (サイトに掲載されている図もX,Yの回転は0でZに値を持っているQuaternion同士を乗算している為)」 と認識しているのですが、いかがでしょうか? それとも、Quaternion.Euler同士の乗算と、rotation同士の乗算は別物なのか、 さらにそれとも、Quaternionの加算とは、結果的にオイラー角の加算とは別物なのか、 ということがわかっていないです。
Bongo

2019/01/06 06:49 編集

おっしゃるように、rotationから得るものもQuaternion.Eulerが作るものもQuaternionですので、テラシュールブログさんの解説はQuaternion.Eulerで作ったクォータニオンでも通用するでしょう。 そして「サイトに掲載されている図もX,Yの回転は0でZに値を持っているQuaternion同士を乗算している為」という考えも妥当だと思います。角度の足し算ができているのは、Z軸周り回転だけに注目しているためでしょうね。 サイトの図ですと、30°の回転と-30°の回転を合成して0°に戻すことができていますが、別の軸が絡んでくるとややこしくなるはずです。 以前、別の方のご質問に「Quaternion.Euler(-10, -10, -10)で各軸-10°回転したあとQuaternion.Euler(10, 10, 10)で各軸10°回転したのにぴったり元の姿勢に戻らない」というものがありました( https://teratail.com/questions/141897 )。これもクォータニオンの合成結果とオイラー角の和が一致しなくなる一例かと思います。 ※念のため補足しますと、テラシュールブログさんの記事では「2つの回転が合成され、等価な1つの回転になる」ということをわかりやすく表現するため「Quaternionの加算」とおっしゃっているのかと思います。 ですが本来は、クォータニオンを加算するというのは、まさにそのままクォータニオンのx、y、z、w成分をそれぞれ足し合わせることを意味するはずです。とはいえ、回転を表現する上でこの本来の加算が出てくる場面はまずないでしょうから、さほどお気になさらずとも問題ないかと思います。 .NET FrameworkのQuaternion( https://docs.microsoft.com/ja-jp/dotnet/api/system.numerics.quaternion?view=netframework-4.7.2 )には加算のためのAddメソッドや+演算子がありますが、UnityのQuaternion( https://docs.unity3d.com/ja/current/ScriptReference/Quaternion.html )にはそういったメソッドや演算子は用意されておりません。おそらく、わざわざ加算機能を用意しなくても大して困らないからじゃないでしょうかね?
退会済みユーザー

退会済みユーザー

2019/01/06 15:06

ご回答ありがとうございます。 今まではテラシュールさんのブログのQuaternionの加算を見て、 別の軸が絡んでも角度の足し算ができるものと勘違いしていたので、理解できてよかったです。 ご提示のリンク拝見しました。逆回転のご説明もとても勉強になりました。 クォータニオンのx、y、z、w成分の内容はとても難しそうですね。 ご教示いただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問