mieki256's diary



2018/01/06() [n年前の日記]

#1 [love2d] love2dのShader用GLSLコードを最適化

Raspberry Pi Zero W + love2d で、STGサンプルモドキのBGタイルマップサイズを16x16から32x32に戻して動作確認していたのだけど。

どうやら Shader利用についても結構処理がかかってるようだと分かってきた。Shaderを使うと、フレームレートが数フレーム減ってしまう。

ということは、GLSLで書いてる部分も最適化していかないとダメかな、と。てなわけでそのあたり試行錯誤。

今回 Shaderを使ってやっているのは、特定の1色(今回は赤。R=1.0、G=0.0、B=0.0)を別の色で置き換えるという処理。昔の2Dゲームで言うところのパレット書き換えっぽい見た目にしたいなと。

今までは以下のようなコードだったけど…。
function love.load()
  local shadercode = [[
      extern number factor;
      extern vec3 checkcolor;
      extern vec3 replacecolor;

      vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
        vec4 pixel = Texel(texture, texture_coords);
        if (pixel.r == checkcolor.r && pixel.g == checkcolor.g && pixel.b == checkcolor.b) {
          pixel.r = pixel.r * (1.0 - factor) + replacecolor.r * factor;
          pixel.g = pixel.g * (1.0 - factor) + replacecolor.g * factor;
          pixel.b = pixel.b * (1.0 - factor) + replacecolor.b * factor;
        }
        return pixel * color;
      }
  ]]
  myshader = love.graphics.newShader(shadercode)
  myshader:send("checkcolor", {1.0, 0.0, 0.0})  -- R,G,B
  myshader:send("replacecolor", {0.0, 0.0, 0.0})  -- R,G,B
  angle = 0
end

function love.update(dt)
  angle = (angle + 90 * dt) % 360.0
  local v = 1.0 - math.abs(math.sin(math.rad(angle)))
  myshader:send("factor", v)  -- set 0.0 - 1.0
end

その位置の色(R値、G値、B値)が、置換の対象となる色と同じかどうかを判断する際、if文を使っているわけで。巷の解説記事によると、「GLSLでif文を使うと処理が遅くなるから避けるべし」というのが通説らしいので、なんとかしたいところだなと…。

試しに、以下のように書き換えてみた。
function love.load()
  local shadercode = [[
      extern number factor;
      extern number checkcolor;
      extern vec4 replacecolor;

      vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
        vec4 pixel = Texel(texture, texture_coords);
        float nowcol = (pixel.r * 255.0 * 65536.0) + (pixel.g * 255.0 * 256.0) + (pixel.b * 255.0);
        return mix(pixel, replacecolor, ((checkcolor == nowcol)? factor : 0.0)) * color;
      }
  ]]
  myshader = love.graphics.newShader(shadercode)

  -- checkcolor : (R << 16) + (G << 8) + B
  local chkcol = (1.0 * 255 * 65536) + (0.0 * 255 * 256) + (0.0 * 255)
  myshader:send("checkcolor", chkcol)
  myshader:send("replacecolor", {0.0, 0.0, 0.0, 1.0})  -- R,G,B,A

  angle = 0
end

ただ、気になるところも。if文を三項演算子で置き換えてみても、はたして効果はあるのだろうか…? 分岐が無くなるだけでも改善されるのか、比較が入るだけで遅くなるのか、さて、どっちなんだろう…。

もし、三項演算子があっても遅くなるのであれば、そこも無くしたいところだよなと。なので、以下のようにしてみたり。
function love.load()
  local shadercode = [[
      extern number factor;
      extern number checkcolor;
      extern vec4 replacecolor;

      vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
        vec4 pixel = Texel(texture, texture_coords);
        float nowcol = ((pixel.r * 65536.0) + (pixel.g * 256.0) + pixel.b) * 255.0;
        float fac = (1.0 - sign(abs(nowcol - checkcolor))) * factor;
        return mix(pixel, replacecolor, fac) * color;
      }
  ]]
  myshader = love.graphics.newShader(shadercode)

  -- checkcolor : 0xRRGGBB = (R << 16) + (G << 8) + B
  local checkcolor = ((1.0 * 65536) + (0.0 * 256) + 0.0) * 255.0
  myshader:send("checkcolor", checkcolor)
  myshader:send("replacecolor", {0.0, 0.0, 0.0, 1.0})  -- R,G,B,A

  angle = 0
end
これで、if文も三項演算子も排除できた…。

しかし、Raspberry Pi Zero W 上で動作確認してみたら、やっぱり Shader を使うだけで、5〜7フレームぐらいフレームレートが落ちてしまう。うーん。

コレ以上軽くするのは無理かな…。そもそも、下手すると逆に遅くなってたりしないのかな…。調べる方法はないのかな…。

ちなみに今回、640x480のCanvasに描いた内容を、1920x1080の画面解像度で全画面に拡大描画すると60FPSが出ない ―― 50〜56FPSぐらいになるので悩んでる、という状況で。画面解像度が1280x720なら、ずっと60FPSが出ているのだけど…。

別のやり方。 :

そもそも、BGの特定の1色だけを変えたいだけなら、別の方法もあるなと思いついたので一応メモ。色を変化させたいところだけ透明色にしておいて、Canvas にアレコレ描画する前の塗りつぶしクリア時に、色を変化させつつ塗りつぶししておく、とか。

ただ、その方法では問題もあって。BGのレイヤーが複数枚あるときは、一番奥のレイヤーしかそれっぽく表示できない…はず。手前のレイヤーの特定部分の色を変える、てのは難しい。まあ、一番奥のレイヤーだけ色が変わればそれでいい、という時は使えるかもだけど。

現状でも凄いといえば凄い。 :

考えてみたら、本体価格が約1,300円のPC上で、640x480のゲーム画面をフルHDの1920x1080に拡大描画して50FPS前後出てる、という状況は結構凄いのではないかと思えてきたりもするわけで。

ガラケー向けのゲームアプリを作ってた頃は、本体価格が5万円ぐらいする機器なのに、QVGA相当のゲーム画面を10〜20FPSで描画するのが関の山…。その状況を思い返せば、Raspberry Pi Zero W は意外とスゴイ。ような気もする。

とは言え、これが Raspberry Pi3 なら60FPS出るのだろうし。ソフトウェア的なアレコレをするなら Raspberry Pi3 を使いましょうよ、Raspberry Pi Zero W なんか買ってしまったらダメですよ、てなところもあるのだけど。

以上、1 日分です。

過去ログ表示

Prev - 2018/01 - 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 31

カテゴリで表示

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


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

Powered by hns-2.19.6, HyperNikkiSystem Project