バージョン4.1のOpenGLでのテキストレンダリングの最先端技術とは何ですか?[閉まっている]


199

OpenGLでのテキストのレンダリングについては、すでに次のような多くの質問があります。

しかし、主に説明されているのは、固定機能パイプラインを使用したテクスチャ付きクワッドのレンダリングです。確かにシェーダーはより良い方法を作らなければなりません。

私は国際化については特に心配していません。ほとんどの文字列はプロットの目盛りラベル(日付と時刻または純粋に数値)です。しかし、プロットは画面のリフレッシュレートで再レンダリングされ、かなりの量のテキストが表示される可能性があります(画面上のグリフは数千以下ですが、ハードウェアアクセラレーションレイアウトが適切であることには十分です)。

最新のOpenGLを使用したテキストレンダリングの推奨アプローチは何ですか?(アプローチを使用して既存のソフトウェアを引用することは、それがうまく機能することの良い証拠です)

  • たとえば、位置と方向および文字シーケンスを受け入れ、テクスチャ化された四角形を放出するジオメトリシェーダー
  • ベクターフォントをレンダリングするジオメトリシェーダー
  • 上記と同じですが、代わりにテッセレーションシェーダーを使用します
  • フォントのラスタライズを行う計算シェーダー

10
今日は主にOpenGL ES指向であるため、最新の状況に回答することはできませんが、GLUテッセレーターを使用してTTFをテッセレーションし、CPUで計算されたカーニングを使用して古い固定機能パイプラインを介してジオメトリとして送信すると、アンチに関する優れた視覚的結果が得られました-ほぼ10年前でさえ、ハードウェアと全体的なパフォーマンスのエイリアシング。したがって、(もちろん、基準によっては)「より良い」方法を見つけることができるのは、シェーダーだけではありません。FreeTypeはベジェグリフの境界とカーニング情報を出力できるため、実行時にTTFからライブで作業できます。
Tommy

QML2(Qt5のは)OpenGLと距離フィールドのテキストのレンダリングで、いくつかの興味深いトリックを行います。blog.qt.digia.com/blog/2012/08/08/native-looking-text-in-qml-2
mlvljr

ですから、もう失うことはありません。ここに、Valveの距離フィールドメソッドを実装するライブラリがあります。code.google.com/p/glyphyまだ試していません。また一見の価値があるかもしれません:code.google.com/p/signed-distance-field-font-generator
Timmmm

7
この「オフトピック」は、スタックオーバーフローの呪いです。真剣に?
Nikolaos Giotis 2014

1
:バージョン「それを行うにはどのように」もっと素朴stackoverflow.com/questions/8847899/...
チロSantilli郝海东冠状病六四事件法轮功

回答:


202

アウトラインのレンダリングは、合計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デモは、こちらから入手できます


1
それらは非常によく見えます(非常に小さいテクスチャーとデータを適切に補間しているため、単純なフィルタリングを使用し、ミップマッピングがない場合でも)。個人的には、多くの場合、「本物」よりも見栄えがよくなると思います。それは、私が「奇妙」と感じるものを生み出すヒントを与えるような奇妙な点がないためです。たとえば、明確な理由もなく、小さいテキストが突然太字になったり、ピクセル境界に飛び出したりすることはありません。これは、「実際の」フォントでよく見られる効果です。これには歴史的な理由があるかもしれませんが(1985年のディスプレイ)、今日では、そのようにする必要があるのは私の理解を超えています。
デイモン

2
うまくいきます、共有してくれてありがとう!HLSLフラグシェーダーソースが必要な場合は、こちらを参照してください。これをGLSLに適合させるには、clip(...)行をif (text.a < 0.5) {discard;}(またはtext.a < threshold)に置き換えます。HTH。
エンジニア、

1
更新していただきありがとうございます。もう一度賛成票を投じたいと思います。
Ben Voigt 2013年

2
@NicolBolas:よく読んでいないようですね。両方の質問が回答で説明されています。ケプラーは「最新世代」の例として与えられており、2回目のパスはなく(その理由も説明されてます)、仮想的な帯域幅節約手法が著しく高速である、またはトラブルに見合うだけの価値があるとは信じていません。ただし、信念は何も意味しません-知る必要があります(「通常の」量のテキストをボトルネックにすることを考えていないので、私は知りません)。それは可能性が 1の帯域幅について必死で、テキストの「異常な」量を持っている場合、それにもかかわらずで価値があります。
デイモン

