プロフィール

Na-7

Author:Na-7
SE(システムエンジニア)として約15年間システム系ソフト会社を勤めあげ、2008年3月退社。現在、ゲーム制作会社設立を目指して活動中。


アクセスカウンター


最新記事


最新コメント


最新トラックバック


月別アーカイブ


カテゴリ


DATE: CATEGORY:スポンサー広告
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
DATE: CATEGORY:三国志軍記開発
槍兵シェーダーインスタンス改修中
開発中のメイン画面
柵モデルのシェーダーインスタンス化が完了した。槍兵モデルは改修中とのことだが、パフォーマンス向上の手応えはあるようだ。



風邪気味で数日休んだこともあって、一週間ぶりの更新となりました。季節の変わり目ですので、皆さんもお体にお気を付けください。


◎第一段階

本番プログラムにシェーダインスタンスを実装します。まずはデモPJのパイプラインやランタイムを取り込みましょう。

1.メインPJフォルダ配下にDemoPipelineとDemoTypesWinのフォルダをコピーし、PJに追加する(追加>既存PJ)

2.メインPJの参照設定にDemoTypesの出力DLLを追加する(DemoTypes\bin\x86\Debug\DemoTypes.dll)

3.メインPJのContentの参照設定にDemoPipelineの出力DLLを追加する(DemoPipeline\bin\x86\Debug\DemoPipeline.dll)

4.簡易モデルのコンテンツプロセッサを「インスタンスモデルプロセッサ」に変更する

5.メインPJにInstancedModel.fxを追加する

6.メインPJにBasic.fxを追加し、ビルドアクションを「なし」とする

7.ビルドしてエラーにならないことを確認する

これで第一段階が完了しました。



◎第二段階

続いて第二段階「簡易モデルのシェーダーインスタンス化」です。

柵モデルをインスタンス化すべく改修を進めると、問題点が浮き彫りになってきました。


○改修前の仕様
・マップオブジェクトモデルは部隊単位で管理している

・各部隊に非シェーダーインスタンスを含む複数種のマップオブジェクトモデルが存在する

・各部隊に含まれる個々のマップオブジェクトモデルを順番に更新/描画している


○問題点
・シェーダーインスタンスは、マップオブジェクトモデルの種類毎に一括描画する必要がある


○改修案
・部隊が保持するリストとは別に、モデルの種類毎にリストを用意する

・マップオブジェクト初期化時に、シェーダーインスタンスの場合はリストに追加する

・シェーダーインスタンスの場合は個々の描画を行わず、一括描画を1回だけ呼び出す



◎リストと描画コードの保持者は?

ここまでは大体決まりましたが、新しいリストと描画コードを誰が持つべきか?でかなり悩みました。

最初はモデル自身にリストをstaticで持たせようと考えたのですが、モデルのインスタンスは部隊リスト配下に存在し、マネージャクラスからスマートにアクセスできません。また、モデル自身にリストと描画コードを持たせようとすると、‘同一モデルの複数回描画’を防止するためのフラグ管理が必要になるので、廃案としました。


参考までにデモPJを見てみると、モデルインスタンスのリストと描画コードは呼び出し元が保持しています。描画方式の違いを明確にするにはこの方が良いのでしょう。

しかしこれではオブジェクト指向のカプセル化の概念(データとメソッドはクラス中に隠蔽すべき)から外れてしまい、モデルの種類毎に異なるコードを実装しにくくなります。(例:定数レジスタパラメータをセットするコードは、アニメの有無により異なる)


他にも「(非シェーダーインスタンスを含む)描画呼び出しコードは全面改修すべきか?」などの関連要素があって、どうしたものかと散々悩みました(9案出して比較したりしました)が、結局以下のようにしました。

・モデルインスタンスのリストは呼び出し元が保持する

・描画コードはモデル自身が保持する

・呼び出し元から、リスト内先頭インスタンスの描画コードを1回だけ呼び出す

尚、描画呼び出しコードの全面改修は見送りました。マテリアルバッチに対応する場合は要改修ですが、ACLやSoftimageランタイムを使う場合は改修不要なので、結論先送りというわけです。



◎第二段階完了

というわけで、柵モデルのシェーダーインスタンス化が完了しました。

