頂点シェーダで画像のX軸回転
昨日のVectorクラスをいじりつつ、昔のコードを引っ張り出してMatrixを部分的に実装。これを使って行列を生成して頂点シェーダに食わせれば、使いやすいかどうかはともかく、とりあえず3D描画が可能なはずだ。
やってみた。
require 'dxruby' class Vector def initialize(*v) @vec = v end def rotate(angle) x = @vec[0] * Math.cos(Math::PI / 180 * angle) - @vec[1] * Math.sin(Math::PI / 180 * angle) y = @vec[0] * Math.sin(Math::PI / 180 * angle) + @vec[1] * Math.cos(Math::PI / 180 * angle) temp = @vec.dup temp[0] = x temp[1] = y Vector.new(*temp) end def +(v) case v when Vector Vector.new(*@vec.map.with_index{|s,i|s+v[i]}) when Array Vector.new(*@vec.map.with_index{|s,i|s+v[i]}) when Numeric Vector.new(*@vec.map{|s|s+v}) else nil end end def *(matrix) result = [] for i in 0..(matrix.size-1) data = 0 for j in 0..(@vec.size-1) data += @vec[j] * matrix[j][i] end result.push(data) end return Vector.new(*result) end def [](i) @vec[i] end def size @vec.size end def to_a @vec end end class Matrix def initialize(*arr) @arr = Array.new(4) {|i| Vector.new(*arr[i])} end def *(a) result = [] for i in 0..(a.size-1) result.push(@arr[i] * a) end return Matrix.new(*result) end def [](i) @arr[i] end def size @arr.size end def self.create_rotation_x(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.create_transration(x, y, z) return Matrix.new( [ 1, 0, 0, 0], [ 0, 1, 0, 0], [ 0, 0, 1, 0], [ x, y, z, 1] ) end def to_a @arr.map {|v|v.to_a}.flatten end end hlsl = <<EOS float4x4 g_world, g_view, g_proj; texture tex0; sampler Samp = sampler_state { Texture =<tex0>; }; struct VS_OUTPUT { float4 pos : POSITION; float2 tex : TEXCOORD0; }; VS_OUTPUT VS(float4 pos: POSITION, float2 tex: TEXCOORD0) { VS_OUTPUT output; output.pos = mul(mul(mul(pos, g_world), g_view), g_proj); output.tex = tex; return output; } float4 PS(float2 input : TEXCOORD0) : COLOR0 { return tex2D( Samp, input ); } technique { pass { VertexShader = compile vs_2_0 VS(); PixelShader = compile ps_2_0 PS(); } } EOS core = Shader::Core.new(hlsl, {:g_world=>:float, :g_view=>:float, :g_proj=>:float}) shader = Shader.new(core) shader.g_view = Matrix.new([1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [-Window.width/2, Window.height/2, 0, 1] ).to_a zn = 150.0 zf = 2000.0 sw = 640.0 sh = 480.0 shader.g_proj = 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] ).to_a image = Image.load("../logo.png") a=0 Window.loop do a += 1 shader.g_world = ( Matrix.create_rotation_x(a) * Matrix.create_transration(300,300,300) ).to_a Window.draw_shader(-image.width/2, -image.height/2, image, shader) break if Input.key_push?(K_ESCAPE) end