mieki256's diary



2017/04/07(金) [n年前の日記]

#1 [dxruby][ruby] ドット絵モドキを自動生成するソレを自分でも考えて試したり

ドット絵というか PixelArt というかソレっぽいものを自動生成するアレコレを眺めているうちに、もしかして乱数でテキトーにドットを打つだけでもそれっぽくなるんじゃないかと安易に思えてきたので一応試してみたり。Ruby + DXRuby で実験。

乱数でテキトーにドットを打つだけの版。 :

まずは本当に、乱数でテキトーにドットを打ってみた。

_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

結果は以下。

take1_ss.png

うむ。完全に甘かった。全く話にならない。ダメだこりゃ。

輪郭を与えてみた。 :

sinカーブを使って、ざっくりとした形っぽいものを与えてみた。かつ、最後にエッジ(境界線)を追加してみた。

_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

結果は以下。

take2_ss.png

むっ。ちょっとはマシになってきた、そんな気がしないでもない。

が、これは 16x16ドットで生成して3倍に拡大表示してるからそれっぽく見えている、てなところもあって。例えば、32x32ドットで生成してみると…。

take2_ss2.png

「なんじゃこりゃああ!(ジーパン刑事風に)」度が増してしまってダメダメ状態に近づいていくなと。

小さく作って拡大するのはどうだろう。 :

世の中には、昔の荒いドット絵を如何にそれらしく綺麗に拡大するか、てなアルゴリズムがいくつか存在していて。

_Scale2x
_Scale2x Algorithm
_HiEnd3D - hq3x (Internet Archive)
_Pixel Scalers
_Pixel art scaling algorithms - Wikipedia

もしかすると、小さいサイズで自動生成しておいて、そこからこれらのアルゴリズムで拡大、みたいなことをすれば多少はそれらしくなる、かもしれないと夢想してみたりもして。となると、まずはこれらのアルゴリズムを試しに書いて実験してみて、かな…。

最初から意味がありそうな断片を配置するのはどうだろう。 :

例えば四角を、それもグラデ塗りした四角を、ランダムにテキトーに場所にいくつか配置していって、ソレを横方向 or 縦方向で反転描画するだけでもそれっぽくならないだろうか。そのためには、四角をグラデ塗りする処理を書いてみて…かな…。明度、いや、輝度がちゃんと変わっていかないと立体感のあるグラデにならないだろうから、色相、彩度、輝度からRGB値を求める処理(HSL → RGB変換)も書いてみないと…。

#2 [anime] 「AKIBA'S TRIP」アニメ版最終回を視聴

面白かった…。カットが変わるとパロディ、またカットが変わると別のパロディ、みたいな。よくまあここまでツッコめるもんだなと…。秋葉原を舞台にした作品らしいというか…。

以上、1 日分です。

過去ログ表示

Prev - 2017/04 - Next
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project