Dynamic Hardware Tessellation Basics

NVIDIAのGame&Graphics Development Blog

昨年の12月からNVIDIAがGame&Graphicsの開発者に向けたBlogをはじめました。
Game & Graphics Development Blog
今回はその中での実質的な最初の記事になるDynamic Hardware Tessellation Basics(by Bryan Dudash, posted Wed, 19 Dec, 2012)を見てみたいと思います。

Dynamic Hardware Tessellation Basics

Direct3D 11(とそれをサポートするハードウェア)は、プログラマーにとって多くの新機能を持っています。その中でも最も興味深くパワフルな機能の一つがテッセレーションです。この機能は既に数多くの優秀なタイトルで使われており、コンテンツ制作負荷を最小限に抑えつつ、ビジュアルの向上を実現する素晴らしい方法です。

GPUベースのテッセレーションを向上させる方法として、ジオメトリのLODを動的に計算することが、最も良い方法であると言えると思います。(一通りのAPIを学んだ後ならば)ほとんどの部分の実装は簡単です。ただし、テッセレーションファクターの決定方法などいくつかの議論の余地がある項目があります。

[注:ここでの説明を明快にするため、ここから先は三角形のメッシュについて話します。4角形のための方法も存在しますが、今日の大部分のツールやエンジンが三角形プリミティブを使用しています。加えて、ここでのサンプルコードはD3D11のSM5.0のHLSLで提供されます。ただしこの技法のコンセプトはOpenGLにもそのまま適用できます。]

REVIEW – 復習

グラフィックスパイプラインにおけるハードウェアテッセレーションはさまざまなところで良く解説されています。この記事の読者はそのパイプラインと使い方を既に知っていると思いますが、もしも知らなかったら下記のリンクを参照してください。あせる必要はありません。
http://developer.download.nvidia.com/presentations/2009/GDC/GDC09_D3D11Tessellation.pdf

WHY USE TESSELLATION? – なぜテッセレーションを使うのか


図1:テッセレーションされた頭の近接したときのスクリーンショット。 ディスプレースメントマップは適用されていません。PN-Triangleでスムージングがかけられているだけです。おそらく、同じようなスクリーンショットを何度も見たことがあると思います。

動的テッセレーションは、プリミティブ単位で、メッシュのビジュアルのリアリティを簡単にコントロール出来るようにするだけでなく、LODの変更に伴う不快なメッシュのポッピングを解消します。生来テッセレーションハードウェアは、各エッジごとと各プリミティブ内部ごとのサブディビジョンファクターを動的に指定することが出来ます。これは強力なツールです。

以前はLODはホスト側で事前に計算されて、ジオメトリの差し替えが行われていました。結果的に、新しいモデルを転送するために帯域コストを増加させるだけでなく、ポッピングのようなアーティファクトを発生させていました。動的テッセレーションを使えば、プリミティブごとのスムースなLODシステムを実装できます。これによって、レンダリングされるジオメトリをより繊細にコントロールすることが可能になります。

スムースなLODシステムは動的なハードウェアテッセレーションのようなものが無ければ不可能です。テッセレーションを用いることで、静的に分割されたメッシュを用いるよりも良好なパフォーマンスが得られるという副産物もありますが、なんといっても動的テッセレーションによって、他の方法では得られないビジュアルのリアリティの向上を得られます。


図2:同じシーン、同じ組み合わせです。カメラを後方に少し動かしました。テッセレーションは三角形の密度を少し下げるように自動で調整します。クリックすると拡大されます。


図3:再び同じシーン、同じ組み合わせです。カメラをかなり後方に動かしました。 三角形のテッセレーションの密度は、ほぼ全くテッセレーションされていない状態と同じです。(画像は密度を確認するため拡大されています)

HOW DO I IMPLEMENT IT? – どうやって実装するか?

先ほど言った通り、この記事ではテッセレーションの基礎は扱いません。むしろ、スムースなLODシステムの実装のためにどのように動的テッセレーションを使うかを扱います。
そのために最も大切なことは、テッセレーションされるプリミティブのテッセレーション係数の計算です。

ハルシェーダーは各プリミティブがどのくらい分割されるかを決める部分を担当していることを思い出してください。

良好なパフォーマンスを得るため、ハル-コンスタントシェーダーがパッチ(内部)のテッセレーション係数を設定している間に、ハルシェーダーで制御点の定数として渡すための、テッセレーション係数(対角のエッジのテッセレーション係数)を計算することをお勧めします。

テッセレーション係数の計算は好みに応じて行いますが、いくつかの覚えておくべきキーポイントがあります。

  1. 基本的には距離に応じた手法が良いでしょう。
  2. クリップ空間が深度情報を扱うのに適していますが、もしも非線形な減衰が必要ならば、ビュー空間が必要でしょう。
  3. シルエットを忘れてはいけません。法線もしくはビュー、クリップの三角形の面積を使えば、シルエットが十分にテッセレートされないでしょう。

“CRACK!!!” – クラック

ハードウェアテッセレーションを使うとき、クラックと穴は、最も多い不具合のケースです。生来、テッセレーションのシステムは、プログラマーにメッシュを破綻させるのに十分なだけの、すべての制御を任せています。クラックの回避について話すことが、この記事全体の本質といえるでしょう。

