Shaderの修正とサポートクラスについて

さっそくクラス名と構成を変更することにした。ShaderとShaderSetがトップレベルに出るというのが気に入らない。
もともと普通のユーザーががりがりHLSLでコードを書くというのは想定していなくて、DXRubyで苦手なエフェクトを高速に実現できたらいいなー主に画像のフラッシュとトランジションとラスタスクロールあたり、みたいな感じで作っているから、俺がそのへんを作って提供したらそれで済むのだ。基本は。
ところが、昨日の状態だとマニュアルにWindowモジュールやImageクラスと同じような顔をしてShaderとShaderSetが並ぶ。しかもShaderのほうは普通のユーザーが使うことがないと来たもんだ。マニュアル書くほうも困る。更にWindow.draw系メソッドに渡すのはShaderSetだ。何がなんだかよくわからない。

こうする。
トップレベルに定義するのはShaderクラスで、これは今までのShaderSetである。いままでのShaderはCoreクラスという名前でShaderクラスの下に定義する。Shader::Coreという感じ。
Shaderから派生したサポートクラスを作って提供すれば、ユーザーはShader::Coreのことは一切考えなくてよい。Window.draw系に渡すのもShader派生クラスのオブジェクトだ。たぶんこのほうが扱いやすい。
以上を実装して、昨日のスクリプトを修正したものがこの後にだらだら貼り付けたコードだ。
RGSS2のトランジションに近い引数でnewして、startしたらパラメータの初期化、nextで次のフレームの状態になる。あとはWindow.drawShaderに渡して描画すれば、画像はだんだん消えていく。RGSS2は古い画面をフリーズして内部に保存し、新しい画面を描画しながらその手前に古い画像をエフェクトしつつ描画する(と思っている)。その動作をまあ、なんとなく再現できる。
本格的にRGSS2ぽく動かすにはSpriteの実装と自動描画と、ゲームループの分解が必要だ。でもそこまでして真似する必要もなかろう。

#!ruby -Ks
require 'dxruby'

map = [[0, 0, 0, 0, 0, 0, 0, 0, 29, 11, 11, 30, 34, 66, 67, 67],
       [0, 0, 0, 24, 25, 26, 0, 0, 29, 11, 11, 39, 40, 6, 34, 34],
       [0, 0, 24, 17, 31, 35, 0, 0, 12, 20, 11, 11, 11, 39, 40, 40],
       [0, 24, 17, 34, 7, 44, 0, 28, 28, 29, 11, 11, 11, 11, 11, 11],
       [0, 33, 31, 34, 35, 0, 28, 3, 37, 38, 11, 11, 11, 18, 19, 19],
       [0, 42, 43, 43, 44, 28, 3, 38, 11, 11, 11, 18, 19, 13, 28, 28],
       [0, 0, 0, 0, 3, 37, 38, 11, 11, 18, 19, 13, 28, 28, 28, 0],
       [0, 0, 0, 3, 38, 11, 11, 11, 18, 13, 28, 28, 51, 52, 52, 52],
       [0, 0, 3, 38, 11, 11, 18, 19, 13, 51, 52, 52, 86, 58, 61, 76],
       [28, 0, 29, 11, 11, 18, 13, 28, 51, 86, 58, 58, 61, 61, 58, 62],
       [0, 28, 29, 11, 18, 13, 28, 0, 60, 58, 61, 61, 61, 61, 76, 71],
       [0, 28, 29, 11, 27, 28, 28, 51, 86, 61, 61, 58, 76, 70, 71, 0],
       [0, 0, 29, 11, 36, 4, 28, 60, 58, 61, 58, 76, 71, 0, 1, 2],
       [0, 28, 29, 11, 11, 36, 4, 69, 70, 70, 70, 71, 0, 1, 2, 0],
       [0, 0, 12, 20, 11, 11, 27, 0, 1, 0, 1, 1, 1, 2, 2, 0],
       [0, 0, 28, 12, 20, 11, 27, 0, 0, 0, 2, 2, 0, 2, 2, 0],
       [0, 0, 0, 2, 29, 11, 27, 1, 2, 2, 2, 0, 0, 2, 2, 2],
       [0, 0, 0, 2, 29, 11, 27, 1, 0, 1, 1, 2, 2, 0, 0, 2],
       [0, 0, 0, 0, 29, 11, 27, 1, 0, 2, 2, 2, 1, 1, 2, 2],
       [0, 45, 47, 2, 29, 11, 36, 4, 1, 2, 2, 0, 0, 2, 2, 0],
       [45, 82, 56, 0, 29, 11, 11, 36, 4, 1, 2, 2, 2, 2, 0, 0],
       [54, 0, 56, 0, 12, 20, 11, 11, 36, 37, 4, 0, 2, 2, 2, 2],
       [54, 55, 81, 46, 47, 12, 20, 11, 11, 11, 36, 4, 1, 1, 1, 2],
       [54, 55, 0, 0, 56, 0, 12, 19, 20, 11, 11, 36, 37, 4, 1, 1],
       [54, 0, 55, 55, 56, 0, 0, 0, 12, 20, 11, 11, 11, 36, 37, 37],
       [63, 73, 55, 55, 56, 0, 0, 2, 2, 29, 11, 11, 11, 11, 11, 11],
       [0, 54, 0, 55, 81, 47, 0, 2, 3, 38, 11, 11, 11, 11, 11, 11],
       [0, 54, 0, 0, 55, 56, 2, 0, 29, 11, 11, 11, 21, 22, 22, 22],
       [0, 63, 64, 64, 64, 65, 0, 0, 29, 11, 11, 21, 15, 48, 49, 49],
       [0, 0, 0, 0, 0, 0, 0, 0, 29, 11, 11, 30, 34, 57, 34, 34],
      ]


