[Python] Numpy で3D座標変換 その1
World 行列
Scale
Transform
trans = np.mat(
[
[1.0,0.0,0.0,0.0],
[0.0,1.0,0.0,0.0],
[0.0,0.0,1.0,0.0],
[1.0,2.0,3.0,1.0],
]
)
v1 = np.array([-1.0,1.0,-1.0,1.0])
v1 = np.matmul(v1,trans)
v1
Rotation
View 行列
eye = np.array([0.0, 0.0,-5.0])
at = np.array([0.0,0.0,0.0])
up = np.array([0.0,1.0,0.0])
# zaxis
a = at - eye
b = np.linalg.norm(at-eye)
zaxis = a / b
# xaxis
d = np.cross(up, zaxis)
e = np.linalg.norm(d)
xaxis = d / e
# yaxis
yaxis = np.cross(zaxis, xaxis)
view = np.mat(
[
[xaxis[0], yaxis[0], zaxis[0], 0.0],
[xaxis[1], yaxis[1], zaxis[1], 0.0],
[xaxis[2], yaxis[2], zaxis[2], 0.0],
[-np.dot(xaxis, eye), -np.dot(yaxis, eye), -np.dot(zaxis,eye),1.0]
]
)
view
Projection 行列
povy = 0.785398163
yscale = 1.0 / np.tan(povy/2.0)
aspect = 1.333333333
xscale = yscale / aspect
zn = 0.01
zf = 100.0
proj = np.mat(
[
[xscale, 0, 0, 0],
[0, yscale, 0, 0],
[0, 0, zf / (zf-zn), 1 ],
[0, 0, -zn * zf / (zf-zn), 0]
]
)
proj
頂点を計算
座標 (-1.0, 1.0, -1.0) の頂点を View, Proj 変換を行う。
v1 = np.array([-1.0,1.0,-1.0,1.0])
v1 = np.matmul(v1,view)
v1 = np.matmul(v1,proj)
v1 頂点を変換して、頂点シェーダーからの出力として、SV_POSITION に渡される値は下記になる。
[-1.81066017, 2.41421356, 3.99039904, 4. ]
なお、この時点での座標は w が 4.0 の同次座標と呼ばれるものになっている。w 4.0 を使って xyz を割る(正規化する)ことにより、座標は NDC(Normalized Device Coordinate)と呼ばれる空間に移る。
この空間は xy が [-1, 1] で、z が [0, 1] の範囲の座標になる。(OpenGL の場合は z は [-1, 1] まで)w で割った後の座標は下記になる。
[-0.45266504, 0.60355339, 0.99759976]
xy が [-1,1] で、z が [0,1] の範囲に収まっている。この値を、次に説明する Viewport のスケール行列を掛けることにより、スクリーン上の座標へとスケールする。
ラスタライザで Pixel 位置を計算
頂点は v1 / v1[0,3] で、w で割っている。その後に Viewport のスケール行列を掛ける。
w = 640.0/2
h = 480.0/2
viewport = np.mat(
[
[w, 0, 0, 0],
[0, -h, 0, 0],
[0, 0, 1, 0],
[w, h, 0, 1]
]
)
v1 = v1 / v1[0,3]
v1 = np.matmul(v1, viewport)
v1
v1 の結果として、下記になる。
[175.14718614, 95.14718618, 0.99759976, 1. ]
この結果により座標変換を行った頂点は Viewport 640×480 の 175, 96 位置にラスタライザされる。 Depth Buffer に書かれるのは、この時の3つ目の要素 0.9976 あたりがこの時の深度値。