2017/04/07(金) [n年前の日記]
#1 [dxruby][ruby] ドット絵モドキを自動生成するソレを自分でも考えて試したり
ドット絵というか PixelArt というかソレっぽいものを自動生成するアレコレを眺めているうちに、もしかして乱数でテキトーにドットを打つだけでもそれっぽくなるんじゃないかと安易に思えてきたので一応試してみたり。Ruby + DXRuby で実験。
◎ 乱数でテキトーにドットを打つだけの版。 :
まずは本当に、乱数でテキトーにドットを打ってみた。
_tinypixelartgen_take1.rb
結果は以下。
うむ。完全に甘かった。全く話にならない。ダメだこりゃ。
_tinypixelartgen_take1.rb
# ドット絵自動生成のテスト # # License : CC0 / Public Domain class TinyPixelArtGen attr_accessor :pixels def initialize(w, h, mirror_x: true, mirror_y: false, seed: 0, color: 8) srand(seed) @color = color @w, @h = w, h @mirror_x, @mirror_y = mirror_x, mirror_y @canvas = Array.new(@h).map { Array.new(@w, 0) } @pixels = Array.new(@h).map { Array.new(@w).map { Array.new(4, 0) } } ww, hh = (@w / 2.0).round, (@h / 2.0).round wwf, hhf = @w % 2, @h % 2 # generate dot pattern @h.times do |y| @w.times do |x| @canvas[y][x] = rand(@color) end end # generate palette @pal = Array.new(@color) @pal.size.times do |i| if i == 0 r, g, b, a = 0, 0, 0, 0 elsif i == 1 r, g, b, a = 0, 0, 0, 255 elsif i == @pal.size - 1 r, g, b, a = 255, 255, 255, 255 else r, g, b = rand(256), rand(256), rand(256) a = 255 end @pal[i] = [r, g, b, a] end # make pixel data @h.times do |y| @w.times do |x| sx, sy = x, y sx = (x >= ww and @mirror_x)? (ww - 1 - wwf - (x - ww)) : x sy = (y >= hh and @mirror_y)? (hh - 1 - hhf - (y - hh)) : y r, g, b, a = @pal[@canvas[sy][sx]] @pixels[y][x] = [r, g, b, a] end end end end if $0 == __FILE__ # ---------------------------------------- require 'dxruby' def conv_pixels_to_image(pixels) w, h = pixels[0].size, pixels.size img = Image.new(w, h, [0, 0, 0, 0]) h.times do |y| w.times do |x| r, g, b, a = pixels[y][x] img[x, y] = [a, r, g, b] end end return img end def generate_images(n, seed) sz = 16 imgs = [] n.times do |i| pat = TinyPixelArtGen.new( sz, sz, mirror_x: (rand > 0.5)? true : false, mirror_y: (rand > 0.5)? true : false, seed: seed + i, color: 8 ) imgs.push(conv_pixels_to_image(pat.pixels)) end return imgs end Window.resize(640, 480) Window.bgcolor = [32, 128, 196] Window.minFilter = TEXF_POINT Window.magFilter = TEXF_POINT cnt = 96 seed = 0 imgs = generate_images(cnt, seed) seed += cnt Window.loop do break if Input.keyPush?(K_ESCAPE) if Input.keyPush?(K_SPACE) imgs = generate_images(cnt, seed) seed += cnt end scale = 3 border = 4 x, y = 0, 0 imgs.each do |img| Window.drawScale(x, y, img, scale, scale, 0, 0) x += img.width * scale + border if (x + img.width * scale) >= Window.width x = 0 y += img.height * scale + border end end end end
結果は以下。
うむ。完全に甘かった。全く話にならない。ダメだこりゃ。
◎ 輪郭を与えてみた。 :
sinカーブを使って、ざっくりとした形っぽいものを与えてみた。かつ、最後にエッジ(境界線)を追加してみた。
_tinypixelartgen_take2.rb
結果は以下。
むっ。ちょっとはマシになってきた、そんな気がしないでもない。
が、これは 16x16ドットで生成して3倍に拡大表示してるからそれっぽく見えている、てなところもあって。例えば、32x32ドットで生成してみると…。
「なんじゃこりゃああ!(ジーパン刑事風に)」度が増してしまってダメダメ状態に近づいていくなと。
_tinypixelartgen_take2.rb
# ドット絵自動生成のテスト # sinカーブで型を与えて生成、かつ、境界線を付加してみる # # License : CC0 / Public Domain class TinyPixelArtGen attr_accessor :pixels def initialize(w, h, mirror_x: true, mirror_y: false, seed: 0, color: 8) srand(seed) @color = color @w, @h = w, h @mirror_x, @mirror_y = mirror_x, mirror_y @canvas = Array.new(@h).map { Array.new(@w, 0) } @pixels = Array.new(@h).map { Array.new(@w).map { Array.new(4, 0) } } ww, hh = (@w / 2.0).round, (@h / 2.0).round wwf, hhf = @w % 2, @h % 2 # generate dot pattern ang = rand(360) ang_d = rand(270.0 / @h) + (45.0 / @h) (1..(@h-2)).each do |y| rad = (ang + (ang_d * y)) * Math::PI / 180.0 rw = (((w * 4 / 10.0 - 1) * Math.sin(rad)).to_i + (w * 5 / 10.0)) * 2 rx = (w - rw) / 2 @w.times do |x| xx = rand(rw) + rx @canvas[y][xx] = rand(@color) end end # generate edge @h.times do |y| @w.times do |x| next if @canvas[y][x] <= 1 @canvas[y - 1][x] = 1 if (y - 1 >= 0 and @canvas[y - 1][x] == 0) @canvas[y + 1][x] = 1 if (y + 1 < @h and @canvas[y + 1][x] == 0) @canvas[y][x - 1] = 1 if (x - 1 >= 0 and @canvas[y][x - 1] == 0) @canvas[y][x + 1] = 1 if (x + 1 < @w and @canvas[y][x + 1] == 0) end end # generate palette @pal = Array.new(@color) rr, gg, bb = rand(128), rand(128), rand(128) @pal.size.times do |i| if i == 0 r, g, b, a = 0, 0, 0, 0 elsif i == 1 r, g, b, a = 0, 0, 0, 255 elsif i == (@pal.size - 1) c = 255 - rand(48) r, g, b, a = c, c, c, 255 else r = rr + rand(128) g = gg + rand(128) b = bb + rand(128) a = 255 end @pal[i] = [r, g, b, a] end # make pixel data @h.times do |y| @w.times do |x| sx, sy = x, y sx = (x >= ww and @mirror_x)? (ww - 1 - wwf - (x - ww)) : x sy = (y >= hh and @mirror_y)? (hh - 1 - hhf - (y - hh)) : y r, g, b, a = @pal[@canvas[sy][sx]] @pixels[y][x] = [r, g, b, a] end end end end if $0 == __FILE__ # ---------------------------------------- require 'dxruby' def conv_pixels_to_image(pixels) w, h = pixels[0].size, pixels.size img = Image.new(w, h, [0, 0, 0, 0]) h.times do |y| w.times do |x| r, g, b, a = pixels[y][x] img[x, y] = [a, r, g, b] end end return img end def generate_images(n, seed) sz = 16 imgs = [] n.times do |i| pat = TinyPixelArtGen.new( sz, sz, mirror_x: (rand > 0.5)? true : false, mirror_y: (rand > 0.5)? true : false, seed: seed + i, color: 8 ) imgs.push(conv_pixels_to_image(pat.pixels)) end return imgs end Window.resize(640, 480) Window.bgcolor = [32, 128, 196] Window.minFilter = TEXF_POINT Window.magFilter = TEXF_POINT cnt = 96 seed = 0 imgs = generate_images(cnt, seed) seed += cnt Window.loop do break if Input.keyPush?(K_ESCAPE) if Input.keyPush?(K_SPACE) imgs = generate_images(cnt, seed) seed += cnt end scale = 3 x, y = 0, 0 imgs.each do |img| Window.drawScale(x, y, img, scale, scale, 0, 0) x += img.width * scale + 4 if (x + img.width * scale) >= Window.width x = 0 y += img.height * scale + 4 end end end end
結果は以下。
むっ。ちょっとはマシになってきた、そんな気がしないでもない。
が、これは 16x16ドットで生成して3倍に拡大表示してるからそれっぽく見えている、てなところもあって。例えば、32x32ドットで生成してみると…。
「なんじゃこりゃああ!(ジーパン刑事風に)」度が増してしまってダメダメ状態に近づいていくなと。
◎ 小さく作って拡大するのはどうだろう。 :
世の中には、昔の荒いドット絵を如何にそれらしく綺麗に拡大するか、てなアルゴリズムがいくつか存在していて。
_Scale2x
_Scale2x Algorithm
_HiEnd3D - hq3x (Internet Archive)
_Pixel Scalers
_Pixel art scaling algorithms - Wikipedia
もしかすると、小さいサイズで自動生成しておいて、そこからこれらのアルゴリズムで拡大、みたいなことをすれば多少はそれらしくなる、かもしれないと夢想してみたりもして。となると、まずはこれらのアルゴリズムを試しに書いて実験してみて、かな…。
_Scale2x
_Scale2x Algorithm
_HiEnd3D - hq3x (Internet Archive)
_Pixel Scalers
_Pixel art scaling algorithms - Wikipedia
もしかすると、小さいサイズで自動生成しておいて、そこからこれらのアルゴリズムで拡大、みたいなことをすれば多少はそれらしくなる、かもしれないと夢想してみたりもして。となると、まずはこれらのアルゴリズムを試しに書いて実験してみて、かな…。
◎ 最初から意味がありそうな断片を配置するのはどうだろう。 :
例えば四角を、それもグラデ塗りした四角を、ランダムにテキトーに場所にいくつか配置していって、ソレを横方向 or 縦方向で反転描画するだけでもそれっぽくならないだろうか。そのためには、四角をグラデ塗りする処理を書いてみて…かな…。明度、いや、輝度がちゃんと変わっていかないと立体感のあるグラデにならないだろうから、色相、彩度、輝度からRGB値を求める処理(HSL → RGB変換)も書いてみないと…。
[ ツッコむ ]
以上です。