テッセレーション
今回はテッセレーションについて見ていきます。
テッセレーションはDX11から導入されたシェーダでシェーダモデル5.0以上に対応しているGPUで動作します。
これを利用することで1つの三角形や線分、四角形をより細かく分割することが出来ます。
この機能はディスプレイスメントマッピング(英訳:Displacement Mapping)やカメラからの距離に応じてポリゴンの数を調節するLevel of Detail(略称:LOD)などに利用されているようです。
テッセレーションは頂点シェーダの後に実行される3つのステージから構成されており、ハルシェーダとドメインシェーダの2つのシェーダとテッセレータステージのGPUの固定機能に分けることが出来ます。
ハルシェーダでテッセレータステージがどのようにプリミティブを分割するかを決め、ドメインシェーダで分割した頂点の移動などの処理を行います。
GPUの固定機能を使用しているためシステムセマンティクスの意味を理解することがテッセレーションの理解につながります。
参考サイト:
ドキュメント テッセレーションの概要 (日本語)(英語)
「DirectX 11」のテッセレーション-テッセレーションの意味と重要性
西川善司の3DゲームファンのためのDirectX 11テッセレーション活用講座
概要
今パートではテッセレーションの使い方について見ていきます。
対応するプロジェクトはPart12_Tessellationになります。
三角形の分割
それでははじめに三角形の分割を仕方を見ながらハルシェーダとドメインシェーダについて見ていきましょう。
ハルシェーダ
ハルシェーダは与えられたプリミティブをどのように分割するかを決めるシェーダになります。
ちなみに分割を行うプリミティブのことをパッチ(英訳:Patch)と呼ばれているようです。
ドキュメント:ハル シェーダーの設計
(日本語)
(英語)
今まで見てきたシェーダと比べますとコード量や設定するものが多いですが順に見ていきます。
エントリポイント
まず、エントリポイントとなるmain関数についてみていきます。
ハルシェーダのエントリポイントには属性が多数用意されています。
指定できる属性
- domain
ハルシェーダで処理を行うパッチを指定します。三角形のtri,四角形のquad,線分のisolineの3つ指定出来ます。
- partitioning
分割方法を指定します。指定できるのはinteger,fractional_even,fractional_odd,pow2になります。
- outputcontrolpoints
ハルシェーダが作成する制御点の個数を指定します。
- outputtopology
テッセレータの出力するプリミティブを指定します。line,triangle_cw,triangle_ccwが指定できます。
- patchconstantfunc
パッチ定数データを計算する関数を指定します。上のコードだとCalcHSPatchConstants関数がパッチ定数データを計算する関数になります。
- maxtessfactor
最大となる分割係数の値を指定します。
エントリポイントの引数にあるInputPatchはハルシェーダの入力値となる制御点の配列を表しています。
制御点の型と個数は<…>で指定します。
ドキュメント:InputPatch
(日本語)
(英語)
説明が後になってしまいましたが、エントリポイントは1つのパッチにつきその制御点の個数分呼びだされます。
パッチ定数関数
パッチ定数関数がパッチ1つごとに1回実行され、分割数の決定やこちらで用意したパラメータを計算します。
言い換えるとパッチを分割するためのデータを計算する関数となります。
分割数を指定するにはシステムセマンティックのSV_TessFactorとSV_InsideTessFactorを利用します。
ドキュメント:
SV_TessFactor
(日本語)
(英語)
SV_InsideTessFactor
(日本語)
(英語)
この2つは分割するプリミティブによって意味合いが変わります。
三角形の場合は以下の意味になります。
三角形の場合の分割数の指定
- SV_TessFactor
三角形の辺の分割数を指定します。
三角形の場合、このシステムセマンティクスを指定する場合は必ず要素数が3つのfloat型の配列にする必要があります。
配列の0番目は三角形の0番目の頂点と向き合っている辺に対応しています。
配列の1番目は三角形の1番目の頂点と向き合っている辺と、配列の2番目は三角形の2番目の頂点と向き合っている辺にそれぞれ対応しています。
- SV_InsideTessFactor
三角形の内部の分割数を指定します。
三角形の場合、このシステムセマンティクスを指定する場合は必ずfloat型の変数にする必要があります。
手動で分割数を決める際は不均等な分割になってしまう可能性があります。
意図的にそうしている場合はいいですが、避けたい場合はHLSLの組み込み関数を利用するといいでしょう。
コード例はサンプルを御覧ください。
ドキュメント:三角形のテッセレーション係数の修正を行う組み込み関数
ProcessTriTessFactorsAvg
(日本語)
(英語)
ProcessTriTessFactorsMax
(日本語)
(英語)
ProcessTriTessFactorsMin
(日本語)
(英語)
ハルシェーダは以上のことに注意して実装する必要があります。
ドメインシェーダ
ドメインシェーダはテッセレータステージで分割、生成した頂点を加工するシェーダになります。
ドキュメント:ドメイン シェーダーの設計
(日本語)
(英語)
使用している構造体が多いですが、エントリポイント自体はシンプルな処理になります。
エントリポイントにつけるdomain属性はパッチのプリミティブを指定します。
ハルシェーダと同じくtri、quad、isolineが指定できます。
ドキュメント:domain属性
(日本語)
(英語)
エントリポイントの引数にはハルシェーダからの出力値とパッチ内での位置(SV_DomainLocation)が渡されます。
SV_DomainLocation
ドキュメント:
(日本語)
(英語)
生成された頂点のパッチ上の位置を求めるために使うものになります。
ドメインシェーダのエントリポイントには必ずこのセマンティクスを付けた引数を用意する必要があります。
このセマンティクスを付けた変数の型はdomain属性で指定したプリミティブによって異なります。
三角形の場合はfloat3にする必要があります。
float3の各要素はxが0番目の頂点に、yは1番目の頂点、zが2番目の頂点の係数になります。
位置を求めるには以下のように補間してください。
三角形の分割は以上になります。
設定しないといけないものが多いですが使っていくうちに慣れるでしょう。
残りの四角形と線分は三角形と異なる部分について見てきます。
四角形の分割
四角形を分割する際は各domain属性にquadを指定してください。
ハルシェーダ
処理自体は三角形の分割とそう変わりはありません。
分割数の指定の仕方が少し変わったぐらいでしょうか。
四角形の場合の分割数の指定
- SV_TessFactor
四角形の辺の分割数を指定します。
四角形の場合、このシステムセマンティクスを指定する場合は必ず要素数が4つのfloat型の配列にする必要があります。
配列の0番目は四角形の0番目のと2番目をつなぐ辺に対応しています。
配列の1番目は四角形の0番目のと1番目をつなぐ辺に対応しています。
配列の2番目は四角形の1番目のと3番目をつなぐ辺に対応しています。
配列の3番目は四角形の2番目のと3番目をつなぐ辺に対応しています。
- SV_InsideTessFactor
四角形の内部の分割数を指定します。
四角形の場合、このシステムセマンティクスを指定する場合は必ず要素数が2つのfloat型の配列にする必要があります。
内部の分割はいわゆる縦と横それぞれの分割になります。
配列の0番目は四角形の0番目のと1番目をつなぐ辺の向きに分割する数になります。
配列の1番目は四角形の0番目のと2番目をつなぐ辺の向きに分割する数になります。
三角形の時と同じように分割数の調節を行う組み込み関数が用意されています。
ドキュメント:四角形のテッセレーション係数の修正を行う組み込み関数
ProcessQuadTessFactorsAvg
(日本語)
(英語)
ProcessQuadTessFactorsMax
(日本語)
(英語)
ProcessQuadTessFactorsMin
(日本語)
(英語)
Process2DQuadTessFactorsAvg
(日本語)
(英語)
Process2DQuadTessFactorsMax
(日本語)
(英語)
Process2DQuadTessFactorsMin
(日本語)
(英語)
ドメインシェーダ
ドメインシェーダも行う処理自体に変わりはありません。
パッチ内の位置を計算する方法が変わるぐらいでしょう。
四角形の時のSV_DomainLocation
四角形の場合はfloat2にする必要があります。
意味は0~1の範囲のUV座標系とおなじになります。
(0,0)で0番目の頂点の位置に、
(1,0)で1番目の頂点の位置に、
(0,1)で2番目の頂点の位置に、
(1,1)で3番目の頂点の位置になります。
位置を求めるには以下のように補間してください。
線分の分割
線分を分割する際は各domain属性にisolineを指定してください。
ハルシェーダ
線分も前2つとほぼ同じです。
ただし、分割数を指定するためのSV_TessFactorの意味が少々異なり、SV_InsideTessFactorは必要ありません。
線分も同じように分割数の調節を行う組み込み関数が用意されています。
ドキュメント:線分のテッセレーション係数の修正を行う組み込み関数
ProcessIsolineTessFactors
(日本語)
(英語)
ドメインシェーダ
ドメインシェーダもあまり変わりはありません。
SV_DomainLocationの意味が変わったぐらいです。
線分の時のSV_DomainLocation
線分の場合はfloat2にする必要があります。
x成分が生成した線分を表すものでy成分が分割数となります。
両方共0~1の範囲を取りますので注意してください。
線分の場合はパッチ定数関数の出力を使うのが必須となりそうです。
点から三角形を生成
テッセレーションステージもジオメトリシェーダと同じく点から三角形を生成することが出来ます。
その際はパッチの制御点に1を指定する必要があります。
InputPatchとOutputPatchの制御点数に1を設定している以外は三角形の分割と同じです。
まとめ
今回はテッセレーションについて見てきました。
GPUの固定機能を使用しているため設定する部分が多く、使いこなすには知識も必要となります。
ですが行っていることは複雑ではないのでハルシェーダ、バッチ定数関数、ドメインシェーダの役割を把握すればうまくあつかえるようになるのではないでしょうか?
後、本文では説明しませんでしたがテッセレーションを使用する際、入力アセンブラステージのプリミティブトポロジにはパッチの制御点の個数を表すものを設定する必要がありますので忘れずに設定してください。
このパートでグラフィックスパイプラインも一通り見終えました。
ここまでくればDX11の一山を登り終えたといえます。
シェーダについてはこれ以上新しい物はありません。
ここまでの内容があれば後は3DCG知識があれば実装に困ることはあまりないと思います。