3Dの変換と行列の機能

メモ。
ビュー変換行列と射影変換行列を設定できるようにした。
擬似3Dではクリッピング面をz=0と置くために、すべてのzにznを足していたが、カメラを移動するとその補正は邪魔になるので、Window.view=でnil以外を設定するとWindow.draw系メソッドでの補正をカットするようにした。
とりあえず射影変換行列のデフォルトはこれと同等。

zn = -1000.0
zf = -60000.0
width = Window.width
height = Window.height

Window.project = 
Matrix.new([[ -(2.0 * zn) / width ,                   0,                    0, 0],
            [                   0, -(2.0 * zn) / height,                    0, 0],
            [                   0,                   0,       zf / (zn - zf), -1],
            [                   0,                   0, -zn * zf / (zn - zf),  0]])

ビュー行列はznの補正があるからデフォルトと同等のものをユーザーが設定することはできないが、znをのぞけばこれと同等。

Window.view =
Matrix.new([[1, 0, 0, 0],
            [0, -1, 0, 0],
            [0, 0, 1, 0],
            [-Window.width/2, Window.height/2, 0, 1]])

ビューでyを反転して、射影変換でzを反転し、結果としてx軸中心に180度回した左手座標系。これがDXRuby1.1の擬似3Dのカラクリだ。
ビュー行列を自分で設定するのは難しいから、ワールド座標系の中でカメラの位置と姿勢をデフォルト位置からの差分で表現して、ビュー行列を作ってくれるメソッドMatrix.createViewを作った。
これでカメラの移動がかなりイメージしやすくなるはずだ。自分の位置と向きを突っ込めばそこからの視界になる。D3DXのカメラはよくわからん。
前の3D迷路の最後の部分をそれ用に書き換えてみた。ついでに上下にも動けるようにもしてみた。

x = -6*640 + 320
z = -6*640 + 320

y = 0

angle = -90
Window.loop do
  angle += Input.x
  if Input.y != 0
    x = x + Math.cos((angle-90) * Math::PI / 180) * Input.y * -8
    z = z + Math.sin((angle-90) * Math::PI / 180) * Input.y * -8
  end

  y -= 1 if Input.keyDown?(K_Z)
  y += 1 if Input.keyDown?(K_X)

  camera = Matrix.createRotY( angle ) * Matrix.createTransration( x, y+240, z )
  Window.view = Matrix.createView(camera)

  Window.drawMesh([0,0,0], [90, 0, 0], [1,1,1], m, -10)
end