5

短縮版

ユーザーがズームインおよびズームアウトするときに座標を手動で再計算することなく、OpenGL マッピング アプリケーションで短いテキスト ラベルを描画するにはどうすればよいですか?

ロングバージョン

最大約 25 万ポイントのデータ セットを描画できるようにする必要がある OpenGL ベースのマッピング アプリケーションがあります。各ポイントには、通常 4 ~ 5 文字の短いテキスト ラベルを付けることができます。

現在、すべての文字を含む単一のテキストを使用してこれを行っています。ポイントごとに、ラベル内の各文字のクワッドを定義します。したがって、「Fred」というラベルの付いたポイントには 4 つのクワッドが関連付けられ、各クワッドはテクスチャ座標を使用してその単一のテクスチャに対応するキャラクターを描画します。

マップを描画するときは、マップ ポイント自体をマップ座標 (経度/緯度など) で描画します。次に、画面座標で各点の位置を計算し、その点のラベルの各クワッドの 4 つのコーナー ポイントを、再び画面座標で更新します。(たとえば、ポイントがスクリーン ポイント 100, 150 に描画されていると判断した場合、ポイントのラベルの最初の文字のクワッドを、左上のポイント 105, 155 で始まり、幅が特定の文字に適した 6 ピクセル、高さ 12 ピクセル. 次に、2 番目の文字は 120、155 などで始まります.) 次に、これらすべてのラベル文字クワッドが正しく配置されたら、直交スクリーンを使用してそれらを描画します.投影。

