#!ruby -Ks # -*- mode: ruby; encoding: sjis -*- # Last updated: <2014/01/13 13:55:40 +0900> require 'dxruby' # Shaderを使ってラスタスクロールさせるクラス # # 宇宙空間っぽいBG画像をラスタースクロールさせる # class StarRasterScrollShader < DXRuby::Shader hlsl = <; // こっちだと画面チラチラ // MinFilter = LINEAR; // MagFilter = LINEAR; // MipFilter = LINEAR; // こっちならチラチラしない MinFilter = POINT; MagFilter = POINT; MipFilter = NONE; AddressU = WRAP; AddressV = WRAP; }; struct PixelIn { float2 UV : TEXCOORD0; }; struct PixelOut { float4 Color : COLOR0; }; PixelOut PS(PixelIn input) { PixelOut output; input.UV.x += ((d + (d * spd * dd[fmod(input.UV.y * size.y, 16)])) / size.x); output.Color = tex2D( Samp0, input.UV ); output.Color.a *= afactor; return output; } technique StarRasterScroll { pass P0 { PixelShader = compile ps_2_0 PS(); } } EOS @@core = DXRuby::Shader::Core.new(hlsl, { :size => :float, :d => :float, :spd => :float, :afactor => :float }) attr_accessor :speed # # 初期化処理 # # @param [float] speed スクロール速度 # @param [float] v ラスター毎の速度変化量。大きくすると速度差が大きくなる # @param [int] width 画面横幅。省略するとウインドウ横幅 # @param [int] height 画面縦幅。省略するとウインドウ縦幅 # def initialize(speed=1, v=0.4, width=nil, height=nil) super(@@core, "StarRasterScroll") w, h = Window.width, Window.height w = width if width h = height if height self.size = [w, h] self.d = 0.0 self.spd = v self.afactor = 1.0 @speed = speed end # # スクロール値更新処理 # def update self.d += @speed end # # スクロール値初期化 # def reset_scroll self.d = 0 end # # スクロール速度変更 # # @param [float] speed スクロール速度 # @param [float] v ラスター毎の速度変化量。大きくすると速度差が大きくなる # def set_speed(speed=1, v=0.4) @speed = speed self.spd = v end # # アルファ値変更 # # @param [float] v アルファ値、0.0で透明、1.0で不透明 # def set_alpha(v) self.afactor = v end # # 星が描かれた背景用画像を作成して返す # # @param [int] width 画像横幅(省略時はWindow横幅) # @param [int] height 画像縦幅(省略時はWindow縦幅) # @param [int] num 散布するドット数 # @param [bool] len_sync trueなら線の長さをラスタースクロール速度に合わせる # デフォルトはfalse # # @return [Image] 作成した画像(Image) # def get_star_image(width=nil, height=nil, num=0x3fff, len_sync=false) width = Window.width unless width height = Window.height unless height img = Image.new(width, height, [0, 0, 0, 0]) srand(0) # 速度テーブル(16ラスター分) spdtbl = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] # 速度と対応した線の長さ wtbl = [5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 12, 24, 36, 480] # ドットの色データ(3色に決め打ち) colordata = [[0, 47, 191], [69, 119, 209], [180, 191, 227]] # 画像に対してランダムに線を引く img.height.times do |y| # next if y % 16 != 0 and y % 16 != 1 ymod = y % spdtbl.length if len_sync # ラスター毎のスクロール速度と対応した線の長さにする場合 w = wtbl[spdtbl[ymod]] else # ラスター毎のスクロール速度と対応してない線の長さにする場合 w = wtbl[ymod] end b = ((spdtbl[ymod] + 1) / spdtbl.length.to_f) * 0.5 + 0.5 if w == wtbl[-1] w = rand(w) + wtbl[0] b *= (rand(70) + 31) / 100.0 end l = ((spdtbl.length - ymod) * 0.75).to_i l = 1 if l == 0 l.times do |i| x = rand(img.width - w) if w <= 8 wt = [[x, w], [x + 1, w - 2], [x + 2, w - 4]] else wt = [[x, w], [x + w * 0.08, w * 0.84], [x + w * 0.16, w * 0.68]] end colordata.each_with_index do |d, i| lx, lw = wt[i] img.line(lx, y, lx + lw, y, [d[0] * b, d[1] * b, d[2] *b]) break if i== 1 and wt[i][0] == wt[i+1][0] end end end # 画像に対してランダムに点を打つ num.times do |i| c = colordata[rand(3)] b = rand(50) / 100.0 + 0.5 img[rand(img.width), rand(img.height)] = [c[0] * b, c[1] * b, c[2] * b] end return img end # # 動作確認用。垂直な線を引いた画像を作成して返す # # @param [int] width 画像横幅(省略時はWindow横幅) # @param [int] height 画像縦幅(省略時はWindow縦幅) # # @return [Image] 作成した画像(Image) # def get_vertical_line_image(width=nil, height=nil) width = Window.width unless width height = Window.height unless height img = Image.new(width, height, [0, 0, 0, 0]) img.line(0, 0, 0, img.height, C_WHITE) return img end end if __FILE__ == $0 # 動作テスト。使い方 spd = 1 # スクロール速度 spd_sub = 0.5 # ラスター毎のスクロール速度差 # Window.resize(1280, 720) # Window.fps = 10 # "Window.scale = 2.0 # スクロール速度を渡して生成 shader = StarRasterScrollShader.new(spd, spd_sub, Window.width, Window.height) # 星画像を生成・取得(引数無しならWindowのサイズで作る) img_nosync = shader.get_star_image(Window.width, Window.height, 0x3fff, false) img_sync = shader.get_star_image(Window.width, Window.height, 0x3fff, true) # 動作テスト用。垂直線画像を生成・取得 img_vline = shader.get_vertical_line_image(Window.width, Window.height) alpha_chg = false framecnt = 0 # メインループ Window.loop do break if Input.keyPush?(K_ESCAPE) # スクロール値更新 shader.update # 描画 if Input.keyDown?(K_X) # Xキーで垂直線を表示 Window.draw_shader(0, 0, img_vline, shader) elsif Input.keyDown?(K_Z) # Zキーでスクロール速度と対応した星画像を表示 Window.draw_shader(0, 0, img_sync, shader) else Window.draw_shader(0, 0, img_nosync, shader) end # ---------------------------------------- # 以下は動作テスト用 # # Fキー : フルスクリーン表示 # Cキー : スクロール値リセット # Vキー : 押してる間はスクロール速度を変更 # Aキー : アルファ値の変化を切替 # Window.windowed = !Window.windowed? if Input.keyPush?(K_F) shader.reset_scroll if Input.keyPush?(K_C) if Input.keyDown?(K_V) shader.set_speed(0.5, 0.05) else shader.set_speed(spd, spd_sub) end alpha_chg = !alpha_chg if Input.keyPush?(K_A) if alpha_chg v = Math.sin(framecnt * Math::PI / 180.0).abs shader.set_alpha(v) else shader.set_alpha(1.0) end framecnt += 1 end end