なおしのこれまで、これから

学んだこと・感じたこと・やりたいこと

3D数学の復習と実践(ロドリゲスの回転公式)

始めに

過去記事

shitakami.hatenablog.com

shitakami.hatenablog.com


Quaternionの解説をしようと思いましたが、今回はロドリゲスの回転公式を扱っていこうと思います。

この内容と同時にQuaternionをやっていこうと当初は予定していましたが、四元数の解説が予想より長くなりそうなので2回に分けてQuaternionは次回に回します。



ロドリゲスの回転公式

ロドリゲスの回転公式とは「ベクトル空間において、与えられた回転軸に対して回転を行うための効率的なアルゴリズムを指す。」というものです。(wikipediaより

図で解説すると次のようになります。 (頑張って書いたけど汚い. . . .)

f:id:vxd-naoshi-19961205-maro:20191203180705p:plain

あるベクトル  \vec{v} を単位ベクトル  \vec{n} を軸に  \theta だけ回転して  \vec{v'}を得ます。

ベクトル  \vec{v}  \vec{n} の垂直成分と平行成分の和に書き換えることが出来ます。


\vec{v} = \vec{v_{\perp}} + \vec{v_{\parallel}}


次に、この2つのベクトルを書き換えていきます。平行成分のベクトルは  \vec{v}  \vec{n} 内積を使って求められます。


\vec{v_{\parallel}} = (\vec{v} \cdot \vec{n}) \vec{n}


垂直成分についても書き換えていきます。 先ほど求めた平行成分のベクトルを利用します。


\vec{v_{\perp}} = \vec{v} - \vec{v_{\parallel}}  \\\
 = \vec{v} - (\vec{v} \cdot \vec{n}) \vec{n}


次に  \vec{v} の平行成分と垂直成分に垂直なベクトル  \vec{\omega} を求めます。  \vec{\omega}  \vec{n}  \vec{v} の垂直成分の外積を使用することで求められます。


\vec{\omega} = \vec{n} \times \vec{v_{\perp}}  \\\
 = \vec{n} \times (\vec{v} - \vec{v_{\parallel}}) \\\
= \vec{n} \times \vec{v} - \vec{n} \times \vec{v_{\parallel}} \\\
= \vec{n} \times \vec{v} - 0 \\\
= \vec{n} \times \vec{v}


では  \vec{\omega} \vec{v} の垂直成分を利用して  \vec{v'} の垂直成分を求めます。これは2次元の回転と同じ計算になります。


\vec{v'_{\perp}} = \cos \theta \vec{v_{\perp}} + \sin \theta \vec{\omega} \\\
= \cos \theta ( \vec{v} - (\vec{v} \cdot \vec{n}) \vec{n} ) + \sin \theta (\vec{n} \times \vec{v}) \\\


最後に回転して得られるベクトル  \vec{v'} は次のようになります。


\vec{v'} = \vec{v_{\parallel}} + \vec{v'_{\perp}} \\\
= (\vec{v} \cdot \vec{n}) \vec{n} + \cos \theta ( \vec{v} - (\vec{v} \cdot \vec{n}) \vec{n} ) + \sin \theta (\vec{n} \times \vec{v})

変換行列にする

 \vec{v}  \vec{n} を次のようにする。



\vec{v} = 
\begin{pmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{pmatrix}
\\\
\vec{n} = 
\begin{pmatrix}
n_{x} & n_{y} & n_{z}
\end{pmatrix}


では、ここから表現行列を求めます。先程求めた回転公式を少し整理します。


\vec{v'} = \vec{v_{\parallel}} + \vec{v'_{\perp}} \\\
= (\vec{v} \cdot \vec{n}) \vec{n} + \cos \theta ( \vec{v} - (\vec{v} \cdot \vec{n}) \vec{n} ) + \sin \theta (\vec{n} \times \vec{v})
\\\
= \cos \theta \vec{v} + (1 - \cos \theta)(\vec{v} \cdot \vec{n})\vec{n} + \sin \theta (\vec{n} \times \vec{r})


次にこれらの記事を参考に変換行列を求めます。(本の内容よりもこちらの方が分かり易いと感じました)

ロドリゲスのの回転公式の表現行列

ベクトル射影の表現行列

ベクトル積の表現行列


求められた変換行列は次のようになります。

f:id:vxd-naoshi-19961205-maro:20191204050956p:plain



シェーダーで実装

この変換行列をシェーダーで実装してみます。

         float3 rotation(float3 vertex, float3 Axis, float theta) {

                float t = radians(theta);

                float cosValue = cos(t);
                float sinValue = sin(t);
                float a = 1 - cosValue;

                float3 axis = normalize(Axis);

                float3x3 mat = float3x3(
                    float3(axis.x * axis.x * a + cosValue,
                        axis.x * axis.y * a - axis.z * sinValue,
                        axis.x * axis.z * a + axis.y * sinValue),
                    float3(axis.x * axis.y * a + axis.z * sinValue,
                        axis.y * axis.y * a + cosValue,
                        axis.y * axis.z * a - axis.x * sinValue),
                    float3(axis.x * axis.z * a - axis.y * sinValue,
                        axis.y * axis.z * a + axis.x * sinValue,
                        axis.z * axis.z * a + cosValue));

                return mul(vertex, mat);


            }

            v2f vert (appdata v)
            {
                v2f o;

                v.vertex.xyz = rotation(v.vertex.xyz, _Axis, _Theta);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }


結果が次のgif動画になります。

f:id:vxd-naoshi-19961205-maro:20191204040836g:plain



過去記事と同様に、座標変換やスケーリングと組み合わせることも可能です。

            v2f vert (appdata v)
            {
                v2f o;

                float4x4 scale = float4x4(
                    float4(_ScaleX, 0, 0, 0),
                    float4(0, _ScaleY, 0, 0),
                    float4(0, 0, _ScaleZ, 0),
                    float4(0, 0, 0, 1));

                v.vertex = mul(scale, v.vertex);

                v.vertex.xyz = rotation(v.vertex.xyz, _Axis, _Theta);

                v.vertex.x += _PosX;
                v.vertex.y += _PosY;
                v.vertex.z += _PosZ;

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }



最後に

ロドリゲスの回転公式についてまとめました。個人的な感想になりますが、「ロドリゲスの回転公式」って滅茶苦茶かっこいい名前ですよね。

次回はQuaternion(四元数)についてまとめていく予定です。


追記

最後にQuaternionをまとめました。

shitakami.hatenablog.com