WGGの活動log

都内でゲーム開発だったりVRだったりをしてるかもしれないエンジニアです. WGGは「ワグ」と読みます

Blender でジオメトリノードを使って四次元超立方体を作る

こんなものを作ってみました

Wikipedia正八胞体 とか調べると出てくるやつです

正八胞体 - Wikipedia

https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Tesseract.gif/120px-Tesseract.gif

作り方

流れはこんな感じです

  • 4次元立体の表示の1つになる3次元の立体を作成する
  • 作成した立体から4次元の座標構造を取得する
  • 4次元空間内での座標変換 (回転など) を行う-①
  • 変換した4次元座標を元に3次元空間上にオブジェクトを変形する
  • 4次元空間内での座標変換 (回転など) を行う-②

1. 4次元立体の表示の1つになる3次元の立体を作成する

立方体を2つ作ります 中心は同じ座標で,大きさは内側の立方体が一辺2, 外側の立方体が一辺4 にしました

次に,片側の立方体から隣り合う頂点を2つ選び,それと同じ位置にもう一方の立方体の2頂点, 合計4つの頂点を選択して面を貼ります

1面だけ貼ったのが以下です

これを, 全ての頂点の組み合わせに対して行います (12面あるはずです)

これで出来上がった立体は以下の情報を持っています

  • 16の頂点 (8頂点の立方体2つなので)
  • 24の面 (6面の立方体2つ + 追加した12面)
  • 32の辺 (12辺の立方体2つ + 2つの立方体の対応する頂点間を結ぶ辺8つ)

この情報を変形して,4次元空間での操作を行います

2. 作成した立体から4次元の座標構造を取得する

立方体の8つの頂点は以下のように表せます

(-1, -1, -1) (-1, -1, 1) (-1, 1, -1) (-1, 1, 1)
(1, -1, -1) (1, -1, 1) (1, 1, -1) (1, 1, 1)

本当は座標は 0 / 1 でも良いですが, 後の計算に楽なので -1 / 1 にしました

今回 1. で作成した形状の頂点は以下のようになります

(-1, -1, -1) (-1, -1, 1) (-1, 1, -1) (-1, 1, 1)
(1, -1, -1) (1, -1, 1) (1, 1, -1) (1, 1, 1)
(-2, -2, -2) (-2, -2, 2) (-2, 2, -2) (-2, 2, 2)
(2, -2, -2) (2, -2, 2) (2, 2, -2) (2, 2, 2)

各辺の長さが2倍の立方体を作っているので,3軸の符号が同じで各要素が2倍になった頂点の組み合わせができます

そして,今回作るべき正八胞体の座標を考えてみます

(-1, -1, -1, -1) (-1, -1, -1, 1) (-1, -1, 1, -1) (-1, -1, 1, 1)
(-1, 1, -1, -1) (-1, 1, -1, 1) (-1, 1, 1, -1) (-1, 1, 1, 1)
(1, -1, -1, -1) (1, -1, -1, 1) (1, -1, 1, -1) (1, -1, 1, 1)
(1, 1, -1, -1) (1, 1, -1, 1) (1, 1, 1, -1) (1, 1, 1, 1)

4次元なので軸が1つ増えて,同様に±の組み合わせが増えています

4軸のうち,3軸は先はど作成した形状の頂点をそのまま使用して, 残り1軸は 作成した形状の頂点が 「内側か, 外側か」という情報を4軸目に割り当てていきます

つまり, (-1, -1, -1) は4次元上で (-1, -1, -1, -1) に, (-2, -2, -2) は4次元上で (1, -1, -1, -1) といった形に各頂点を変換する仕組みを作ります

変換後の座標を格納する領域を作る

ここからはジオメトリノードを使って実際に作っていきます

名前付き属性格納 (英名: Store Named Attribute) を使って,4つ属性を付与します

形式は整数・ポイントで, 名称はそれぞれ W/X/Y/Z としました

位置XYZ分離 で3軸それぞれを取得して, それぞれの符号を見て -1 or 1 を割り当てます

更に,3軸のいずれかが 「1 より大きいか」 を見ることで,内側か外側かの判別をして W軸に割り当てます

