アウトラインのレンダリングは、合計で 12 文字のみをレンダリングしない限り、曲率を概算するために 1 文字あたりに必要な頂点の数が原因で、「ダメ」のままです。代わりにピクセル シェーダーでベジエ曲線を評価するアプローチがありましたが、これらは容易にアンチエイリアシングされないという問題があり、これは距離マップ テクスチャ クワッドを使用すると些細なことであり、シェーダーで曲線を評価することは依然として必要以上に計算コストがかかります。
「高速」と「品質」の間の最良のトレードオフは、署名されたディスタンス フィールド テクスチャを持つテクスチャ化されたクワッドです。プレーンな通常のテクスチャ クワッドを使用するよりもわずかに遅くなりますが、それほどではありません。一方、品質はまったく異なる球場にあります。結果は本当に驚くべきもので、可能な限り高速で、グローなどの効果も簡単に追加できます。また、必要に応じて、この手法を古いハードウェアに適切にダウングレードすることもできます。
この手法については、有名なValve の論文を参照してください。
この手法は、ポリゴンを生成しませんが、暗黙的なサーフェス (メタボールなど) が機能する方法と概念的に似ています。これは完全にピクセル シェーダーで実行され、テクスチャからサンプリングされた距離を距離関数として取得します。選択したしきい値 (通常は 0.5) を超えるものはすべて "in" で、それ以外はすべて "out" です。最も単純なケースでは、シェーダーに対応していない 10 年前のハードウェアで、アルファ テストのしきい値を 0.5 に設定すると、まったく同じことが行われます (ただし、特殊効果とアンチエイリアシングはありません)。
フォントにもう少し太さを追加したい場合 (偽の太字)、コードを 1 行も変更せずに、わずかに小さいしきい値でうまくいきます ("font_weight" ユニフォームを変更するだけです)。グロー効果の場合、単純に、あるしきい値を超えるすべてのものを「イン」と見なし、別の (小さい) しきい値を超えるすべてを「アウトだがグローイン」と見なし、2 つの間の LERP を考慮します。アンチエイリアシングも同様に機能します。
この手法では、1 ビットではなく 8 ビットの符号付き距離値を使用することで、テクスチャ マップの有効解像度を各次元で 16 倍に増加させます (黒と白の代わりに、可能なすべての陰影が使用されるため、256 倍になります)。同じストレージを使用する情報)。しかし、16 倍をはるかに超えて拡大しても、結果はまだ許容できるものに見えます。長い直線は最終的に少し波打つようになりますが、典型的な「ブロック状」のサンプリング アーティファクトはありません。
ポイントからクワッドを生成するためにジオメトリ シェーダーを使用できます (バス帯域幅を減らします) が、正直なところ、得られるものはわずかです。GPG8 で説明されているように、インスタンス化されたキャラクターのレンダリングについても同じことが言えます。インスタンス化のオーバーヘッドは、描画するテキストが多い場合にのみ償却されます。私の意見では、その利点は、追加された複雑さと非ダウングレード性とは関係ありません。さらに、定数レジスタの量によって制限されるか、テクスチャ バッファ オブジェクトから読み取る必要がありますが、これはキャッシュ コヒーレンスにとって最適ではありません (そして、最初から最適化することが目的でした!)。
シンプルでプレーンな古い頂点バッファーは、アップロードを少し前にスケジュールすると、同じくらい高速 (場合によっては高速) になり、過去 15 年間に構築されたすべてのハードウェアで実行されます。また、フォント内の特定の文字数や、レンダリングする特定の文字数に制限されることもありません。
フォントの文字数が 256 文字を超えていないことが確実な場合は、ジオメトリ シェーダーでポイントからクワッドを生成するのと同様の方法でバス帯域幅を取り除くために、テクスチャ配列を検討する価値があります。配列テクスチャを使用する場合、すべてのクワッドのテクスチャ座標は同一、定数s
、および座標を持ち、レンダリングする文字インデックスに等しい座標t
のみが異なります。
しかし、他の手法と同様に、前世代のハードウェアとの互換性がないという犠牲を払って、期待される利益はわずかです。r
距離テクスチャを生成するための Jonathan Dummer による便利なツールがあります:説明ページ
更新: Programmable Vertex Pulling
(D. Rákos、「OpenGL Insights」、pp. 239) で
最近指摘されたように、最新世代の GPU のシェーダーから頂点データをプログラムでプルすることに関連する大幅な追加のレイテンシーやオーバーヘッドはありません。標準の固定機能を使用して同じことを行うのと比較して。
また、最新世代の GPU では、ますます合理的なサイズの汎用 L2 キャッシュ (nvidia Kepler では 1536kiB など) が使用されるようになっているため、バッファ テクスチャからクワッド コーナーのランダム オフセットを取得するときに、一貫性のないアクセスの問題が発生する可能性があります。問題。
これにより、バッファー テクスチャから一定のデータ (クワッド サイズなど) を取得するというアイデアがより魅力的になります。したがって、仮想的な実装では、次のようなアプローチを使用して、PCIe とメモリの転送、および GPU メモリを最小限に抑えることができます。
- このインデックスと を渡す頂点シェーダーへの唯一の入力として、文字インデックス (表示される文字ごとに 1 つ) のみをアップロードし、
gl_VertexID
それをジオメトリ シェーダーで 4 ポイントに増幅し、文字インデックスと頂点 ID (この唯一の属性として「頂点シェーダーで利用可能になった gl_primitiveID」) になり、変換フィードバックを介してこれをキャプチャします。
- 出力属性が 2 つしかないため (GS の主なボトルネック)、これは高速になります。
- フォント内の各文字について、ベース ポイントに対するテクスチャ化されたクワッドの頂点位置を含むバッファ テクスチャをバインドします (これらは基本的に「フォント メトリック」です)。このデータは、左下の頂点のオフセットのみを格納し、軸に沿ったボックスの幅と高さをエンコードすることで、クワッドごとに 4 つの数値に圧縮できます (ハーフ フロートを想定すると、これは 1 文字あたり 8 バイトの定数バッファーになります --典型的な 256 文字のフォントは、2kiB の L1 キャッシュに完全に収まる可能性があります)。
- ベースラインのユニフォームを設定する
- バッファ テクスチャを水平オフセットにバインドします。これらはおそらく GPU で計算することもできますが、CPU でそのようなことを行う方がはるかに簡単で効率的です。これは厳密に連続した操作であり、まったく簡単ではないためです (カーニングを考えてください)。また、別の同期ポイントとなる別のフィードバック パスが必要になります。
- フィードバック バッファーから以前に生成されたデータをレンダリングします。頂点シェーダーは、基点の水平オフセットとバッファー オブジェクトからのコーナー頂点のオフセットを取得します (プリミティブ ID と文字インデックスを使用)。送信された頂点の元の頂点 ID が「プリミティブ ID」になりました (GS が頂点を四角形に変えたことを思い出してください)。
このように、必要な頂点帯域幅を理想的には 75% (償却) 削減できますが、レンダリングできるのは 1 本の線だけです。1 回の描画呼び出しで複数の線をレンダリングできるようにしたい場合は、ユニフォームを使用するのではなく、バッファ テクスチャにベースラインを追加する必要があります (帯域幅のゲインを小さくします)。
ただし、75% の削減を想定しても、「妥当な」量のテキストを表示するための頂点データは 50 ~ 100kiB 程度しかないため (これは実質的にゼロです)GPU または PCIe バスに) -- 複雑さが増し、下位互換性が失われることに、本当に苦労する価値があるかどうかはまだ疑問です。ゼロを 75% 削減してもゼロにすぎません。私は確かに上記のアプローチを試したことはありません. それでも、誰かが本当に驚くべきパフォーマンスの違いを実証できない限り (数十億の文字ではなく、「通常の」量のテキストを使用してください!)、頂点データについては、シンプルでプレーンな古い頂点バッファーで十分であるという私の見解は変わりません。 「最先端のソリューション」の一部と見なされます。シンプルでわかりやすく、機能し、うまく機能します。
上記の「 OpenGL Insights 」を既に参照したので、ディスタンス フィールドレンダリングについて詳細に説明している Stefan Gustavson による「ディスタンス フィールドによる 2D シェイプ レンダリング」の章も指摘する価値があります。
更新 2016:
一方、極端な倍率で邪魔になるコーナーの丸みアーティファクトを除去することを目的とした追加の技術がいくつかあります。
1 つのアプローチは、距離フィールドの代わりに疑似距離フィールドを使用するだけです (違いは、距離が実際の輪郭ではなく、輪郭または端から突き出た想像上の線までの最短距離であることです)。これはいくらか良く、同じ量のテクスチャ メモリを使用して同じ速度 (同じシェーダー) で実行されます。
別のアプローチでは、 github で入手可能な3 チャネル テクスチャの詳細と実装で 3 の中央値を使用します。これは、問題に対処するために以前に使用された and-or ハックを改善することを目的としています。品質が高く、わずかに、ほとんど目立たず、遅くなりますが、3 倍のテクスチャ メモリを使用します。また、追加の効果 (グローなど) を正しく設定するのは困難です。
最後に、文字を構成する実際のベジエ曲線を保存し、それらをフラグメント シェーダーで評価することが実用的になりました。パフォーマンスはわずかに劣りますが (問題になるほどではありません)、最高の倍率でも素晴らしい結果が得られます。
この手法を使用して大きな PDF をリアルタイムでレンダリングする WebGL のデモは、こちらから入手できます。