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