18

短縮版:

OpenGL には、ネイティブ サイズ (エッジ ピクセルを含む) で描画したときにアトラスからテクスチャをピクセル パーフェクトに 2D 描画し、非ネイティブ サイズにフィルタリングするときに高品質のスケーリングを可能にする一般的なアプローチはありますか?

問題の詳細:

テクスチャ アトラスからスプライトを描画するための何らかのシステムを作成しているとします。これにより、ユーザーはスプライトと、スプライトが描画されるサイズと位置を指定できるようになります。さらに、スプライトのサブ長方形のみを描画したい場合もあります。

たとえば、これは 64x64 のチェッカーボードです。

市松模様

...そして、そのテクスチャ アトラスに単独で:

テクスチャアトラスのチェッカーボード

注:ビューポートはデバイス ピクセルに 1:1 でマッピングするように設定されていると仮定します。

アプローチ

明確にするために編集: 以下の最初の 2 つのアプローチでは望ましい結果が得られないことに注意してください。

1. スプライトをネイティブ サイズで描画するには、単に GL_NEAREST を使用します

  • GL_NEAREST min/mag フィルタリングを使用するように OpenGL を設定します
  • 位置 (x,y) に描画するには、頂点を (x,y) から (x+64,y+64) に配置します
  • テクスチャ座標 (0,0) を使用 -> (64/atlas_size,64/atlas_size)

これはネイティブ サイズでの描画には問題ありませんが、ユーザーが 64x64 以外のサイズでオブジェクトを描画すると、NEAREST フィルタリングの結果は良くありません。また、ピクセル グリッドに合わせてテクセルを強制的に配置するため、オブジェクトが非整数ピクセル位置に描画されると、結果が悪くなります。

0.25 のピクセル オフセットで gl_nearest で描画 0.5 のピクセル オフセットで gl_nearest で描画

2. GL_LINEAR を有効にする

  • 線形フィルタリングでは、UV 座標をテクセルの中心にシフトする必要があります: (0.5/atlas_size,0.5/atlas_size)to (63.5/atlas_size,63.5/atlas_size). それ以外の場合、最も外側のピクセルについては、線形フィルタリングによってアトラス内のスプライトの隣接ピクセルがサンプリングされます。
  • (0,0) から (64,64) までの頂点を使用し続けると、次のようにテクスチャが両方向に 1px 引き伸ばされるため、次に頂点を変更する必要もあります。

UV 座標を 0.5 テクセルずらして描画すると、引き伸ばされます。

  • したがって、(0.5,0.5) から (63.5,63.5) までの頂点を使用する必要があります。一般に、テクスチャがネイティブ サイズ (a,b) の比率で描画される場合、頂点を「内側」にシフトする必要があります (a/2,b/2)。次の結果が得られます (紫色の背景)。

UV を 0.5 テクセル分シフトインし、頂点を 0.5 ピクセル分シフトして描画

頂点境界がピクセル間の中間に位置するため、背景とブレンドされるエッジ ピクセルを除いて、ピクセル単位で正確な描画が得られることに注意してください。

編集:また、この動作はアンチエイリアシングが有効になっているかどうかにも依存することに注意してください。そうでない場合、前のアプローチは実際にはピクセル パーフェクト レンダリングを提供しますが、スプライトがピクセル アラインメントからサブピクセル位置に移動するときに適切な遷移を提供しません。さらに、多くの場合、アンチエイリアシングは必須です。

3. テクスチャ アトラスにスプライトを埋め込む

エッジ ピクセルの問題に対する 2 つの明白な解決策は、スプライトのエッジをテクスチャ アトラスの 1 ピクセルの境界線でパディングすることです。次のいずれかを実行できます。

  • 透明ピクセルの 1 層でパディングし、頂点を縮小するのではなく (0.5a,0.5b) だけ拡張します。
  • スプライトの最も外側のピクセルの 2 番目のコピーでパディングし、(0,0) から (64/atlas_size,64/atlas_size) までのテクスチャのサンプリングに戻ります

これにより、基本的に線形スケーリングによるピクセル単位の正確な描画が得られます。ただし、ユーザーがスプライトのサブ四角形のみを描画できるようにすると、サブ四角形には明らかに必要なパディングがないため、これらのアプローチのいずれも失敗します。

私は何かを逃したことがありますか?この問題の一般的な解決策はありますか?

4

1 に答える 1