柵モデルのシェーダーインスタンス化完了

まだ柵モデルだけですが、少し軽くなった気がしますね。
第二段階完了です。

ちなみに、城も簡易モデルですが、城のシェーダーインスタンス化は第四段階で行います。



◎第三段階

続いて第三段階「板ポリUVアニメのシェーダーインスタンス化」です。

いきなりUVアニメ化は難しそうなので、まずは槍兵モデルに普通のシェーダーインスタンスを適用し、その後UVアニメ化を行うことにしました。

しかし改修して動かしてみると、槍兵どころか槍騎兵まで全く表示されなくなりました。何故槍騎兵まで消えてしまうのか不思議だったのですが、モデルデータの参照先は同じ板ポリモデルなので、片方だけにエフェクトを設定したつもりでも、両方に影響してしまうからでした。

エフェクト関連のコードを修正すると、槍騎兵だけ表示されるようになりましたが、槍兵は相変わらず表示されません。原因を調査すると、暫定で設定した回転値に問題があって、板ポリの裏面が手前になって板ポリが表示されていませんでした。



◎回転

回転値は、CreateConstrainedBillboardで作成したマトリックスから得たかったので、マトリックスを分解してクォータニオンを取得し、クォータニオンからオイラー角を得ようといろいろ試してみたのですが、結局断念しました。角度からクォータニオンや回転行列を生成出来るのに、何故その逆が簡単に出来ないんですかねぇ?

そんなわけで、自力で回転軸(RotateAxis)と回転量(Rotation)を設定することにしました。回転軸の的確な表現方法がわからずに苦戦しましたが、最終的には以下のようなコードになりました。

Vector3 tmpPos = mainMap.CameraPosition - pos[i];
float viewPlaneRot = (float)Math.Atan2(tmpPos.Z * -1.0f, tmpPos.X) - 0.25f * MathHelper.TwoPi;
Vector3 RotateAxis = Vector3.UnitY;
float Rotation = viewPlaneRot;


円柱状ビルボードの計算式はこんな感じじゃないでしょうか?
(「- 0.25f * MathHelper.TwoPi」は90度手前に回転させることを意味します)


余談(と言うか笑い話?)ですが、いろいろ試してようやく成功した時は以下のコードでした。

Vector3 RotateAxis = Vector3.Transform(Vector3.UnitY, Matrix.CreateFromYawPitchRoll(1.0f * MathHelper.TwoPi, 0, 0));

これって結局Vector3.UnitYだよね?ってことで現在のコードになりました。



◎次回予告

というわけで、シェーダーインスタンス化が完了したのはまだ柵モデルだけです。槍兵モデルの描画も早くなりましたが、アニメしないで場所だけ移動する中途半端な状態です。

次回は槍兵モデルのUVアニメ化ですが、1つ厄介な問題が見えてきたので、どうしたものかと思案中です。

果たして無事に動くのか?乞うご期待!(笑)

スポンサーサイト

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント

>リストと描画コード
リストは(同じ描画コードを用いる)描画対象インスタンスのリストのことですね?

考え方は色々ですが、そもそも描画ロジックそのものと描画対象リストをワンセットで新しい単独のクラスとする方法もあります。

よりコードの独立性が向上しメンテナンスが楽になりますし、同じインスタンス群を2度レンダリングするようなゲームの場合(例えば画面全体に普通に描画しつつ、右上に赤外線スコープの映像を重ねたい、対人レースゲームで分割画面を用いたい等)、描画クラスのインスタンスをいくつか別に用意するだけでよいので柔軟性があります。ご参考までに。

検討します

yohさんこんにちは~

>描画対象インスタンスのリストのことですね?
はい、その通りです。

>新しい単独のクラスとする方法
9案の中にはその案もあって、最後まで迷いました。

>同じインスタンス群を2度レンダリングする
なるほど、そのようなメリットもあるのですね。

UVアニメ化にあたり、リスト管理が複雑化しそうなので、改めてそちらの方式に移行すべきか検討します。

コメントの投稿


管理者にだけ表示を許可する

トラックバック


この記事にトラックバックする(FC2ブログユーザー)



copyright © ゲーム制作の舞台裏 all rights reserved.Powered by FC2ブログ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。