シェーダで画像のくりぬき

今日のんはDXRuy1.1.12dev対応。
画像をクリヌキするシェーダを書いてみた。

DXRuby1.0の機能でどうにかならんかと思って考えていたのだが、手が思いつかなかった。
背景が黒で、黒く抜くだけなら、黒い絵に透明色の円を描いて重ねればいいが、やりたいことは好きな形に切り取った画像を他の画像の上に重ねる、だから、シェーダを使うしかない。
シェーダが使うテクスチャ座標はすべてのテクスチャに対して同じ値となってしまうので、2つ以上の画像をシェーダに渡すときは同じサイズにする必要がある。
そうでなければシェーダにサイズも渡して計算してもらうか。
まあ、とりあえずできた、ということで。


あ、あと、今回のソースはDXRuby1.1系で対応したアンダースコア式の命名規則に沿ってみた。どっちの方式でも好きなほうで書いてよい。
・・・つもりだったのだが、Inputモジュールを対応し忘れていたようだ。

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],
      ]

hlsl = <<EOS
texture Tex0;
texture Tex1;
sampler Samp0 = sampler_state
{
 Texture =<Tex0>;
};
sampler Samp1 = sampler_state
{
 Texture =<Tex1>;
};

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 = tex2D( Samp1, input.UV ).r;

  return output;
}

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

shader = Shader.new(hlsl, "TShader")
rt = RenderTarget.new(640, 480)
rt2 = RenderTarget.new(640, 480)
image_circle = Image.new(640, 480)
image_map = Image.load_to_array("image/maptile.png", 9, 10)
image_circle.fill([0,0,0])
image_circle.circle_fill(300, 200, 100, [255,255,255])

Window.loop do
  x, y = Input.mousePosX, Input.mousePosY

  Window.draw_tile(150, 16, map, image_map, 0, 0, 12, 14)
  Window.update(rt)

  Window.draw(0,0,rt)
  Window.draw_shader(x-300,y-200,[rt, image_circle], shader)
end

ちなみに原理的には2つ目の画像の赤成分を1つ目の画像のαに設定しているだけだから、形は好きなようにできるし、透明度も自由にできる。
作るのが面倒だったから透明度だけ変更した画像。