20200101

Blenderのpythonでクオータニオン(Quaternion)を勉強した

Blenderのpythonでクオータニオン(Quaternion)を勉強した。しかし、BlenderのQuaternionの機能は一切使わない。Quaternionを使った計算方法、モデルの操作方法が知りたいからだ。

まとめ

先に、まとめを書く。

クォータニオンで

  • オブジェクトの回転ができる。

  • 回転が、任意のベクトルでできる。

注意点

  • アフィンと違い、行列の積ではない。Quaternion同士の積は、独自ルールのかけ算が要る。
  • 原点を中心として回転になるので、原点を通らないベクトルで回転させたいときは、座標系の変換がいる。

やりたい事、やりたくない事

  • やりたくない事
    • Quaternionの数学的な理解。
  • やりたいこと
    • Quaternionの数学的操作。

ググったら、

blenderのpythonを使って、オブジェクトを回転させている記事はあった。)

こことかも結構わかりやすくて面白かった。

しかし、オブジェクトの操作のAPIが、クォータニオンを直接受け取れるから実行できるコマンドの記事だった。クォータニオンを使って、計算をしているわけではなかった。
クォータニオンが、回転のどこら辺に効いているのかを知りたい。

そこで、自分で用意した3×3×3=27個の点の回転をクォータニオンで計算する。

クォータニオンは…

その前に、クォータニオンについて、参考書やネットなどで情報収集した。

  • クォータニオンは複素数の拡張版。実数一つに、虚数三つ、計四つ。これは暗記した知識として持っている。「先頭が角度で、その後にx,y,zに関わる何か?」な状態。何も具体的にイメージできない。
  • プログラムとかだと、独自の型をもったクォータニオン型の変数として表されることが多い。行列みたく、単なる長さ4の配列じゃなめなのか。
  • 回転を表すクォータニオンはサイズが1らしい。だったらサイズ1以外だったらどうなっちゃうのか。

そもそも、回転を表すクォータニオンを準備できたとして、どうやって物を(例えばxyz座標上の点を)回転操作するのか。掛け算?

「どうせアフィンみたく行列の掛け算でできるんだろ?もったいぶるなよ。」とか思ってたが違った。

Blenderのモデル

下記コードでできたモデルを使った。

coord = [[[[i,j,k] for i in range(3)] for j in range(3)] for k in range(3)]

