2D画像に全自動で影つけ

自動で法線マップを生成して、シェーダで影を付ける。
こういうのをやってみたいとずっと思っていたのだが、やってみるとなんだか思うようにいかない。
法線マップが理想どおりに作れるとは思ってはいなくて、綺麗に描画するには後で手で修正できるようなツールが必要なはずだ。
あと、Window.drawExのキーにシェーダも追加できるとよいのだが、これがかなり難しいから、回転とかしつつシェーダ描画ができるような何かを考えなければならない。


一番上のんはるびまサンプルで水視さんに描いてもらった絵から影を取り除いたもの。絵をいじるのは苦手なのでちょっと変になってしまった。
上から二番目はそれを元に生成した簡易法線マップ。
んで、たくさん並んでるのがライト位置を変更しつついくつか描画したもの。
なんとな〜く影がそれっぽい方向についている。
影の閾値も変えれるのでその画像も。
もうちょい品質を上げて、回転描画もできるようになれば、なめらかに回転する2Dの絵に違和感なく影がつくようになるのではないだろうか、というのが俺の考えていたこと。

require 'dxruby'

image = Image.load("image/enemy3.png", 0, 0, 48,48).dup
normalmap = Image.new(48, 48)

def count_w(x, y, image)
  c = 0
  while (image[x, y][0] != 0 and x < image.width )
    x += 1
    c += 1
  end
  return c
end

def count_h(x, y, image)
  c = 0
  while (image[x, y][0] != 0 and y < image.height )
    y += 1
    c += 1
  end
  return c
end

for y in 0..47
  x = 0
  while (x < image.width) do
    if image[x, y][0] != 0
      c = count_w(x, y, image)
      for temp in 0..c
        normalmap[x + temp, y] = [255, 255.0 / (c + 1) * (temp + 1), normalmap[x + temp, y][2], normalmap[x + temp, y][3]]
      end
      x += c
    end
    x += 1
  end
end

for x in 0..47
  y = 0
  while (y < image.height) do
    if image[x, y][0] != 0
      c = count_h(x, y, image)
      for temp in 0..c
        normalmap[x, y + temp] = [255,normalmap[x, y + temp][1],  255.0 / (c + 1) * (temp + 1), normalmap[x, y + temp][3]]
      end
      y += c
    end
    y += 1
  end
end

hlsl = <<EOS
float g_min;
float g_x, g_y;
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;
  float4 normal;
  output.Color = tex2D( Samp0, input.UV );
  normal = tex2D( Samp1, input.UV );
  output.Color *= step(g_min, max(0, 1.0 - abs(g_x - normal.x))*max(0, 1.0 - abs(g_y - normal.y))
                       ) * 0.2 + 0.8;
  return output;
}

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

shader = Shader.new(hlsl, "TShader")
g = 0.25
shader.setValue("g_min", g)

Window.loop do
  g += Input.x * 0.01
  shader.setValue("g_min", g)

  Window.draw(0,0,image)
  Window.draw(0,50, normalmap)
  shader.setValue("g_x", 0.25)
  shader.setValue("g_y", 0.25)
  Window.drawShader(0,100,[image, normalmap], shader)
  shader.setValue("g_x", 0.5)
  shader.setValue("g_y", 0.5)
  Window.drawShader(50,100,[image, normalmap], shader)
  shader.setValue("g_x", 0.75)
  shader.setValue("g_y", 0.75)
  Window.drawShader(100,100,[image, normalmap], shader)
  shader.setValue("g_x", 0.5)
  shader.setValue("g_y", 0.75)
  Window.drawShader(150,100,[image, normalmap], shader)
  shader.setValue("g_x", 0.5)
  shader.setValue("g_y", 0.1)
  Window.drawShader(200,100,[image, normalmap], shader)
end