DirectXで太さを変えながら、AAでラインをレンダリングする最速の方法


14

そのため、正確には.NETでSharpDXを使用してDirectX開発を行っています(ただし、DirectX / C ++ APIソリューションは適用可能です)。DirectXを使用して、直交投影で線をレンダリングする最速の方法を探しています(たとえば、科学アプリの2D線画のシミュレーション)。

私がレンダリングしようとしているプロットの種類のスクリーンショットは次のとおりです。 enter image description here

これらの種類のプロットでは、ラインごとにアンチエイリアシング(またはフルスクリーンAAのオン/オフ)の有無に関係なく、数百万のセグメントがあり、太さが変化するラインを持つことは珍しくありません。ラインの頂点を非常に頻繁に(たとえば、20回/秒)更新し、可能な限りGPUにオフロードする必要があります。

これまで私は試しました:

  1. ソフトウェアレンダリング、たとえばGDI +は実際にはパフォーマンスの低下ではありませんが、明らかにCPUに負荷がかかります
  2. Direct2D API-GDIよりも遅く、特にアンチエイリアスがオンの場合
  3. この方法を使用するDirect3D10 は、CPU側で頂点カラーとテッセレーションを使用してAAをエミュレートします。また遅い(プロファイルを作成し、頂点位置の計算に時間の80%が費やされる)

3番目の方法では、頂点バッファーを使用して三角形のストリップをGPUに送信し、200ミリ秒ごとに新しい頂点を更新します。100,000ラインセグメントのリフレッシュレートは約5FPSです。理想的には数百万人が必要です!

今、私は最速の方法は、GPU、例えばジオメトリシェーダーでテッセレーションを行うことだと考えています。頂点をラインリストとして送信するか、テクスチャでパックし、ジオメトリシェーダーでアンパックしてクワッドを作成できます。または、生のポイントをピクセルシェーダーに送信し、ブレゼンハムライン描画を完全にピクセルシェーダーで実装します。私のHLSLは2006年のさびたシェーダーモデル2であるため、最新のGPUが実行できるクレイジーな機能については知りません。

質問は次のとおりです。-誰もこれを以前にやったことがありますか、試してみる提案はありますか?-ジオメトリをすばやく更新してパフォーマンスを改善するための提案はありますか(たとえば、20msごとの新しい頂点リスト)。

1月21日更新

それ以来、LineStripおよびDynamic Vertex Buffersを使用するGeometryシェーダーを使用して、上記のメソッド(3)を実装しました。現在、10万ポイントで100FPS、1,000,000ポイントで10FPSを取得しています。これは大きな改善ですが、今ではフィルレートと計算が制限されているため、他の手法やアイデアについて考えました。

  • ラインセグメントジオメトリのハードウェアインスタンス化はどうですか?
  • Sprite Batchはどうですか?
  • 他の(ピクセルシェーダー)指向のメソッドはどうですか?
  • GPUまたはCPUで効率的にカリングできますか?

あなたのコメントと提案は大歓迎です!


1
Direct3DのネイティブAAはどうですか?
APIビースト

5
プロットしたい曲線の例をいくつか示していただけますか?あなたは100万の頂点に言及していますが、あなたのスクリーンはおそらく100万ピクセルをはるかに超えていないでしょう、その量は本当に必要ですか?私はあなたがどこでもその完全なデータ密度を必要としないように思えます。LODを検討しましたか?
サムホセバー

1
リンクした2番目のアプローチは良いようです。更新する動的な頂点バッファーを使用していますか、それとも新しいバッファーを毎回作成していますか?

1
おそらく動的頂点バッファーを使用する必要があります(多くの作業は不要で、頂点バッファーを動的に指定し、バッファーをマップし、データをコピーし、マップ解除します)が、ボトルネックが最初の頂点生成である場合、おそらく今はあまり役に立たない。GSを使用してCPUの負荷を軽減することに狂気があるとは思わないでください

1
頂点の数がGPUに対して大きすぎる場合でも、入力のダウンサンプリングを試みることができます-画面が数千ピクセルの幅になる場合、1つの曲線に数億の線分を実際に必要としませんせいぜい。

回答:


16

Y = f(X)グラフのみをレンダリングする場合は、次の方法を試すことをお勧めします。

曲線データはテクスチャデータとして渡され、永続化され、glTexSubImage2Dたとえば、部分的な更新が可能になります。スクロールが必要な場合は、循環バッファを実装して、フレームごとにいくつかの値のみを更新することもできます。各曲線はフルスクリーンクワッドとしてレンダリングされ、すべての作業はピクセルシェーダーによって行われます。

1コンポーネントのテクスチャコンテンツは次のようになります。

+----+----+----+----+
| 12 | 10 |  5 | .. | values for curve #1
+----+----+----+----+
| 55 | 83 | 87 | .. | values for curve #2
+----+----+----+----+

ピクセルシェーダーの動作は次のとおりです。

  • データセット空間で現在のフラグメントのX座標を見つける
  • 例えば データを持つ4つの最も近いデータポイント。例えばX値がある場合41.3、それは選ぶだろう40414243
  • 4 Y値のテクスチャを照会します(サンプラーがいかなる種類の補間も行わないことを確認してください)
  • X,Yペアをスクリーンスペースに変換する
  • 現在のフラグメントから3つのセグメント4つのポイントのそれぞれまでの距離を計算します
  • 現在のフラグメントのアルファ値として距離を使用します

