シェーダマテリアルとは、マテリアルをエフェクトファイルで表現するということ。
XML形式の中間データのフォーマットも安定してきたことだし、そろそろ
変換後のデータフォーマットを決めていきたいと思います。
シェーダマテリアル完結話、よろしくお願いします。
本レッスンでは、XML形式ファイルのフォーマットの説明と、コンバータの変換の工程を示します。
イラストの日付が2011/01/23と書いてありました。
描いていた当時、こんなに公開が遅れるとは思っていませんでした。
COLLADA(.daeファイル) とか使った方がもっと早くできて、楽だったかなぁと思いつつ
Simple XML ファイルで頑張ってみました。
ここから本題に入ります。
まずは SimpleExporter で出力した Simple XML ファイルの詳細について示してみたいと思います。
ということで、さっそく次のページにまとめました。
ひとまず、SimpleXMLファイルについては、この説明でOKとします。
さて、これをどう変換して表示したかを以下にまとめていきたいと思います。
まず頂点レイアウトと聞いて、それが DirectX 10 にとって何なのか理解できますでしょうか?
理解できない!?、そんな方は DirectX 10 入門 を読み返してください。
後半の方に頂点レイアウトについて記述しています。
そんなこんなで 一頂点の情報がどういったもので構成されているか描画側に伝えないといけない訳です。
DirectX 10 入門に書いていましたが、頂点レイアウトはエフェクトファイル(.fx)の
頂点シェーダの入力形式と合うようにしないといけません。
ここで誰もが考えるのが、エフェクトファイルから頂点レイアウトを自動作成する方法です。
たとえば次のエフェクトファイルについて、頂点レイアウトがどういったものになるか見てみましょう。
まずはハイライトした VS_INPUT 構造体に着目してください。
実はこれが頂点の入力レイアウトです。
描画プログラム側でこの構造体を解析して頂点レイアウトを作成すれば良いのです。
なんかこの話、前にも書きましたね。ここに書いています。
前回の後半部分の セマンティクス (DirectX HLSL) の説明のところですね。
型も決まっているし、ガッチガチのルールで固めてあるため、セミコロン「:」の後ろのセマンティクスを利用すれば誰でも自動作成できます。
これで頂点レイアウトの自動作成の課題はクリアできました。
続いて問題になるのは、そもそもの頂点バッファの作成の部分です。
実はこの頂点バッファ、入力レイアウトもそうですが今は自動作成しています。
これも前回述べたけど、最初は自動で作成して後から編集できるようにするコンセプトで作りました。
編集機能はまだ未実装なので使えませんが、自動作成のルール位ならここで述べられますので述べとこうと思います。
要は変換元のデータ SimpleXML から読み込んだ情報を元に頂点レイアウトとバッファを作成します。
例を示すとこんな感じ↓になります。
SimpleXML の MESH に頂点位置がある場合は入力レイアウトにセマンティクス POSITION0 が追加されます。
SimpleXML の MESH に頂点法線がある場合は入力レイアウトにセマンティクス NORMAL[n] が追加されます。
SimpleXML の MESH に頂点のテクスチャ座標がある場合は入力レイアウトにセマンティクス TEXCOORD[n] が追加されます。
SimpleXML の MESH にスキン情報がある場合は入力レイアウトにセマンティクス
BLENDINDICES[n], BLENDWEIGHT[n] が追加されます。
複数指定できるものについて[n]と書きました。
複数指定の場合は 0 からのインデックスが[n]に入ることになります。
たとえば頂点のスキンの重みが4個あったら BLENDWEIGHT0 〜 BLENDWEIGHT3 といった具合です。
これで SimpleXML の MESH 情報から頂点レイアウトを自動作成する課題もクリアできました。
クリアというか、こうやっています、という説明が完了しました。
レイアウトさえ決まってしまえば、後は頂点バッファを作成するだけです。
(どうでもいい話、バッファの一頂点の情報サイズをストライドと呼びます。)
頂点バッファに頂点データを書き込むのは、正直ただの作業です。
変換元のデータから頂点の情報を間違えないように書き込んでバッファ作成完了です。
ここで必要になるのが変換元のデータのフォーマット定義です。
ドキュメントに詳しく書いたので、今後忘れたらこちらを参照して思い出すことにしましょう。
まだ説明してなかったかもしれませんが、頂点レイアウトが決まったらついでに
エフェクトファイルも自動作成して出力するようにしています。
頂点レイアウトは上記の方法で決められるので、それを頂点シェーダの入力情報構造体として書き出します。
頂点シェーダの部分は、頂点位置をワールド配置とスキンフレームにより移動するように書いて
色に関してはシェーダの基本中の基本、ランバート拡散照明で出力するようにしています。
頂点で使用するスキンフレームの数など、頂点の情報がそろっているので
シェーダ部分を自動で書き出す処理は特に困ることはないでしょう。
シェーダ言語やシェーダプログラムについては、次の本があれば簡単に学習できます。
会社の同僚から借りている本ですが、これは本当にすばらしい一冊ですね。
DirectX 9 とありますが、10 でもそのまま使えますのでご心配なく。
さて、上の方で確認してもらった「スキンメッシュアニメーションに使われたエフェクトファイル」の
cbuffer cbNeverChanges
cbuffer cbChangesEveryFrame
の{}の中身に注目してみてください。
ここに書かれているのがエフェクトファイルの予約変数です。
cbuffer cbNeverChanges には一度設定したらそれ以降変更されない変数を記述します。
頂点色とか、ディフォルトの単位行列など
cbuffer cbChangesEveryFrame
には、毎フレーム変更される可能性のある変数を記述します。
World, View, Projection の行列のほか、スキンボーンの行列群、時間などです。
予約といっているけど、私が勝手に決めたことなので皆さんはみなさんでお好きにどうぞ。
今更だけど View と Projection はまとめておいた方がパフォーマンスは上がったかなぁと後悔してます。
なんとなく予想は付いていると思いますが、描画プログラム側でこれら予約変数へのアクセス子を取得しておき
描画時にそれぞれの変数値を設定(デバイスに転送)しています。
ひとまずこれでデバイスは描画に必要な情報を取得できたので、
エフェクトファイルの Vertex Shader の処理を実行して
モニタの所定の三角形領域のピクセル色を計算できるようになります。
さて、デバイスに情報を渡す部分が具体的になったところで
頂点情報(バッファ)とエフェクトファイルの予約変数をどうやって決定したかを見ていきましょう。
すでに頂点バッファ作成については、上記で触れていましたが
頂点位置や法線ベクトル、テクスチャ座標や頂点色などの設定については特に説明はいらないでしょう。
SimpleXML を読み込んで Blender → DirectX の座標系に直して設定するだけなのですから。
みなさんが興味や疑問を持つのはスキンメッシュ情報をどう扱っているか、だと思います。
以下にこの件についてまとめていきます。
さて、以下に述べることをイメージして下さい。
SimpleXML のモデルにはオブジェクトが複数あります。
そのオブジェクトの中にはマテリアルを使用している三角形が無数に存在しています。
ここで同じマテリアルを使用している三角形を束ねて、プリミティブとします。
ここで プリミティブ = マテリアル = エフェクトファイル の関係が築かれました。
ここでエフェクトファイルに渡す予約変数として、スキンメッシュ情報のボーンフレームの行列は
実際に使用しているものだけ設定するようにしたいと思います。(関係ない情報は省くようにします。)
そのためにまず、SimpleXML の情報から、頂点に対して使用しているスキンボーンフレームを登録します。
プリミティブを形成後、プリミティブを構成している頂点を調べて使用しているボーンフレームリストを構築します。
とまぁ、こんな具合です。これがコンバーターの処理になります。
上の方で確認した「スキンメッシュアニメーションに使われたエフェクトファイル」の
予約変数 Frames[32] というのが例として挙げられます。
これは、このエフェクトファイルでは 32 本のボーンフレームを使用するということです。
(参考に、モデルの全ボーン数は60本以上あります)
ここで勘違いしてほしくないのは、エフェクトファイル != 頂点 (同じ単位ではない)ということです。
頂点はあくまで、 BLENDINDICES[n], BLENDWEIGHT[n] の[n]個のボーンしか使用しません。
32 個のボーンを使用するわけではないということです。
この説明だけでは不十分ですね。もうちょっと詳しく説明します。
BLENDINDICES[n] には予約変数 Frames[32] に対するインデックスが入っています。
プログラム側では、これを可能にするようにコーディングすれば完成です。
仕様がしっかりしていますし、この辺はプログラマの腕の見せ所です。
(とかなんとか、時間をかければこんなの誰でも出来ますが…)
これでスキンメッシュ情報をどう扱っているかが明らかになりました。
アニメーションについては、どんな情報がアニメとして出力されているか確認しないと何も始りませんのて
まずは SimpleExporter で出力した Simple XML ファイルの詳細を確認してください。
アニメーションについては
高校レベルの行列の知識があれば特に悩むことなんてありません。
正直座標系の話と、行列の掛け算、座標系変換に逆行列を使用する、くらいしか出てきませんから…
後は説明不要ですね。
(数学についても本当は書きたかったのですが、この辺の基礎がわからないという方は自分で勉強してください。)
ちょっと本編とは関係ないです。
負荷テストやってみました。
Kio Miku 400体が歩くスキンメッシュアニメーションの様子です。(まるで軍隊(笑))
400体くらいならフレームレートは落ちていないようでした。
デバッグ情報を出力してみりゃいいのですが、SimpleDX10がまだ文字列の表示に対応していなくて
その辺はまた今度実装する予定です。
1000体とかでカクついてしまうことを確認しました。
GPU側に全投げしている部分で時間を食っていることを確認したので
シェーダの部分を軽くすればもっとパフォーマンス上がるかもしれません。
次回は物理エンジンの導入を検討しています。お楽しみに!
2011/07/31 初記。
2011/08/07 最終更新。