問題は、これらすべての文字クワッド座標を更新するプロセスが遅く、150k ポイントの特定のテスト データ セットで約 0.5 秒かかることです (つまり、各ラベルの長さは約 4 文字であるため、約 150k * [ 1 ポイントあたり 4 文字] * [1 文字あたり 4 座標ペア] アップデートごとに設定する必要がある座標ペア。

マップ アプリケーションにズームが含まれていなければ、更新のたびにこれらすべての座標を再計算する必要はありません。ラベルの座標を一度計算してから、表示用の四角形をシフトして正しい領域を表示するだけです。しかし、ズームでは、座標計算を行わないと機能させる方法がわかりません。そうしないと、ズームインすると文字が大きくなり、ズームアウトすると文字が小さくなります。

私が望んでいるのは (そして、OpenGL が提供していないと私が理解していること) は、固定された画面座標の四角形に四角形を描画する必要があることを OpenGL に伝える方法ですが、その四角形の左上の位置は、マップ座標空間内の特定のポイント。したがって、プリミティブ階層 (特定のマップ ポイントは、そのラベル キャラクター クワッドの親) と、この階層内で 2 つの異なる座標系を混在させる機能の両方が必要です。

私が設定できる魔法の変換マトリックスがあるかどうかを理解しようとしていますが、それを行う方法がわかりません。

私が検討したもう 1 つの方法は、各ポイントでシェーダーを使用して、そのポイントのラベル文字クワッド座標の計算を処理することです。私はこれまでシェーダーを扱ったことがなく、(a) シェーダーを使用してこれを行うことが可能かどうか、および (b) シェーダー コードでこれらすべてのポイントを計算することで、自分で計算するよりも実際に何かが得られるかどうかを理解しようとしています。 . (ちなみに、大きなボトルネックは、更新された座標を GPU にアップロードすることではなく、クワッド座標を計算することであることを確認しました。後者は少し時間がかかりますが、それは計算であり、更新される座標の数です。その 0.5 秒の大部分を占めます。)

(もちろん、別の方法としては、最初に特定のビューでどのラベルを描画する必要があるかをより賢くすることです。しかし、ここでは、すべてのラベルを描画する必要があると仮定して、解決策に集中したいと思います。)

4

3 に答える 3

4

したがって、基本的な問題 (「そうしないと、ズームインすると文字が大きくなり、ズームアウトすると文字が小さくなるため」) は、画面座標ではなくマップ座標で計算を行っていることですか? また、スクリーン座標でそれを行うと、より多くの計算が必要になりますか? 明らかに、レンダリングはマップ座標からスクリーン座標に変換する必要があります。問題は、マップから画面への変換が遅すぎることです。したがって、ポイントごとに 1 つのマップからスクリーンへの変換を行ってからスクリーン座標で作業するのではなく、主にマップ座標で作業し、最後に文字ごとにスクリーン座標に変換します。そして遅い部分は、あなた画面座標で作業し、OpenGL にマップ座標を伝えるためだけに手動でマップ座標に戻す必要があり、それらをスクリーン座標に変換します! それはあなたの問題の公正な評価ですか?

したがって、解決策は、その変換をパイプラインの早い段階でプッシュすることです。ただし、一見すると、OpenGL はすべてを「ワールド座標」(マップ座標) で実行したいように見えますが、画面座標ではそうではないため、なぜトリッキーなのかがわかります。

まず、なぜキャラクターごとに別々の座標計算を行っているのか疑問に思っています。どのフォントレンダリングシステムを使用していますか? FreeType のようなものは、文字列全体のビットマップ イメージを自動的に生成し、文字ごとに作業する必要はありません [編集: これは正しくありません。コメントを参照]。すべてのキャラクターのマップ座標 (または画面座標) を計算する必要はありません。ラベルの左上隅の画面座標を計算し、フォント レンダリング システムにラベル全体のビットマップを一度に生成させます。これにより、処理速度が約 4 倍になるはずです (ラベルごとに 4 文字を想定しているため)。

画面座標での作業に関しては、シェーダーについて少し学ぶことが役立つ場合があります。OpenGL について学べば学ぶほど、実際には 3D レンダリング エンジンではないことがわかります。これは、いくつかの非常に高速なマトリックス プリミティブが組み込まれた 2D グラフィック ライブラリです。OpenGL は実際には、最も低いレベルで画面座標で動作します (ピクセル座標ではなく、正規化された画面空間で動作します。X 軸と Y 軸の両方で -1 から 1 のメモリからだと思います)。世界座標で作業しているように「感じる」唯一の理由は、設定したこれらの行列のためです。

したがって、最後までマップ座標で作業している理由は、それが最も簡単だからだと思います.OpenGLは自然にマップから画面への変換を行います(マトリックスを使用)。自分で画面座標で作業したいので、それを変更する必要があります。そのため、OpenGL がデータを取得する前に、長い時間変換を行う必要があります。したがって、ラベルを描画するときは、次のように、マップからスクリーンへの変換マトリックスを各ポイントに手動で適用する必要があります。

  1. マップ座標に特定のポイント (ラベルの描画が必要) があります。
  2. map-to-screen マトリックスを適用して、ポイントをスクリーン座標に変換します。これはおそらく、OpenGL が頂点をレンダリングするときに行うのと同じアルゴリズムを使用して、ポイントに MODELVIEW および PROJECTION マトリックスを乗算することを意味します。したがって、GL_MODELVIEW_MATRIX と GL_PROJECTION_MATRIX をglGetして OpenGL の現在の行列を抽出するか、自分で行列のコピーを手動で保持することができます。
  3. これで、マップ ラベルが画面座標に表示され、ラベルのテキストの位置が計算されます。上記のように、これは X 軸と Y 軸に 5 ピクセルを追加するだけです。ただし、ピクセル空間ではなく、正規化された画面空間にいることに注意してください。つまり、パーセンテージで作業していることに注意してください (たとえば、0.05 単位を追加すると、画面空間の 5% が追加されます)。アプリケーションは解像度に合わせてスケーリングされるため、ピクセル単位で考えない方がよいでしょう。しかし、本当にピクセルで考えたい場合は、解像度に基づいてピクセル対単位を計算する必要があります。
  4. glPushMatrix を使用して現在の行列を保存し、次にglLoadIdentityを使用して現在の行列をIDに設定します。頂点を変換しないように OpenGL に指示します。(PROJECTION マトリックスと MODELVIEW マトリックスの両方でこれを行う必要があると思います。)
  5. 画面座標でラベルを描画します。

したがって、シェーダーを実際に記述する必要はありません。シェーダーでこれを行うことができ、ステップ 2 が確実に高速化されます (独自のソフトウェア行列乗算コードを記述する必要はありません。GPU での行列乗算は非常に高速です)。しかし、それは後で最適化することになり、多くの作業が必要になります。上記の手順は、画面座標で作業するのに役立ち、OpenGL マップ座標を指定するためだけに多くの時間を無駄にする必要がなくなると思います。

于 2011-06-16T03:34:20.027 に答える
0

サイドコメント:

""" 文字列全体のビットマップ イメージを生成し、文字ごとに作業する必要はありません ... ラベルの左上隅の画面座標を計算し、フォント レンダリング システムに次のビットマップを生成させます。ラベル全体を一度に. これにより、処理が約 4 倍高速化されます (ラベルごとに 4 文字を想定しているため)。

フリータイプであろうとなかろうと、各文字ではなく各ラベルのビットマップ画像を計算することは確かにできますが、それには次のいずれかが必要になります。

  • ラベルごとに 1 つずつ、何千もの異なるテクスチャを保存する

    • 多くのテクスチャを保存するのは悪い考えのように思えますが、そうではないかもしれません。

    また

  • 画面の更新ごとに、各ポイントの各ラベルをレンダリングします。

    • これは確かに遅すぎるでしょう。
于 2011-06-16T18:26:46.393 に答える
0

解決策をフォローアップするためだけに:

私はこの問題を本当に解決したわけではありませんが、そもそもラベルをいつ描画するかについては賢くなりました。あまりにも多くの文字を描画しようとしているのか (つまり、典型的なポイント密度の典型的な画面では、ラベルが互いに近すぎて有用な方法で読み取ることができないほど多くの文字) を描画しようとしているのかどうかをすぐに判断することができました。まったくラベル付けしないでください。一度に最大約 5000 文字を描画すると、上記のように文字座標を再計算する際の速度が著しく低下することはありません。

于 2011-08-01T19:21:51.190 に答える