3D関連理論と2D平面に「立方体」を描く

前の会社はPS4、XBOXなどのコンソールゲームを開発する仕事をやってた。
今の仕事はゲーム開発と関連ありませんが、只今春節休み中、特にやることなくて詰まらない。3D関連の理論を復習してあれそれ実験をやってみる。
目標は2D絵の関数で立体に見える立方体を描く。
簡単に理解できるため、マトリクス演算などわりと理解しにくい知識は使わなくてベクトル演算だけ使う。

まず基礎理論を紹介する。2次元直交座標系は、2次元ベクトルは略。使うのは右でXが正、上でYが正。3次元直交座標系は右手系を使う:X軸の方向は右手の指を沿って、Y軸は指先の方向に示すとき、親指の方向はZ軸にする。

立方体の出番だ。立方体の中心は(0, 0, 1)に置く。辺の長さは2なので、頂点は (1/-1, 1/-1, 2/0) 全部八つだ。


(blenderからスクリーンショットを借りる)

六つの面にも「正面」と「反面」がある。正面から観測して見えるが、反面から見れば見えないはず。例えば上の画像に y=1 の面は見えない。
正面はどこへ向くのは易く判断のため、何かのルールがあれば便利になる。このルールは、「頂点を右回り順番で描く表面は正面」だ。原因は、右手座標系でそうすると頂点を連結するベクトルのクロス積の方向は描く表面の正面方向になる。

だから画像の中の立方体の表面で、頂点の連接順番はこうだ。


頂点は右回りで連接する(見える)表面は三つだ。ほかの表面は左回りで連接する(こっちに向いてない)から見えない。
向こうから観測すれば、頂点の連接順番は逆になる。

次の問題はどうすれば2Dのスクリーンで3Dの立方体を表示できる。
現実の立方体を見える原因は、立方体から人の目まで光線がある。そして目から「スクリーンの中の」立方体まで連接する「光線」は決してスクリーンを通過する。スクリーンはただの平面なので、その「光線」はスクリーンと交わす点で光線の色を描いたら2D平面で立体感ある立方体を表示できる。
ならば、その「交わす点」の計算方法を知ったらスクリーンに表示すべき画像は手に入れる。
三角形相似の性質で交わす点を計算できる。

A点(既知)は目で、D点(既知)はスクリーンを通過して見た点。BC点はスクリーン平面にある。AC⊥BC。
ACの長さも既知なので、|AE|=(ベクトルAD)・(ベクトルAC)÷|AC|。←投影
だから|AB|=|AD|÷|AE|×|AC|。
ベクトルABの長さはした後、(ベクトルAB)=(ベクトルAD)÷|AD|×|AB|。
A点もベクトルABも既知になったらB点の座標も知った。
スクリーン平面で2D座標系を作って、B点の座標はスクリーンの座標系に変換(スクリーンに投影)したら2D平面で表示できるようになった。

ソースコード(結果も見える)
https://github.com/sorayuki/Learn3D (learn3d-1.html)


コードは解説して、
3dutil.jsの中にベクトル演算のクラスがある。
「カメラ」は「目」だ。カメラのデータはカメラの位置、方向、スクリーン(projection planar)への距離を含む。立方体の頂点の座標はスクリーン座標に変換できるメソッドも含む。
スクリーンの座標系は、Z軸はカメラの方向と反対する単位ベクトルk(←カメラ方向は見える表面の方向と反対だから)。そしてZ軸と垂直する任意の単位ベクトルiをX軸方向にして、Y軸の方向はZ軸方向のベクトルとX軸方向のベクトルのクロス積jだ(そうしてXとYのクロス積はZ軸の方向)。上の画像に、ベクトルABをスクリーンに投影するとき、座標は (AB・i,AB・j) になる。
DrawRect関数は立方体の表面を描く。正面はカメラ方向に向く表面(頂点は右回り順番で描く表面)だけ描く。

p.s.
なんだか長い時間で日本語で何も書いてなくて忘れそうになった。ツイッターもほとんどあきらめた。復帰したいけど、タイムラインに入れなかった気が多少とも感じる。

この記事を書くとき、学んでなかった専門用語は多くて難しいと思う。辞書とかネット検索エンジンとかのツールを活用して何とかなる。自分以外の人も理解る可能性があるかもって思って…

とりあえずこれからもよろしくね