(↓画像内の NodeGroup004 は, 「乗算:2倍→減算: -1」をすることで, (0 or 1) → (-1 or 1) に変換する処理です)

これで, 正八胞体の4次元空間上の座標を定義することができました

3. 4次元空間内での座標変換を行う-①

ここからは, 先までに取得した4次元座標の回転を行います

その前に,変換後の4次元座標を格納する領域を作ります

X/Y/Z/W と同様に, 名前付き属性格納 を用いて変換後の座標を表す領域を作ります

形式はFloat・ポイントで, 名称はそれぞれ PX/PY/PZ/PW としました

4次元空間上の回転について軽く説明します

3次元空間で特定の軸に沿って回転させる場合, X軸・Y軸・Z軸回りの回転で合計3通りありますが, 4次元空間では合計6通りになります

○軸回りと言うよりも,「○○平面で回転」と言ったほうがわかりやすいかもしれません

XY平面/XZ平面/XW平面/YZ平面/YW平面/ZW平面 の6通りです

(↓の記事とかが参考になると思います)

やっと理解できる!3次元・4次元・N次元物体の描画まとめ - Qiita

そして今回の4次元立体の描画では, XYZの3次元はそのまま3次元空間に割り当てますが, W次元を利用して4次元っぽさのあるアニメーションにするため,この軸に変化がかかる回転が重要になります

そのため「XW平面」「YW平面」「ZW平面」の3つの回転を作ります (残りの3つで回転しても,ただの3次元上の回転に見えるだけなので)

XW 平面での回転

↑に貼った記事を見るとわかるのですが,基本的に次元が1つ増えただけで3次元の回転行列とほぼ変わらないので, Blender上の ベクトル回転を利用することができます

4次元のうちX座標, W座標の2つを, 入力値 (X, Y) としたベクトルを作り, それをZ軸回りに回転させます

名前付き属性 (英名: Named Attirbute) を用いて格納された各座標を取得して, XYZ合成 でベクトル生成 → ベクトル回転 でZ軸回りに回転させ → XYZ分離 で再び各座標情報に戻します

この出力結果の X, Y がそれぞれ入力値である X, W に対応するので, PX PW に入れます

PY, PZ には一旦それぞれ Y Z をそのまま入れておきます

↓の画像では, 45℃回転させた場合に値が√2になっていて,回転できているのがわかります

4. 変換した4次元座標を元に3次元空間上にオブジェクトを変形する

さて, XW平面の回転ができたため次のYW平面, ZW平面とやっても良いのですが,早く動くものを見てみたいので一旦この回転を3次元空間の立体に反映してみます

4次元座標を3次元に変換するには, 4次元の各要素を3次元に いい感じに 割り振りします

ここはやり方によって見栄えが変わってくるところなので,色々試行錯誤してみると楽しいと思います

今回は,そこそこ一般的(多分?)な以下の式を用いて3次元への投影を行ってみます

X: PX * (PW / 2 + 1.5)
Y: PY * (PW / 2 + 1.5)
Z: PZ * (PW / 2 + 1.5)

W軸の値によって中心から離れるような広がり方をします

( 位置設定を2つ使っていますが, 1つ目の位置設定にスケール-1 のベクトルを渡しているのは各座標を一度原点に戻すためです, これをすることで, 2つ目の位置設定に渡した値そのものの座標に移動することができるようになります)

5. 4次元空間内での座標変換 (回転など) を行う-②

最後に, 3. で作った回転を利用して,複数の回転軸を作っていきます

まずは, 3. で作った回転の仕組みをグループ化して, 2軸と角度を入力できるように構造化します

このグループを3つ繋げ, W軸は次のグループの入力値へと伝搬します

回転行列は複数組み合わせることができるから,1つ目の回転行列の出力を2つ目の回転行列の入力にすることができます

完成したものがこちらです

ついでに初めのTwitter動画に使ってたようなマテリアルも使用しています, 見た目が好きなので

終わりに

こんな感じで,4次元正八胞体を Blender で作る方法を纏めてみました.

回転の仕方と3次元への投影式によってはすごく面白い動きをしたりするので楽しいです

記事内で一通り作り方については解説していますが,もし Blender ファイルが欲しいなどありましたら twitter かコメントにて連絡してくださると配布できる形で用意すると思います