libGDXへのColorKeyの追加

こんにちはHabr! この記事では、libgdxライブラリー(またはシェーダーを持つ他のライブラリー)にcolorkeyを追加することについて説明します。 ご存じのように、libgdxには「透明色」のネイティブサポートがないため、RGBA8888形式(ピクセルあたり4バイト)、または切り捨てられたRGBA4444形式(ピクセルあたり2バイト)でフルカラー画像を保存する必要があります。写真。 2Dゲームを作成する際、多くの場合、1ビットの透明度で十分です...特にモバイルプラットフォームでは...しかし、そうではありません...作りましょう!













RGBA8888



まず、RGBA8888形式の参照画像が必要です。これを使用して、以降のすべてのバイト保存の試行が比較されます。 レベルが描画されるタイルのセットを試してみましょう。空と小さな男には注意を払いません。 タイルサイズは512および512ピクセルです。 ディスクでは、テクスチャは透過性のある8ビットpngで保存され、20キロバイトを占有します(グラフィックは単純であるため、同じ結果で32ビットpngで保存できますが、常に透過性があるか、透過性がありません)。 ビデオメモリでは、正確に512 * 512 * 4 = 1メガバイトを占有します。 以下のホットな、これは唯一のテクスチャではありません...













RGBA4444



まず、切り捨てられたビット深度を使用するというアイデアが生まれます。 Pixelartはシンプルで、色数が少ないため、512キロバイトをすぐに節約できます。 私達は試みます:







草はわずかに色合いを変えただけで、我慢できますが、石はひどく苦しんでいます。 あなたがこれに我慢する準備ができているなら、あなたはこれ以上読むことができません。 準備ができていません。



シェーダーを書いています!



さらに苦労せずに、デフォルトのシェーダーをコピーし、フラグメントシェーダーを変更しました。







private static String fragmentShader = "#ifdef GL_ES\n" // + "#define LOWP lowp\n" // + "precision mediump float;\n" // + "#else\n" // + "#define LOWP \n" // + "#endif\n" // + "varying LOWP vec4 v_color;\n" // + "varying vec2 v_texCoords;\n" // + "uniform sampler2D u_texture;\n" // + "void main()\n"// + "{\n" // + " LOWP vec4 pixel = texture2D(u_texture, v_texCoords);\n"// + " gl_FragColor = v_color * pixel;\n" // + " if( pixel.rgb == vec3(1.0, 0.0, 1.0) ){gl_FragColor.a = 0.0;}\n"// + "}";
      
      





最後の3行のみが興味深いです。 まず、テクセルの色を保存します(重要!テクスチャの補間はオフにする必要があります。つまり、ロード時にNEARESTフィルタリングを使用します)。 次に、テクセルの色に頂点の色を掛けて、ピクセルの色を設定します。 頂点の色を混ぜない場合、この乗算は代入によって置き換えることができます。 最後に、テクセルの色と「透明色」を比較し、色が一致する場合はピクセルを透明にします。 「透明」として、古典的なvyviglazno-purple rgb(255,0,255)を選択しました。 確かに、条件演算子を取り除くことはできますが、...「そして、そうです!」









RGB565



これで、1ビットの透明度を格納するために4ビットを費やす必要がなくなり、色情報を格納するためにより多くのビットを費やすことができます。 vyrviglazikは透明になり、視覚による色に関する情報の損失は区別できません(入力画像によっては、特にグラデーションの場合、かなり区別できるようになります)。













これにより、品質と速度をほとんど損なうことなく、メモリ消費量を簡単かつ自然に半分に削減できます(結局、シェーダーの条件演算子を取り除きたい)。 しかし、もっと欲しい。 テクスチャをETC1形式に圧縮したいのですが、透明度が必要です。 それでも、RGBの6分の1です。 ディスク上ではなく、メモリ内に!







しようと...エピック失敗。 期待される。 タイトル画像は、この試みの結果です。 ETC1は非可逆圧縮形式であるため、結果が期待されます。 大きな損失を伴います。 vyrviglazny色は濁り、半vviglazovy色のピクセルが現れました。 通常、アルファチャネルは別のテクスチャに保存されます。 多くの場合-圧縮なし。 しかし、これは私たちの方法ではありません! シェーダーを少し台無しにした場合に何が達成できるかを見てみましょう。







芸能人のためのシェーダー



  if( vec3( min(pixel.r,0.95), max(pixel.g,0.05), min(pixel.b,0.95)) == vec3(0.95, 0.05, 0.95) ) { gl_FragColor.a = 0.0; }
      
      





シェーダーの最後の行のみを置き換えます。 現在、特定の色と厳密に比較するのではなく、わずかな偏差を付けて比較しています。赤と青の成分を少し暗くし、緑を明るくします。













ここでは、オリジナルと比較する必要さえありません。アーティファクトは肉眼で見ることができます。 しかし! 許容偏差で遊んだり、色間の「距離」を考慮した場合(十分な偏差がある場合)、特定のテクスチャセットで許容できる結果を得ることができます。 キロバイトごとに戦うとき、この方法はかなり受け入れられることが判明するかもしれません。







透明なjpeg?



どうして? テストを透明にするシェーダーが既にあります。 運が良ければ、結果はさらに使いやすくなります。 ディスク領域が占有されていることが重要であり、pngがあまりにもひどく押している場合、それはなぜですか。 圧縮プロファイルを「最大」と「非常に高い」の2つのオプションを同時に試します。













「最大」プロファイルでは、「透明色」でjpgを使用することが非常に可能です。 理論的に。 pngを使用すると収益性が低下する場合。







そのため、カラフルなテクスチャをほとんど失うことなく、完全に透明な領域を示すために「透明色」を取得することで、占有メモリを半分にすることができました。 おまけとして、透明なjpgの作成方法を学びました。







このメモが私だけでなく役に立つことを願っています。 誰かが条件付きステートメントなしで同等のコードを提供することをさらに願っています。 ご清聴ありがとうございました。









UPD

ユーザーFadeToBlackは、条件ステートメントなしで2つのシェーダーオプションを提案しました。







このシェーダーは、カラーキーで透明度が指定されているテクスチャでのみ使用できます。 実際の透明度のテクスチャは正しく表示されません。 この記事のシェーダーは、実際の透明度と「透明色」の両方のテクスチャを正しく処理します。







  void main() { LOWP vec4 pixel = texture2D(u_texture, v_texCoords); gl_FragColor = v_color * pixel; gl_FragColor.a = float(pixel.rgb != vec3(1.0,0.0,1.0)); }
      
      





そして、このシェーダーは記事のシェーダーと同等ですが、条件ステートメントはありません。 スプライト全体の半透明性は、テクスチャの透明度に関係なく、スプライトの頂点の色で設定できます。







  void main() { LOWP vec4 pixel = texture2D(u_texture, v_texCoords); gl_FragColor = v_color * pixel; gl_FragColor.a = gl_FragColor.a * float(pixel.rgb != vec3(1.0,0.0,1.0)); }
      
      






All Articles