3Dの計算
そもそも行列とはなんぞや、というところから理解できていないので、とりあえず一通り自分で実装してみた。
Direct3Dがやろうとしていることは、だいたいこういうことだろうと思う。
ソースちょっと長いけど。
カメラの設定が面倒だったからビュー変換は手抜き。
require 'dxruby' Window.caption = "TestWindow" Window.width = 320 Window.height = 240 image = Image.new(320,240) class Vector def initialize(vec) @vec = vec end def *(matrix) result = [] for i in 0..3 data = 0 for j in 0..3 data += @vec[j] * matrix[j][i] end result.push(data) end return Vector.new(result) end def [](i) @vec[i] end def []=(i,d) @vec[i]=d end end class Matrix def initialize(arr) @arr = Array.new(4) {|i| Vector.new(arr[i])} end def *(a) result = [] for i in 0..3 result.push(@arr[i] * a) end return Matrix.new(result) end def [](i) @arr[i] end def self.createMatrixRotationZ(angle) cos = Math.cos(Math::PI/180 * angle) sin = Math.sin(Math::PI/180 * angle) return Matrix.new([ [ cos, sin, 0, 0], [-sin, cos, 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]) end def self.createMatrixRotationX(angle) cos = Math.cos(Math::PI/180 * angle) sin = Math.sin(Math::PI/180 * angle) return Matrix.new([ [ 1, 0, 0, 0], [ 0, cos, sin, 0], [ 0,-sin, cos, 0], [ 0, 0, 0, 1] ]) end def self.createMatrixRotationY(angle) cos = Math.cos(Math::PI/180 * angle) sin = Math.sin(Math::PI/180 * angle) return Matrix.new([ [ cos, 0,-sin, 0], [ 0, 1, 0, 0], [ sin, 0, cos, 0], [ 0, 0, 0, 1] ]) end def self.createMatrixMove(x, y, z) return Matrix.new([ [ 1, 0, 0, 0], [ 0, 1, 0, 0], [ 0, 0, 1, 0], [ x, y, z, 1] ]) end end x = 160.0 y = 120.0 z = 100.0 v = [ Vector.new([-25.0, 25.0, 25.0,1.0]), Vector.new([ 25.0, 25.0, 25.0,1.0]), Vector.new([ 25.0, 25.0,-25.0,1.0]), Vector.new([-25.0, 25.0,-25.0,1.0]), Vector.new([-25.0,-25.0, 25.0,1.0]), Vector.new([ 25.0,-25.0, 25.0,1.0]), Vector.new([ 25.0,-25.0,-25.0,1.0]), Vector.new([-25.0,-25.0,-25.0,1.0]), ] anglex = 30 angley = 0 anglez = 0 Window.loop do image.fill([212,208,200]) # world m = Matrix.createMatrixRotationX(anglex) * Matrix.createMatrixRotationY(angley) * Matrix.createMatrixRotationZ(anglez) * Matrix.createMatrixMove(x, y, z) * # local→world Matrix.createMatrixMove(-160, -120, 0) # world→view # projection zn = 100.0 zf = 300.0 sw = 160.0 sh = 120.0 n = Matrix.new([ [2.0 * zn / sw, 0, 0, 0], [ 0, 2.0 * zn / sh, 0, 0], [ 0, 0, zf / (zf - zn), 1], [ 0, 0, -zn * zf / (zf - zn), 0] ]) # viewport vp = Matrix.new([ [ 160, 0, 0, 0], [ 0, 120, 0, 0], [ 0, 0, 1, 0], [ 160, 120, 0, 1] ]) tv = [] v.each do |a| a = a*m*n a[0] = a[0] / a[3] a[1] = a[1] / a[3] a[2] = a[2] / a[3] a[3] = a[3] / a[3] a *= vp tv.push(a) end image.line(tv[0][0], tv[0][1], tv[1][0], tv[1][1], [0,0,0]) image.line(tv[1][0], tv[1][1], tv[2][0], tv[2][1], [0,0,0]) image.line(tv[2][0], tv[2][1], tv[3][0], tv[3][1], [0,0,0]) image.line(tv[3][0], tv[3][1], tv[0][0], tv[0][1], [0,0,0]) image.line(tv[4][0], tv[4][1], tv[5][0], tv[5][1], [0,0,0]) image.line(tv[5][0], tv[5][1], tv[6][0], tv[6][1], [0,0,0]) image.line(tv[6][0], tv[6][1], tv[7][0], tv[7][1], [0,0,0]) image.line(tv[7][0], tv[7][1], tv[4][0], tv[4][1], [0,0,0]) image.line(tv[0][0], tv[0][1], tv[4][0], tv[4][1], [0,0,0]) image.line(tv[1][0], tv[1][1], tv[5][0], tv[5][1], [0,0,0]) image.line(tv[2][0], tv[2][1], tv[6][0], tv[6][1], [0,0,0]) image.line(tv[3][0], tv[3][1], tv[7][0], tv[7][1], [0,0,0]) Window.draw(0,0,image) angley = angley + 1 anglez = anglez + 1 end