pythonによる3D射影表示

Pythonには、描画環境としてTkinterがあります。
これはあくまで2Dの描画ソフトなので、これを3Dに拡張して見ようと思いました。
使う理論は、高校で習う「ベクトル」です。

Tkinterのcanvas / create_polygonで描画しているだけなので、単色の四角が並ぶだけです。
簡易的に陰線処理くらいはしています。

ソースコードはご自由にお使いください。
ノークレームでお願いします。

Tkinterで表示したルービックキューブ

◆2Dへの射影


空間内の平面は、平面に垂直な法線ベクトルpl[x0,y0,z0]を使って
pl[0]x + pl[1]y + pl2]z + d = 0  …①
と記述できる。
法線ベクトルは、
視点と目的点の差分 [x2-x1 , y2-y1 , z2-z1]
のベクトルとして記述できる。
dは、平面plに含まれる点を一つ規定して①に代入して計算で得る。

三次元世界での(x1,y1,z1),(x3,y3,z3)の二点を通る直線は、媒介変数tを使って
(x,y,z)=(x1,y1,z1) + t((x3-x1),(y3-y1),(z3-z1))と表現する。…②
対象物の点[x3,y3,z3]が平面plに投影されたときの空間座標は、
①と②を連立一次方程式として、tを計算することで得られる。
tがマイナスになったときは、対象物が目の後ろにあることを意味する。

◆三次元平面pl上の座標を二次元平面に変換する。


空間中の平面plを規定する法線ベクトル pl[0],pl[1],pl[2]
平面plに含まれ、水平なベクトルph[0],ph[1],ph[2]
plとphは直角であるので、内積が零。
pl[0]*ph[0] + pl[1]*ph[1] + pl[2]*ph[2] = 0
水平なので、ph[2]=0
pl[0]*ph[0] + pl[1]*ph[1] = 0
pl[0]*ph[0] = -(pl[1]*ph[1])
ph[0] = -(pl[1]*ph[1])/pl[0]

ph[0]/ph[1] = -pl[1]/pl[0]
ということは、
ph[0] = -pl[1]
ph[1] = pl[0]
ph[2] = 0

# 垂直ベクトル
pl[0]*pv[0] + pl[1]*pv[1] + pl[2]*pv[2] = 0
ph[0]*pv[0] + ph[1]*pv[1] + ph[2]*pv[2] = 0

ph[2]=0
を代入。
ph[0]*pv[0] + ph[1]*pv[1] = 0
ph[1]で除する。
(ph[0]/ph[1])*pv[0] + pv[1] = 0

ph[0]/ph[1] = -pl[1]/pl[0]
を代入。
(-pl[1]/pl[0])*pv[0] + pv[1] = 0
pv[1] = (pl[1]/pl[0])*pv[0]

pv[1]/pv[0] = pl[1]/pl[0]
pv[0] = pl[0]
pv[1] = pl[1]

pv[2]を求める。
pl[0]*pv[0] + pl[1]*pv[1] + pl[2]*pv[2] = 0
pl[2]*pv[2] = -(pl[0]*pv[0] + pl[1]*pv[1])
pv[2] = -( pl[0]*pv[0] + pl[1]*pv[1] )/pl[2]
pv[2] = -( pl[0]*pl[0] + pl[1]*pl[1] )/pl[2]

ということは、
pv[0] = pl[0]
pv[1] = pl[1]
pv[2] = -( pl[0]*pl[0] + pl[1]*pl[1] )/pl[2]
この項の分母に視点と目的点のz差分を表すpl[2]があるせいで、真横に視線を向けると破綻します。

得られた点にベクトルphを乗じて合計すれば、水平座標が得られ、
pvを乗じて合計すれば垂直座標が得られる。

◆プログラム構造


p3drect.py
class Room3d

3D表示モジュール

class Cube
キューブ生成モジュールが付属してます。

3drect_example.py
class Room3dの単純な使用例です。
p3drect.pyと同じディレクトリにおいてください。

動画を再生するには、videoタグをサポートしたブラウザが必要です。



黄色いキューブの周りを視点がぐるぐる回ります。
小さいキューブは、スプライト機能を使って座標指定だけで動いてます。
黄色いキューブは動いて無くて固定されてます。
上下カーソルキーで視点の高さが変わります。

3drect_tennis.py
class Room3dの使用例、木偶の坊がテニスだかなんだかわからん球技のラリーをします。
f keyでカメラがボールを追いかけます。
z keyでズームアップ、b keyでズームバックです。