3
@NicolBolas:あなたはその文章について正しい、ごめんなさい。それは確かに少し誤解を招くものです。前の段落で、「おそらくGPUでこれを生成することもできますが、それにはフィードバックが必要であり、... isnogud」と書きました。-その後、「フィードバックバッファから生成されたデータ」で誤って続行されました。これを修正します。実は週末に全部書き直すので曖昧さは少なくなります。
デイモン

15

http://code.google.com/p/glyphy/

GLyphyと他のSDFベースのOpenGLレンダラーの主な違いは、他のほとんどのプロジェクトがSDFをテクスチャにサンプリングすることです。これには、サンプリングに伴う通常の問題がすべてあります。つまり。アウトラインを歪め、低品質です。GLyphyは、GPUに送信された実際のベクトルを使用してSDFを表します。これにより、非常に高品質のレンダリングが実現します。

欠点は、コードがiOS用のOpenGL ESであることです。私はおそらくWindows / Linux OpenGL 4.xポートを作成するつもりです(ただし、作者が実際のドキュメントを追加することを願っています)。


3
GLyphyに興味のある方は、おそらくLinux.conf.au 2014での作者の講演をご覧ください:youtube.com/watch
Fizz

14

最も普及しているテクニックは、まだテクスチャ付きのクワッドです。しかし、2005年にLORIAは、ベクターテクスチャと呼ばれるものを開発しました。これを使用してTrueTypeまたはOpenTypeフォントをベクターテクスチャに変換すると、次のようになります。

http://alice.loria.fr/index.php/publications.html?Paper=VTM@2005


2
この手法を使用した実装を知っていますか?
luke 2013

2
いいえ(プロダクショングレードと同様)が、Kilgardの論文(リンクについては以下の私の回答を参照)に短い批評があります。この地域ではより多くの研究が行われています。Kilgardが引用した最近の研究には、research.microsoft.com / en
Fizz

9

マーク・キルガードの赤ちゃん、NV_path_rendering(NVpr)が上記のいずれにも言及されていないことに驚いています。その目的はフォントレンダリングよりも一般的ですが、フォントからカーニングを使用してテキストをレンダリングすることもできます。OpenGL 4.1も必要ありませんが、現時点ではベンダー/ Nvidiaのみの拡張機能です。基本的に、フォントをglPathGlyphsNVfreetype2ライブラリに依存するメトリックに取得するために使用するパスに変換します。次に、でカーニング情報にアクセスし、glGetPathSpacingNVNVprの一般的なパスレンダリングメカニズムを使用して、パス「変換された」フォントを使用してテキストを表示することもできます。(実際の変換はないため、曲線はそのまま使用されます。)

NVprのフォント機能の記録デモが、残念ながら、特に印象的ではありません。(たぶん、誰かがintertubesで見つけることができる非常に奇抜なSDFデモのラインに沿って1つを作成する必要があります...)

フォント部分の2011 NVpr APIプレゼンテーショントークはここから始まり次の部分に続きます。そのプレゼンテーションがどのように分割されるかは少し残念です。

NVprに関するより一般的な資料:

そして、「ステンシル」という単語は私の回答の前にこのページでヒットを生成しなかったため、このページに参​​加したSOコミュニティのサブセットは、かなり多数であるにもかかわらず、テッセレーションフリーのステンシルバッファーに気付かなかったようです一般にパス/フォントレンダリングのベースメソッド。KilgardのFAQのような投稿がopenglフォーラムにあり、テセレーションのないパスのレンダリング方法が、[GP] GPUをまだ使用しているにもかかわらず、沼地の標準3Dグラフィックとどのように異なるかを明らかにするかもしれません。(NVprにはCUDA対応チップが必要です。)

歴史的観点から見ると、キルガードは古典的な「テクスチャマッピングされたテキスト用のシンプルなOpenGLベースのAPI」、SGI、1997の著者でもあり、2011年にデビューしたステンシルベースのNVprと混同しないでください。


NVprなどのステンシルベースのメソッドやGLyphyなどのSDFベースのメソッド(他の回答ですでにカバーされているため、ここではこれ以上説明しません)など、このページで説明する最近のメソッドのすべてではありませんが、制限が1つあります。スケーリングのレベルにギザギザのない従来の(〜100 DPI)モニターでの大きなテキスト表示に適しています。また、高解像度の網膜のようなディスプレイで、サイズが小さくても見栄えがします。ただし、MicrosoftのDirect2D + DirectWriteが提供する機能、つまりメインストリームディスプレイ上の小さなグリフのヒントを完全には提供していません。(一般的なヒントの視覚的な調査については、たとえばこの誤植ページを参照してください。より詳細なリソースはantigrain.comにあります。)