class TransitionShader < Shader
  hlsl = <<EOS
  float g_min;
  float g_max;
  float2 scale;
  texture tex0;
  texture tex1;
  sampler Samp0 = sampler_state
  {
   Texture =<tex0>;
  };
  sampler Samp1 = sampler_state
  {
   Texture =<tex1>;
   AddressU = WRAP;
   AddressV = WRAP;
  };

  struct PixelIn
  {
    float2 UV : TEXCOORD0;
  };
  struct PixelOut
  {
    float4 Color : COLOR0;
  };

  PixelOut PS(PixelIn input)
  {
    PixelOut output;
    output.Color = tex2D( Samp0, input.UV );
    output.Color.a = smoothstep(g_min, g_max, tex2D( Samp1, input.UV * scale ).r );

    return output;
  }

  technique TShader
  {
   pass P0
   {
    PixelShader = compile ps_2_0 PS();
   }
  }
EOS

  @@core = Shader::Core.new(
    hlsl,
    "TShader",
    {
      :g_min => :float,
      :g_max => :float,
      :scale => :float2,
      :tex1 => :texture,
    }
  )

  # durationは遷移にかけるフレーム数、imageはImageオブジェクト(省略でクロスフェード)、vagueは曖昧さ
  def initialize(duration, image=nil, vague=nil)
    super(@@core)
    @duration = duration
    if image
      @image = image
      @vague = vague == nil ? 40 : vague
    else
      @image = Image.new(1, 1, [255,255,255])
      @vague = 256
    end
    @count = 0
    self.g_min  = 0.0
    self.g_max  = 256.0 / @vague
    self.tex1   = @image
    self.scale  = [Window.width.to_f / @image.width, Window.height.to_f / @image.height]
  end

  def start
    @count = 0
    self.g_min  = 0.0
    self.g_max  = 256.0 / @vague
  end

  def next
    @count += 1
    self.g_min = @count.to_f / @duration
    self.g_max = (@count+@vague).to_f / @duration
  end
end

shader = TransitionShader.new(180)#, Image.load("右渦巻き.png"))

rt = RenderTarget.new(640, 480)
image = Image.loadToArray("image/maptile.png", 9, 10)

Window.loop do
  rt.drawTile(150, 16, map, image, 0, 0, 12, 14)
  rt.update

  shader.start if Input.keyPush?(K_Z)
  shader.next

  # drawShaderに渡すのはShaderオブジェクト
  Window.drawShader(0, 0, rt, shader)
end