潜在的なズームレベルに応じて、4をより大きな値に置き換えることができます。

この機能を実装する非常に迅速で汚いGLSLシェーダーを作成しました。後でHLSLバージョンを追加することもできますが、あまり手間をかけずに変換できるはずです。結果は、さまざまな行サイズとデータ密度で以下に見ることができます。

curves

明確な利点の1つは、転送されるデータの量が非常に少なく、描画呼び出しの数が1つだけであることです。


これはかなりクールなテクニックです。以前にGPGPUを実行したことがあるので、テクスチャをデータでパックすることは私が知っていることですが、これについては考慮していません。最大サイズ(アップロード可能な最大データセットなど)気にせずに遊んでコードをダウンロードするつもりです。パフォーマンスを楽しみにしています。
ABT博士

@ Dr.ABTデータセットのサイズは、最大テクスチャサイズによってのみ制限されます。適切なデータレイアウトでは、何百万ものポイントが機能しない理由はわかりません。私のコードは確かに製品品質ではありませんが、問題があれば個人的に私に連絡してください。
サムホセバー

@SamHocevar乾杯-やってみよう。OpenGLのサンプルマインドをコンパイルしてからしばらく経ちました!:-)
ABT博士

私はテクスチャのロードを使用していないが、ピクセルシェーダーに線を描画して正しい方向に向けることができる実際のOpenGLの例を提示したように、答えとしてマークします!! :)
ABT博士

4

アンチエイリアス処理されたラインのレンダリングに関するGPU Gemsの章:Fast Prefiltered Linesがありました。基本的な考え方は、各ラインセグメントをクワッドとしてレンダリングし、各ピクセルで、ラインセグメントからのピクセル中心の距離のガウス関数を計算することです。

これは、グラフ内の各線セグメントを個別のクワッドとして描画することを意味しますが、D3D11では、ジオメトリシェーダーやインスタンスを使用してクワッドを生成し、GPUに転送されるデータ量をデータポイントだけに減らすことができます自分自身。おそらく、頂点/ジオメトリシェーダーによって読み取られるようにデータポイントをStructuredBufferとして設定し、描画するセグメントの数を指定して描画呼び出しを実行します。実際の頂点バッファーはありません。頂点シェーダーはSV_VertexIDまたはSV_InstanceIDを使用して、どのデータポイントを調べるかを決定します。


ちょっとありがとう-はい、その記事は知っていますが、残念ながらソースコードはありません:-( GeoShader + FragShaderの前提は、この種の作業の勝者のようです。GPUにデータを効率的に取得することが、トリッキーな部分!
博士ABT

ちょっと@NathanReedに戻ります-ジオメトリシェーダーまたはインスタンス化を使用して四角形を描画する場合、Point1 / Point2は四角形の反対の頂点になります。ピクセルシェーダーでPt1とPt2の間の線までの距離を計算するにはどうすればよいですか?ピクセルシェーダーは入力ジオメトリの知識を持っていますか?
Dr. ABT

@ Dr.ABT数学ラインのパラメーターは、インターポレーターを介してピクセルシェーダーに送信する必要があります。ピクセルシェーダーは、ジオメトリに直接アクセスできません。
ネイサンリード

これは、GeometryShaderから出力を送信するか、インスタンス化することで実行できますか?余分なクレジット(もし興味があるなら)のために、私はここにQを追加しました:gamedev.stackexchange.com/questions/47831/...
博士ABT

@ Dr.ABTこれは、ピクセルシェーダーに補間および入力される頂点/ジオメトリシェーダーからの出力を書き込むことにより、テクスチャ座標、法線ベクトル、およびそのようなすべてのものが通常ピクセルシェーダーに送信されるのとまったく同じ方法です。
ネイサンリード

0

@ Dr.ABT-これに対するおologiesびは質問であり、答えではありません。上記のSam Hocevarによる答えの中にそれを尋ねる方法が見つかりませんでした。

グラフの線を最終的にどのように実装したかについて、詳細を教えてください。同じようにWPFアプリも必要です。これについて共有できる詳細やコードを事前に感謝します。


あなたが知っているようにその答えがない編集を喜ばとコメントとしてそれを置く
MephistonX

私はそれをやろうとしていましたが、元の投稿には「コメントを追加」ボタンがありません。
ErcGeek

こんにちはErcGeek、申し訳ありませんが、この情報は私の会社のものであるため、最終的なソリューションを共有することはできません。上記の例と提案により、私たちは正しい軌道に乗ることができました。ではごきげんよう!
ABT博士

ある程度の評判に達するまでコメントを投稿することはできません。そして、これらすべてのダウン票は、あなたにその機会を提供するものではありません。
マティアスリュックガードローレンツェン

その男は、最終結果を知りたいと思って、彼に利用可能な唯一の方法でそれを求めることで罰せられるべきではありません。答えを支持しました。
デビッドチン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.