現時点でMicrosoftが示唆していることを実行できる、オープンで製品化されたOpenGLベースのものについては知りません。(私はAppleのOS X GL / Quartzの内部について無知であることを認めています。私の知る限り、AppleはGLベースのフォント/パスレンダリングの方法を公開していないためです。MacOS 9とは異なり、OS Xはそうではないようですヒンティングを行うと、一部の人々は不快になります。)とにかく、INRIAのNicolas P. Rougierによって書かれたOpenGLシェーダーを介したヒンティングに対処する2013年の研究論文があります。OpenGLからヒントを得る必要がある場合は、おそらく読む価値があります。ヒンティングに関しては、freetypeのようなライブラリがすでにすべての作業を行っているように見えるかもしれませんが、私が論文から引用している次の理由により、実際にはそうではありません。

FreeTypeライブラリは、RGBモードでサブピクセルアンチエイリアスを使用してグリフをラスタライズできます。ただし、グリフを正確に配置するためにサブピクセル配置も実現したいので、これは問題の半分にすぎません。テクスチャ付きクワッドを小数ピクセル座標で表示しても、ピクセル全体のレベルでテクスチャ補間が行われるだけなので、問題は解決しません。代わりに、サブピクセルドメインで正確なシフト(0と1の間)を実現したいと考えています。これは、フラグメントシェーダーで行うことができます[...]。

解決策は簡単なものではないので、ここでは説明しません。(この論文はオープンアクセスです。)


Rougierの論文から私が学んだもう1つのこと(そしてKilgardは考慮していないようです)は、(Microsoft + Adob​​e)であるフォントパワーが1つではなく2つのカーニング仕様メソッドを作成したことです。古いものはいわゆるカーンテーブルに基づいており、freetypeでサポートされています。新しいものはGPOSと呼ばれ、HarfBuzzやpangoなどの新しいフォントライブラリでのみサポートされています。NVprはこれらのライブラリのいずれもサポートしていないようなので、一部の新しいフォントについては、NVprではカーニングがそのままでは機能しない可能性があります。このフォーラムのディスカッションによると、それらのいくつかは明らかに野生にあります。

最後に、複雑なテキストレイアウト(CTL)を実行する必要がある場合、OpenGLベースのライブラリが存在しないように思われるため、現在OpenGLは運が悪いようです。(一方でDirectWriteはCTLを処理できます。)HarfBuzzのようなオープンソースライブラリがあり、CTLをレンダリングできますが、それらを(ステンシルベースのメソッドを使用して)うまく機能させる方法はわかりません。 OpenGL。おそらく、再成形されたアウトラインを抽出し、NVprまたはSDFベースのソリューションにパスとしてフィードするためのグルーコードを記述する必要があります。


4
NV_path_renderingは拡張機能であり、問​​題を悪化させるベンダー独自のベンダーであるため、NV_path_renderingについては触れませんでした。私は通常、普遍的に適用可能な手法に対してのみ答えを出そうとします。
datenwolf 2014

1
まあ、ある程度はそれに同意できます。メソッド自体( "ステンシル、次にカバー")は、OpenGLに直接実装することは実際には難しくありませんが、以前のステンシルベースの試行が終了したため、そのままこの方法で実行すると、コマンドのオーバーヘッドが高くなります。Kilgrad氏によると、スキア(Ganesh経由)は現時点でステンシルベースのソリューションを試しましたが、あきらめました。CUDA機能を使用してNvidiaの下層に実装されている方法により、CUDA機能が実行されます。多数のEXT / ARB拡張機能を使用して、自分でStCを「マントル」しようとすることができます。ただし、Kilgard / NvidiaがNVprに関して2つの特許を申請していることに注意してください。
Fizz、2014

3

あなたの最善の策は、OpenGLバックエンドでcairoグラフィックを調べることだと思います。

3.3コアのプロトタイプを開発するときに私が抱えていた唯一の問題は、OpenGLバックエンドでの非推奨の関数の使用でした。1〜2年前だったので状況は良くなったかもしれない…

とにかく、将来的にはデスクトップのopenglグラフィックスドライバーがOpenVGを実装することを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.