1.12 に入る破壊的変更: サブ画像の描画

Hajime Hoshi
2020-06-28

この記事は 2020 年 6 月現在の情報であり、予定を変更する可能性もあります。

要約

Ebiten は 1.12 (現在の master ブランチ) から破壊的を導入する予定です。 Ebiten は、画像の描画の際、描画元領域のチェックをしないようになり、そのためいくつかのケースでは予期しないピクセルが描画されることがあります。この描画結果の違いは、以下の条件のすべてが満たされた場合に発生しえます:

例えば、テクスチャアトラス上のタイルを拡大縮小や回転をさせて描画する場合、 1.11 とは違った結果になりえます。描画結果が隣接する領域のピクセルを含むかもしれません。一方例えば、 9-patch で描画するような場合は、画像が連続しているので問題にはなりません。

次の画像はサブ画像、および右側に連続しない画像を含む例です。

必要ならば、テクスチャアトラスの各領域にパディングを追加する必要があります。

背景

Ebiten には自動テクスチャアトラスと呼ばれる機能があります。たとえ大量の ebiten.Image を作ったとしても、 Ebiten はそれらを一つのテクスチャアトラス領域上に可能な限り確保しようとします。これによって GPU に送られるグラフィックス命令を減らすことができ、効果的な描画が実現できます。さらに、 Ebiten ユーザーはこのことを気にする必要がないのです。素晴らしい!

Ebiten がテクスチャアトラスの一部を描画するとき、 Ebiten は隣接する領域のピクセルを露出させないために、あるハック (小細工) をしていました。このハックがないと、特に画像を回転または拡大縮小させて描画したときに、隣接するピクセルが露出してしまいます。これは一般的なゲームプログラミングの問題です。ハックとして、 Ebiten は次の 2 つのことをしていました:

両方ともコードのメンテナンス性の観点から問題でしたが、特に 2 つ目のハックが問題でした。一つの理由はパフォーマンスです。このハックのせいで if 分岐がシェーダーに追加されてしまい、パフォーマンスが落ちていました。もう一つの理由は、 1.12 に向けて導入中のカスタムシェーダーの定義が難しくなることです。カスタムシェーダー内で、画像からピクセルを取るときに簡単な方法で取れるのが望ましいのですが、このハックを実現するために特殊な関数などを用意しなければなりません。

ハックの削除

我々はこれらのハックを削除することにしました。代わりに全ての自動テクスチャアトラス上の画像は 1px のパディング付きで確保されます。描画元領域のチェックがなくとも、外側のピクセルは透明であり、隣接する領域を侵害してしまうことは決してないのです。

しかし、 Ebiten はサブ画像も取り扱えます。サブ画像は (*ebiten.Image).SubImage 関数で作られた画像です。これらは既存の画像の一部です。サブ画像はパディング付きで独立して確保することができません。全てのサブ画像をパディング付きの独立した画像として確保する案もありましたが、これはパフォーマンスとメモリの観点から良くありませんでした。もしユーザーが画像上で SubImage を 1px ずつずらした領域で大量に呼んだ場合、それぞれのサブ画像分領域が確保されてしまいます。

よって、サブ画像についてはパディングを追加するのは諦めました。結果として、サブ画像の描画は 1.11 と比べると違った結果を引き起こすことがあります。特に、画像を回転または拡大縮小させた場合、隣接する領域のピクセルが描画されることがあります。

拡大と回転したサブ画像によって、隣接領域が見えてしまった描画の例です。右側に本来描画されるべきではない緑色のピクセルが描画されています。この描画は最新の master ブランチ (aee5d6d7) でテストされました。結果は GPU によって異なります。


  

あなたがすべきこと

もし自作のテクスチャアトラスがあり、かつそれらの一部を拡大縮小または回転させて描画させ、かつそれらの画像が連続していない場合、パディングをそれぞれのパートに追加する必要があります。