まずは、悪いテッセレーションファクターが原因のクラックについて考えてみます。

エッジに沿ってテッセレーション係数を決めるアルゴリズムを設計しているときに注意することがあります。隣接するプリミティブの両サイドのエッジが同じ係数を持つように注意しなくては、Tジャンクションが形成され、これはほぼクラックの原因となります。個々のプリミティブに対して行う、分割の制御で、メッシュのC0連続性を簡単に破綻させることができるのです。

一般的に、一致したデータをテッセレーション係数の計算に用いる限り安全です。危険性のある不一致なデータの例として、法線、UV座標、頂点カラーなど、アーティストが位置を共有している頂点で別の値を指定できるものがあります。


図4: 二つの隣接するプリミティブ。個々のテッセレーション係数はそれぞれ計算されるので、一致するように注意する必要があります。

CHOOSING THE TESSELLATION FACTOR – テッセレーション係数の決定

プリミティブのテッセレーション係数の決定のいくつかの例について見てみましょう。
どのようにテッセレーション係数を変化させるか決めるため、今作っているゲームやコンテンツの特徴について考えてみたいかも知れません。
その際の、幾つかの良いスタート地点があります。

係数を決めるにあたり、重要な考慮すべきこと

  • 係数は、カメラマトリクスに基づくようにすること。
  • 入力に応じて、テッセレーションが連続的に変化すること。
  • プリミティブ上のどんなディスプレースメントマップの複雑さにも対応できる拡張性をもつこと
  • アーティストによって簡単に調整できるようにするためのパラメーター化されていること

シンプルな方法:”カメラからの距離”手法

テッセレーション係数計算するシンプルな方法は、カメラからの距離を1から64のレンジに変換し、スケーリングや値のモディファイを伴い、それをテッセレーション係数とすることかも知れません:

利点:比較的単純な計算。SomeFunction()は好みに応じて、いろいろ試すことが出来ます。
最も単純な方法は:

SomeFunction(x) = Cx,
  Cはアーティストによって設定される定数で、テッセレーション係数を大きくします。

欠点:三角形の実際のサイズに依存していない。そのため、不均等に三角形化されたメッシュでは、テッセレーションの過剰もしくは不足になる。

お勧めの方法:クリップ空間での球の直径による手法

堅牢な動的LODシステムのための、一般的なニーズに合致した手法は、クリップ空間でエッジの境界球の直径を使ってテッセレーション係数を決めることかもしれません。

  1. アーティストによって制御される理想的なジオメトリスケールのパラメーター「Pixels Per Edge」を定義します
  2. これを「Edges Per Screen Height」にコンバートして、シェーダー定数とします。
  3. クリップ空間に変換された各エッジの境界球の直径を求め、先ほどのシェーダー定数を掛け、テッセレーション係数とします。

注意点:

  • 「Screen Height」は、正確には「Rendert Target Height」を意味します。
  • ProjectionMatrix[1,1]は、投影マトリクスの[1,1]の要素を意味します。
  • Posclip.Wで割ることで、パースペクティブコレクトされた直径となります。

以下にこれを行なうHLSLを示します:

float GetPostProjectionSphereExtent(float3 Origin, float Diameter)
{
    float4 ClipPos = mul( VIEWPROJ, float4( Origin, 1.0 ) );
    return abs(Diameter * g_mTessProj11 / ClipPos.w);
}

float CalculateTessellationFactor(float3 Control0, float3 Control1)
{
    float e0 = distance(Control0,Control1);
    float3 m0 = (Control0 + Control1)/2;
    return max(1,g_fDynamicTessFactor * GetPostProjectionSphereExtent(m0,e0));
}

上記により、テッセレーション係数として使用する、エッジの方向に影響を受けないパラメーターが計算できます。パースペクティブコレクトされた直径の単位は[0..1]で、1はスクリーンサイズと同等であることを示します。EdgesPerScreenHeightを掛けることで、このパラメーターを希望するエッジの本数(テッセレーション係数)へとコンバートします。

WRAP-UP – おわりに

テッセレーション係数を計算が終われば、LODに必要なことは出来たも同然です。これを既存のメッシュの置換を行なうLODと組みあわせて、スムースなLOD遷移を実現することも出来るでしょうし、ドメインシェーダーでディスプレースメントマップを付加し、さらにメッシュのディテールを制御することも可能でしょう。

要するに、動的テッセレーションは、煩雑なメッシュの取り扱いや、ポッピングの問題、リソースのスワップに煩わされること無く、比較的簡単に、あなたのタイトルに、優れたジオメトリのリアリティを付加することが出来る方法だということです。

GPUがよりパワフルになるにつれ、GPUリソースがますます増えていくことになり、「ポリゴンの密度」のようなシンプルなスライダーを用意し、またそれを使うことにより、まだ使い切れていないGPUパワーをを引き出すことが可能になります。

今回は、動的テッセレーションによるLODで最も重要で、しばしば見落とされる部分はテッセレーション係数の決定にあることを説明することが出来ました。それではがんばってください。

QUESTIONS OR COMMENTS? – 質問とコメント

DevTalkフォーラムでフィードバックをいただければ幸いです。フォーラム(英語)に来てブログの記事に関して話し合ってみませんか。
ありがとうございました。

以上でDynamic Hardware Tessellation Basicsの記事は終わりです。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中