KimberliteFramework という、Kimberlite 機能一式を使えるまっさらな状態の WPF アプリを確認しました。
ポイントスプライトのシェーダコードを確認しました。
プラグイン機構を構築しました。
KimberliteFramework をプラグインに改造し、距離画像をポイントスプライトシェーダで3D表示しました。
前回、Kimberlite の機能追加を思いついたのでこれを行います。
詳細は前回の途中で示していますが、簡単にまとめると「シェーダパラメータとプロパティを結び付けたい」です。
ユーザーの操作を示して、システムに期待することを明らかにすれば機能追加作業が見えてくると思います。
まずはシェーダコードの編集です。
上記は単なる BOX を Blender から出力して、Kimberlite2 に読み込んだ状態です。
ディフォルトでシェーダコードが作成され、利用しているエフェクト予約変数が列挙されています。
現在の予約変数一覧がこちら↓
Kimberlite2 のプロパティタブでこれらの予約変数を追加すると…
ん?今は別に変数を見ておらず、UpdateNodeVariables という関数で、毎フレーム下記の通り更新しています。
なるほど、確かにこれは変えたいですね。
当時の自分の考えだとここはノードに関する情報を更新する、という意味なのですが
存在を確認してから更新したほうが正しいと考えるようになりました。
で、毎フレーム更新する予約変数なのか、そうでないのか判断しないといけなくはなります。
修正すると次のとおりです。
そして、ここで更新していない予約変数はいつ更新しているのか見てみます。
下記の通り、プリミティブごとのシェーダ変数更新関数で確認しました。
ん?予約変数使っていないですね。セマンティクスアクセスです。
かなり奇っ怪なことしていますが、テクスチャフラグが立つと Binary の Diffuse カラーがテクスチャインデックスになります。
(そんなところ節約してどうするって言いたいですが、常にいらない情報が詰まっているのが許せなかったみたい。
ひとまずこのしくみでエフェクトにテクスチャ16枚まで設定できます。)
テスト用にテクスチャ付きボックスを Blender から fbx 出力して dae 変換したところ
この dae の読み込みに失敗しました…調査、修正します。
修正完了しました。
fbxConverter が出す dae は、テクスチャのサンプリング指定が Items2D 固定で直にイメージへ結びつけています。
期待していた Items が null だったので例外が出ていました。
Collada のこういう自由すぎる参照ルールには困ったところですよね。
このほか、関連した不具合を修正しました。
頂点レイアウトにテクスチャ座標があったときに、自動シェーダ作成機構が誤ってディフューズテクスチャ変数を
作成するというものです。場合によりテクスチャなしでも、テクスチャ座標が埋め込まれることがあるので
これは不具合です。
さて、さらにディフォルトでアルファブレンドできるようステート設定をするようにしました。
これにより半透明なオブジェクトを表現することができるようになりました。
はい、では本題に戻って後からシェーダパラメータの結びつけ機能というものを考えていきましょう。
まず、テクスチャ付きBOXを読み込むと次のシェーダを自動で作成してくれます。
ここで、テクスチャファイルを別のものに置き換えたい場合について考えます。
やっぱりカラーfloat4をそれぞれインデックスにしているのはいただけない。
ここはどうにかして、可変長のインデックス配列を継ぎ足せたら良いのだが…
そこでカスタムアトリビュートの概念が出てきます。
しかし、全然アイデアが浮かばない…
あ、浮かんできました。
まずは現在シーンで使用しているイメージをリストアップします。
今はこんな感じですが、修正後は
こんな感じです。
いまのところ、プロパティが次のとおり、何もいじれません。
ファイルを指定できるように修正します。
修正しました。
ファイルを選択後、いつものようにエフェクトを適用すると
リソースを作り直して、描画結果に反映されます。
エフェクト反映後↓
テクスチャファイルを別のものに置き換えたい場合はプロパティタブからファイルを指定する。
解決しました。
さて、話が少し戻りますが、カメラの情報とライトの方向はいつ、誰が更新しているのでしょうか?
答えはメインウィンドウです。
3Dビューの中で中ボタンドラッグ中はそのマウス移動イベントで現在のエフェクト全てに対して
予約変数の更新をかけます。
シーンとプリミティブのインデックスを指定するところ -1 が指定されています。
これは全てのシーンにてすべてのエフェクトに対し、指定した予約IDのエフェクト変数を更新する
という意味です。
エフェクト変数を更新する作業は GPU への介入なので
メインウィンドウのイベントハンドラなどから実行する場合はロックが必要になります。
そして話を戻して、今度は予約変数を増やします。
時間とマテリアルに関する情報をシステムに指定してもらうようにします。
そして更新のタイミングですが…それはいつか?
考えていきましょう。
正直ノード姿勢の更新なども、一旦更新しましたフラグを見るのが良いと考えます。
プロパティにすべて更新しましたフラグを追加してみます。
ここまでで確認した UpdateNodeVariables を見てみます。
基本的に親ノードから階層構造で辿っていくので、末端までは再帰します。
このとき必要になるのが、親の姿勢です。
こればっかりは必要なので、親ノードの姿勢というものがどこに格納されているのか見ます。
ColladaBinary の Node クラスですね。
さっそく変数更新処理をノードの更新ありき、なければ以前の情報を参照する。
という処理に変えてみます。
あ、カメラの更新も確認する必要がありました。
視点の移動に関しては Camera.IsUpdated を更新し、フレーム描画終了後にフラグを落とします。
はい、対応しました。
今までどおりの描画で、かつ、ノードエフェクト変数の描画の必要性がなければ更新をスキップするよう修正しました。
続いて、マテリアルプロパティの方の更新について見てみましょう。
今のところテクスチャが指定されていたら、イメージを適用するという処理になっています。
修正前の現在のコードは下記の通り
要約すると、ひどい仕様。
かろうじて動作しているだけということがわかります。
また、無駄に毎フレーム更新しているので、これも避けたい。
まずは、ColladaBinary を拡張します。
マテリアルに対して、可変長のマップを定義できるように修正します。
UIは暫定ですけど、修正しました。
これはもうファイルフォーマットの修正なので、読み書き、既存のリソースも修正します。
修正しました。
加えて、参照イメージファイルも追加、削除できるようにしたかったので修正しました。
次に示すのはシーンノードを選択した時のプロパティタブの様子です。
そして、プリミティブプロパティ更新の処理を次のとおり修正しました。
はい、imageInfo.IsUpdated フラグを見て、立っている時だけセマンティクスアクセス
イメージデータの更新を行なうように修正しました。
これでテクスチャデータをGPUへ毎フレーム転送する不要な重い処理がなくなりました。
描画結果は変わらず、今までどおりのテクスチャマッピングができています。
さて、今のところまだマテリアルプロパティは反映されませんが
テクスチャマップを任意の数追加し、描画に利用できることを確認しましたので
マルチテクスチャ描画というものをテストしてみます。
まずは5枚のディフューズマップを用意し、それぞれに5枚のテクスチャを追加します。
上記が追加する5枚のテクスチャです。
モデルの設定は次のとおり Kimberlite2 の新しい機能で編集しました。
さて、うまく機能していればシェーダにて5枚のブレンドを書けば
ABCDEが重なった絵が見られるはずです。
今はまだ、上の画像で示した A だけが描画されている状態です。
シェーダコードを次のとおり書き換えます。
そして、忘れてはならないのがエフェクト変数の追加です。
さらに、忘れてはならないのが、マテリアル情報の更新です。
利用するテクスチャの枚数です。今は1なのでこれを5とします。
エフェクトを適用すると初回だけイメージ情報をGPUに転送し、以降は次の更新あるまで
転送をスキップします。そして描画結果が次のとおり。
期待した結果になりました。(ガッツポーズ)
ここまで、イメージファイルの追加、削除機能を導入
テクスチャマップの追加、削除機能を導入
エフェクト変数にテクスチャマップを指定することで
マテリアルで指定した枚数の任意の数のテクスチャをシェーダに適用できること
描画結果に反映されること、GPU側への転送処理を必要回数だけ行なうようにしました。
まだ、前回やりたいと思っていたことが一つもできていないので作業を進めます。
次はマテリアルプロパティを編集した時に、これがマテリアルに反映されるように修正します。
っと、保存した ColladaBinary ファイルを開いたらクラッシュしてしまいました。
テクスチャマップを編集できたのは良いですが、書き込み時に反映されていませんでした。
修正して、前回の状態を正しく復元できることを確認しました。
できることならば、自動設定を行ってほしいところです。
現在のシェーダ自動作成コードを少々編集します。
修正しました。
自動で書ける所といったら、カラーのアルファ値くらいでしたが
こちらがマテリアルプロパティの Transparency と連結するとようにしました。
そして、追加した予約シェーダ変数IDが次のとおり
次のように、透明度を上げると透明になります。
1.0 で透明、0.0 で不透明です。
最終的にプリミティブの変数更新関数は次の形になりました。
最後に、予約変数に時間を追加したいと思います。
追加しました。
こちらは毎フレーム、全てのエフェクトについて更新します。
ディフォルトでオブジェクトの不透明度が sin 波で点滅するようにしました。
はい、やっと前回やりたいと思っていたことが片付きました。
今回はこんなところで失礼します。
次回は指先認識の結果を使って、オブジェクトを指先位置にBOXを描画する予定です。
お楽しみに!
2014/07/24 初記。
2014/07/31 更新。