for i in range(3):
for j in range(3):
for k in range(3):
bpy.ops.mesh.primitive_uv_sphere_add(size=1, view_align=False, enter_editmode=False, location=(coord[i][j][k][0]*3, coord[i][j][k][1]*3, coord[i][j][k][2]*3), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
mat = bpy.data.materials.new("Material_%s_%s_%s"%(coord[i][j][k][0]+1, coord[i][j][k][1]+1, coord[i][j][k][2]+1))
bpy.context.object.data.materials.append(mat)
bpy.context.object.active_material_index = 0
bpy.context.object.active_material.diffuse_color = (coord[i][j][k][0]/3.0, coord[i][j][k][1]/3.0, coord[i][j][k][2]/3.0)
bpy.context.object.data.name = "ball_%s_%s_%s"%(coord[i][j][k][0]+1, coord[i][j][k][1]+1, coord[i][j][k][2]+1)
bpy.context.object.name = "ball_%s_%s_%s"%(coord[i][j][k][0]+1, coord[i][j][k][1]+1, coord[i][j][k][2]+1)

拾ってきた式

クォータニオンの操作は、この本を参考にした。。面白かった。

クォータニオン

クォータニオンは、こんな感じで4つ1セット書かれている。

\bold{q}=q_0+q_1i+q_2j+q_3k

座標

位置ベクトルをクォータニオンで表す。

座標が[x,y,z]だとしたら、

クォータニオンは[0,x,y,z]と最初に0をつけるだけ。(本当は0+xi+yj+zkと書くべき?)

回転を表すルール

q^2_0+q^2_1+q^2_2+q^2_3=1

上記が成立するクォータニオンは、回転を表現できる。

回転を表しつつ、上記ルールを守るための、三角関数を使ったクォータニオン

q={\cos(\frac{\theta}{2}),e\sin(\frac{\theta}{2})}

\overline{q}={\cos(\frac{\theta}{2}),-e\sin(\frac{\theta}{2})}

「二つしかないからクォータニオンじゃないじゃん!」→実部と虚部を分けて書いてあるだけ。

  • 右側(虚部)の数値は、虚部で分け合って使う。
  • 分け合い方で、回転軸のベクトルを作る。
  • さらに上記「二乗して合計して1」というルールも守って作らなければならない。
  • \overline{q}は、逆四元数。虚部の正負がひっくり返った四元数。

cosとsinがどんな塩梅で合計1になりそうかは、ちょっと分からん。

q={\cos(\frac{\theta}{2})+\frac{\sqrt{2}}{3}\sin(\frac{\theta}{2})i+\frac{\sqrt{2}}{3}\sin(\frac{\theta}{2})j+\frac{\sqrt{2}}{3}\sin(\frac{\theta}{2})k}

\overline{q}={\cos(\frac{\theta}{2})-\frac{\sqrt{2}}{3}\sin(\frac{\theta}{2})i-\frac{\sqrt{2}}{3}\sin(\frac{\theta}{2})j-\frac{\sqrt{2}}{3}\sin(\frac{\theta}{2})k}

↑こんな感じで配分してみた。

1/3は、三等分だから。

√2は、わからん。

とにかく、√2/3しとくと、「二乗して合計して1」ルールを満たす。

回転操作

クォータニオンのかけ算

回転にはクォータニオンのかけ算を使う。

クォータニオンのかけ算のルールは、以下の式。独自ルール。甘くはなかったか。

\bold{q}=q_0+q_1i+q_2j+q_3k

\bold{r}=r_0+r_1i+r_2j+r_3k

\bold{q}\times \bold{r} = p_0r_0-p_1r_1-p_2r_2-p_3r_3+(p_0r_1+p_1r_0+p_2r_3-p_3r_2)i\\ +(p_0r_2+p_2r_0+p_3r_1-p_1r_3)j+(p_0r_3+p_3r_0+p_1r_2-p_2r_1)k

これは、関数を使って実現しておく。スカラー,i,j,kは別々に計算して返せばいいだろう。自作なので、わざわざクォータニオン型とか作らんで、配列でやっとけばいっか。自分がわかってればいいもんね。

def quakakezan(q=[1,2,3,4],r=[5,6,7,8]):
a=q[0]*r[0]-q[1]*r[1]-q[2]*r[2]-q[3]*r[3]
b=q[0]*r[1]+q[1]*r[0]+q[2]*r[3]-q[3]*r[2]
c=q[0]*r[2]+q[2]*r[0]+q[3]*r[1]-q[1]*r[3]
d=q[0]*r[3]+q[3]*r[0]+q[1]*r[2]-q[2]*r[1]
e =[a,b,c,d]
return e

想定外の変数が入ってきたら、エラーで止まるかも。
最初から値が入っているのは、自分がどんな値を入れたらいいか忘れそうだし、関数のすぐ下にコメントアウトで説明書き入れたって、どうせ読まないから、あえて入れちゃう。

回転操作
  1. 座標の位置クォータニオンを作る。

P=(x,y,z)\\p=0+xi+yj+zk

  1. 前述、回転のためのクォータニオンを作る。
  2. 2.を、前と後ろから、かける

qp\overline{q}

もちろん、四元数のかけ算のルールで計算する。

テクスチャを貼っていないから気づかないだろうけど、ボールは回転していない。同じ向きを画面に向けたまま。
BlenderのQuaternionの機能を使って回転させると、ボールの向きも変わるが、それはしなかった。

コード全体は、YouTubeの説明のところに書いてあるので、ぜひ見てください。

振り返ってみると

テクスチャを貼って、オブジェクトのvertexを回転させるのでもよかった気がする。

が、あえてオブジェクトをすべて動かすことで、何か理解した気になった。

posted by yuchan at 19:00 | Comment(66) | Blender