プロフィール

Na-7

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


アクセスカウンター


最新記事


最新コメント


最新トラックバック


月別アーカイブ


カテゴリ


DATE: CATEGORY:スポンサー広告
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
| BLOG TOP |
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つ厄介な問題が見えてきたので、どうしたものかと思案中です。

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

スポンサーサイト

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
NDocで出力したHTMLヘルプ
NDocで出力したHTMLヘルプ
無償のAPIリファレンス作成ツールで、ライブラリやデバッグコンポーネントのドキュメントを出力した。MSDN風のHTMLヘルプやMSヘルプで、APIの仕様が把握しやすい。



◎ライブラリ解析

今回はシェーダーインスタンスのライブラリ解析です。

前回メモをUPしてしまったので、今回も一応UPしておきます。(個人的なメモなので内容は気にしないでくださいw)

今回は、XNBファイル読み込み処理のフロー図を作成しました。

シェーダインスタンスサンプル
XNBファイル読み込みフロー図(PDF形式)




IE(Internet Explorer)からPDFファイルを直接開けない場合

PDFファイルを一旦ダウンロードしてから開くか、
(Adobe/Acrobat)Readerの設定を変更してください。
「編集>環境設定>インターネット>PDFをブラウザに表示」チェックを外す



実はコンテントタイプリーダーとコンテントリーダーの違いをこれまであまり意識せずにいましたが、この図を作成したら完全に別物であると理解しました。コンテントマネージャでコンテントリーダーが作成されて、参照値を引き継ぐ流れも把握しました。



◎プログラミングの勉強

今回は寄り道が多かったので、幾つか挙げておきます。

まずマニュアル(ヘルプ)ですが、プログラミングガイドの「Content Pipeline」を一通り読みました。7割ぐらい理解できたと思いますが、身に付いたのは3割程度かもしれません。まぁ、何がどこに記述されているか把握しておいて、必要な時に読み返すことが出来ればいいですね。

また、配列コピー等が出てきたので、Arrayメンバを一通りチェックしました。便利そうなメソッドが沢山ありますが、知らずに自分でコードをガリガリ書いてました(汗)

また、デリゲートが出てきたので、こちらの記事で勉強させて頂きました。デリゲートはまだ自力で組んだことがありません。イベントハンドラで必須なので、早く体得したいですね。

また、パーシャル(部分クラス)が出てきたので、こちらの記事を拝読させて頂きました。私はC#言語をろくに知らずにプログラムしてるので、他の話も含めてとても勉強になりました。



◎リファクタリング機能

中編が良かったので前後編も一通りチェックすると、前編の「統合開発環境としての新機能」も参考になりました。

無料のExpressEditionでは、リファクタリング機能は「名前の変更」と「メソッドの抽出」しか使えないので無視していたのですが、試しに使ったらとても便利で感心したので触れておきます。

「名前の変更」はクラスやメソッド名を変更する機能です。下手なものまで置換されると怖いので、私はこれまで1つ1つ確認しながら置換していたのですが、統合環境は名前空間をきちんと把握しているので、下手に手動で置換するよりも安心です。

また、私は「メソッドの抽出」を検索機能のようなものかと思ってました(データベースプログラミングでは、抽出は照会検索機能に当たる)が、「指定されたコード・ブロックを新たなメソッドとして抽出する機能」だそうです。

そんな機能知らんという人は、騙されたと思って適当に5行ぐらい選択して「リファクタ→メソッドの抽出」としてOKボタンを押してください。きっと「これは使える!」と思うでしょう。



◎その他の基本テク

データヒントは変数がオブジェクトである場合にも、そのメンバを展開して表示できるのは大変便利ですね。私は最近これに気付いたのですが、それまではいちいち
Console.WriteLine()
で表示してました。試行錯誤に時間がかかった理由の1つですね。

その他に最近使い始めた基本テクは以下の通りです。

・マウス右クリックメニュー>アウトライン
 →サンプルの階層構造を掴むのに便利

・編集>詳細>選択範囲のコメント
 →試行錯誤時の迷走中コードを一括コメント化



◎NDoc

この際だからコメント関連も抑えておきましょう。XNA公式サンプルでは至る所に
/// <summary>
といった記述がありますが、これにどんな意味があるのか調べると、「コードコメントWebレポート」という機能に行き当たりました。これは、プロジェクトで定義されているオブジェクトメンバ等の構造やコメントなどの情報を.htmファイルとして作成する機能ですが、Visual Studio .NET 2003以前でしか使用できないそうです。

じゃあどーすんだ?と調べたら、こちらにNDocの紹介記事がありました。

で、実際にインストールして、試しにライブラリのドキュメントを作成しようとNDOCでビルドすると、エラーが発生しました。

Exception: NDoc.Core.DocumenterException
バージョン 2.0 は互換性のあるバージョンではありません。


このエラーはこちらの方法で回避できたのですが、今度は別のエラーが…Documentation Typeによってエラー内容は異なりますが、MSDNとした場合は下記エラーが発生しました。

Unable to find the HTML Help Compiler. Please verify that the HTML Help Workshop has been installed.

VC# 2005/2008 Express EditionではHTMLヘルプコンパイラが標準でインストールされないようなので、こちらからダウンロードしてインストールしました。

これでようやくビルドが成功したのですが、出力されたドキュメントを開くと日本語が全て文字化けしました。NDOCのオプションのSdkDocLanguageをJapaneseにして再度ビルドすると、ようやく日本語ドキュメントが生成されました。

NDocで出力したHTMLヘルプ   NDocで出力したMSヘルプ

デフォルトではHTMLヘルプとMSヘルプが両方作成されるので好きな方を参照できますが、コンテントパイプラインを含めてビルドすると、MSヘルプだけ開けなくなりました。オプション指定を変更すれば開けるようになるかもしれませんが、とりあえずHTMLヘルプがあるので試してません。

予想外にいろいろありましたが、一度手順が確立すると2回目以降は問題無いので、ライブラリやコンポーネントなど汎用的なものはドキュメント化しておくと手軽に参照できます。また、XNA公式サンプルや他者作成プログラムを利用/解析する際も役に立ちそうです。



◎次回予告

ちょっと寄り道しすぎて時間がかかってしまいましたが、ライブラリの解析が終わりましたので、次回は本番プログラムに実装します。どれだけ早くなるか楽しみですね~!

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
パフォーマンス再測定(メインPC柵)
パフォーマンス再測定
前回のサンプルやモデルに疑念が生じたので、モデルを入れ替えてパフォーマンスを測定。疑念は払拭されたようだ。



◎パフォーマンス再測定

前回の測定は興味本位だったので、測定結果が妥当か否か判断基準がありませんでした。その上、測定結果にやや不可解な点があったので、「作成したサンプルにミスがあるのではないか?」「モデルのパフォーマンス効率が悪いのではないか?」といった疑念が生じました。

そこで改めて目的と判断基準を明確にして、パフォーマンスを再測定します。


○測定目的

・デモPJの測定値を理想値と捉え、
 今後のパフォーマンス判断基準とする

・前回サンプルがデモPJと同等のパフォーマンス
 であることを確認する
 →10%以上低下した場合、原因を追及する

・モデルを入れ替えて、モデルの違いによる
 パフォーマンスの変化を確認する
 →50%以上低下した場合、モデルの見直しを検討する


○測定結果

パフォーマンス再測定(サブPC板ポリ差し替え)

XNAシェーダインスタンス
パフォーマンス測定結果&比較データ(PDF形式)



○判定

・今回の測定データを
 今後のパフォーマンス判断基準とする

・デモPJと前回サンプルの差は誤差の範囲
 →2つのPJのパフォーマンスは同等

・モデルのパフォーマンスは一部偏りがあるものの、
 全体としては悪くない
 →両面板ポリアニメは、本番ゲーム実装時に
  片面板ポリビルボードアニメとする
 →柵モデルは、両面板ポリに変更することを後日検討する


○その他

・前回サンプルはやや不可解な傾向があったが、
 デモPJでも同じ傾向が見られた
 →パフォーマンスに影響を与えるプログラムミスはない

・デモPJの初期状態(高解像度)では、
 メインPCのパフォーマンスに影響は無かったが、
 サブPCは1000個の時だけ
 CPU負荷が5.8ms(20%)まで低下した
 →原因不明


解像度とパフォーマンスの関係に首を突っ込む余裕は無いので、当面は800×600固定とし、最終段階で高解像度に対応することとします。



◎コンテントパイプライン解析

前回サンプルの疑念が解消されたので、このまま本番ゲームに組み込んでも問題無いのですが、XNAでは数少ない日本語コメントサンプルですので、中のコードをじっくり拝見しましょう。

技術習得のやり方は人によって様々ですが、私の場合は、このようなメモを書きながら何時間もかけてじっくり解析します。(個人的なメモなので内容は気にしないでくださいw)
こうすると、後日必要な時に「そういえば…」と思い当たる可能性が高くなります。難点は、消化に時間がかかりすぎることです(^_^;


参考までに、コンテントパイプラインの概要フロー図を作成しました。入力から出力までの流れが一目で把握できると思います。

シェーダインスタンスサンプル
コンテントパイプライン概要フロー図(PDF形式)




◎MeshPacker

解析してようやく気付きましたが、MeshPackerはシェーダインスタンスのデモに使用されていません。

最も時間をかけて解析したモジュールを使わないのはトホホなので、プログラムを改修してメッシュのパック化にチャレンジします。



◎簡易城モデルのパフォーマンス

MeshPackerの動作確認には複数メッシュモデルが必要なので、簡易城モデルを使用します。まずはモデルの仕様と現状パフォーマンスを確認しましょう。

メッシュオブジェクト数:4(→5)
頂点数:196(→220)
プリミティブ数:138(→138)
マテリアル数:5(→5)
テクスチャ数:2

元の数値はメタセコイア、(→)の数値はModelクラスで読み込んだ場合の数値です。柵モデル等と比べると桁が違いますが、ゲーム内での登場数はMAX36個なので大目に見てくださいw

簡易城モデル36個   簡易城モデル1000個

○簡易城モデル:36個
 メインPC:60fps1.30ms
 サブPC:60fps1.02ms

○簡易城モデル:1000個
 メインPC:40fps18.65ms
 サブPC:20fps40.57ms

これがMeshPacker適用前のパフォーマンスです。メッシュをパックしたら早くなるでしょうか?



◎改修

NodeContentツリーを再帰的に検索し、そのノードがメッシュだったらパックするように改修したのですが、改修前と同じオブジェクトが描画されてしまいます。

MeshContent.Identityの設定など、怪しい点が幾つかありますが、コンテントパイプラインはデバッグ機能が大幅に制限されるので、何が悪いのかはっきりしない状況です。(そもそも、MeshPackerがシェーダインスタンスで利用可能なのか確信が持てません><)

うーん…。
アニメインスタンスと同じやり方でないとダメなのかな?
アニメインスタンスのモジュール解析までやりたくないんだけどなぁ…ブツブツ。



◎次回予告

私は面倒なことがキライなので、再測定や解析をやる前は正直気が重かったのですが、時間をかけて頑張っただけの甲斐はあったと思います。

しかし、調子に乗って軽い気持ちでMeshPackerに手を出したら、簡単に詰まってしまいました。
まだまだ修行が足りないなぁ(ーー;


MeshPackerは成功したとしても多大な効果は期待してないので、チャレンジは一旦諦めて次に進みます。
というわけで、次回はライブラリの解析と本番実装です。

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
シェーダーインスタンスのサンプルプロジェクト
サンプルPJ実行画面
GamefestJapan2008Demoの一部を抽出して、解析用のサンプルPJを作成した。



◎カスタムモデルクラス

三国志軍記には「地形モデル」「ビルボードアニメ」「簡易モデル」の3タイプのモデルが登場します。このうち、地形モデルは1つだけですのでインスタンシングは適用しません。「ビルボードアニメ」は前回まででシェーダインスタンスが適用可能となりました。残る課題は「簡易モデルのパフォーマンス向上」です。

簡易モデルは、現状城と柵の2種類だけですが、今後は順次増えていくことでしょう。種類が増えても柔軟に対応できるように、カスタムモデルクラスを導入します。

カスタムモデルクラスは公式サンプルにもありますが、そちらはコメントが英語なので、Gamefest2008デモプログラムを解析して取り込みます。



◎モジュール一覧表

デモPJ(プロジェクト)は、コンテントパイプラインのモジュールだけで16個もあります。私はまだコンテントパイプラインに不慣れで苦手意識が若干あるので、この数を見ただけでちょっと気後れしてしまいます。せめてモジュール一覧表があればいいのに…。

というわけで、適当に作ってみました。

GamefestJapan2008Demo インスタンス関連モジュール一覧表

不要モジュールを削除するか?それとも残して丸ごと取り込むか?少し悩みましたが、結局削除しました。また、7番のモジュール(複数のメッシュ→単一メッシュ)が複数テクスチャに対応してるのか気になりましたが、どうやら対応しているようです。



◎サンプルプロジェクトの作成

サンプルを眺めただけだと、その場は理解した気になっても、いざという時にその知識が思い浮かばず、役に立たないことが往々にしてあります。これは、サンプルを自身のスキルとして消化できていないからです。

サンプルを自らのスキルとして消化するには、部分的に変更して動きを確認したり、自分のプログラムに取り込んだりして、自らの意思で「扱う」ことが重要です。

しかし今回のデモPJは他のデモも含んでいるので、余分な要素が多すぎて扱いにくい状態です。まずは余分な要素をざっと省いて、扱いやすいサンプルPJを作成しましょう。

1.新規PJ作成

2.新規PJ配下にDemoPipelineとDemoTypesWinをコピーし、新規PJに追加する(追加>既存PJ)

3.新規PJの参照設定にDemoTypesの出力DLLを追加する(DemoTypes\bin\x86\Debug\DemoTypes.dll)

4.新規PJのContentの参照設定にDemoPipelineの出力DLLを追加する(DemoPipeline\bin\x86\Debug\DemoPipeline.dll)

5.新規PJにデバッグコンポーネントを追加する

6.新規PJに簡易モデルを追加する

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

8.新規PJにInstancedModel.fxを追加する

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

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

これで下準備が整いました。あとはGameModelDemo.cs等を参考にしながらプログラムを改修します。



◎透過処理の有効化

「ゲームオブジェクトの描画」の中で、
gd.RenderState.AlphaBlendEnable = false;
と記述されていたので、これをtrueとしたのですが、透過処理はオンになりません。でも、fpsカウンタ等を表示すると透過されます。

実際に透過処理をオンにしているコードを調べたら
spriteBatch.Begin();
でした。レンダーステート初期設定値をこちらで確認しようとしたら、「SpriteStateMode」と記述されていたので少し混乱してしまいました。恐らくバージョン違いか記述ミスによるもので、XNA3.1ではSpriteBlendModeが該当すると思います。

というわけで、アルファブレンド用のレンダーステート初期設定値を追加し、透過処理をオンにしました。ちなみに、ヘルプには「このバージョンのBeginは~」と記述されているので、バージョンによって初期設定値が異なるかもしれません。



◎サンプルプロジェクト

「GamefestJapan2008Demo」から、シェーダインスタンス関連モジュールのみ抽出し、自作モデルに置き換えたサンプルPJです。

シェーダーインスタンスのサンプルプロジェクト

シェーダインスタンスのサンプルプロジェクト(XNA3.1用)

・ゲームパッド/キーボード等の操作無し
・デバッグコンポーネント組込済

元のPJは複数のデモが合体して分かりにくいので、シェーダインスタンスだけ勉強したい人は、こちらの方が分かりやすいと思います。



◎パフォーマンス測定

サンプルPJのパフォーマンスを測定して、UVアニメサンプルと比較してみましょう。

一度に描画するインスタンス数:100個
光源計算:あり

○非アニメモデル:1000個
  メインPC:60fps2.90ms
  サブPC:60fps1.86ms

○非アニメモデル:5000個
  メインPC:59fps13.72ms
  サブPC:30fps26.43ms

○非アニメモデル:10000個
  メインPC:30fps27.26ms
  サブPC:16fps49.72ms

サブPC1000個が妙に早いのは謎ですが、全体的にはUVアニメより1.5~2倍ぐらい遅いですね。非アニメとなったので早くなるかと思ったのに、意外です。

遅くなった要素としては、下記事項が考えられます。

頂点数:4 → 16
プリミティブ数:4 → 10
テクスチャ数:1 → 3



◎実験その1

メタセコイアでテクスチャ割り当てを変更し、減らしてみました。

テクスチャ数:3→1
(他の条件は同一)

○非アニメモデル:1000個
  メインPC:60fps2.63ms
  サブPC:60fps1.01ms

○非アニメモデル:5000個
  メインPC:59fps12.51ms
  サブPC:36fps18.36ms

○非アニメモデル:10000個
  メインPC:30fps24.84ms
  サブPC:21fps36.76ms

メインPCは殆ど誤差の範囲ですが、サブPCは1.3~1.8倍ぐらい変わりました。メインPCだけ差が出なかったので、テクスチャ数は頂点シェーダのパフォーマンスに影響を与えるのかもしれません。



◎実験その2

UVアニメで使用したモデルに差し替えてみました。

頂点数:16→4
プリミティブ数:10→4
テクスチャ数:3→1
(他の条件は同一)

○非アニメモデル:1000個
  メインPC:60fps0.95ms
  サブPC:54fps1.61ms

○非アニメモデル:5000個
  メインPC:59fps4.12ms
  サブPC:18fps7.76ms

○非アニメモデル:10000個
  メインPC:55fps8.21ms
  サブPC:11fps16.08ms

うみゅ!?

モデルがシンプルになって、メインPCのパフォーマンスは2~3倍向上しました。サブPCもCPU時間は短縮されましたが、fpsはサイアクです。

ということは、モデルとGeForce5200FXの相性が悪いってこと?メインPCは逆に相性良さげだなぁ…。



◎実験その3

モデルがシンプルになったのに、サブPCのfpsが低下したのは何故でしょう?もしかして、テクスチャが大きすぎたのでしょうか?

試しに、テクスチャを8×8の小さな画像に差し替えてみました。

サブPCテクスチャ差し替え

○非アニメモデル:1000個
  メインPC:60fps0.97ms
  サブPC:60fps1.03ms

○非アニメモデル:5000個
  メインPC:60fps4.11ms
  サブPC:25fps7.85ms

○非アニメモデル:10000個
  メインPC:59fps8.08ms
  サブPC:15fps16.24ms

多少マシになりましたが、まだ最初の複雑なモデルの方が速いですね。



◎次回予告

結局、サブPCのfpsの謎は解けませんでした。もはや見当も付かないので、謎は保留にして話を先に進めます。

とりあえず解析元のサンプルは準備出来たので、次回はコンテントパイプラインを細かく解析する予定です。

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
シェーダーインスタンス1000枚
UVアニメサンプル改
動的頂点バッファをやめて、UV座標をシェーダー定数で渡すようにした。回転軸が固定化されたので、板ポリモデルの回転方向は以前と異なる。



◎はじめに

今回は、普通のシェーダーインスタンス版を作成します。

前回方式より速くなるかどうかは、やってみないとわかりません。速くなるといいなぁ。



◎定数レジスタのフォーマット

動的頂点バッファをやめて、UV座標も定数レジスタにセットしてシェーダに渡します。私は以前アニメ番号を渡す案を書きましたが、yohさんの案の方がスマートなので、yohさんの案を採用させて頂きます。

・position(Vector3)

・scale(float)

・rot(float)
 →回転はY軸固定とする

・textureCoordinate(Vector2)
 →左上頂点のオフセットだけ渡して、頂点データ側に
  “オフセットUVに加算する量”を埋め込む



◎回転の頂点変換式

UV座標関連の改修は難無く出来たのですが、回転関連の改修は1度失敗し、こちらで一次変換式を確認して直しました。

// Y軸を中心に回転させる(HLSL)
float3 ps = input.Position * scale;
float4 pos_ws;
pos_ws.x = pos_os.x + (ps.x * cos(instanceParam1.z) - ps.z * sin(instanceParam1.z));
pos_ws.y = pos_os.y + ps.y;
pos_ws.z = pos_os.z + (ps.x * sin(instanceParam1.z) - ps.z * cos(instanceParam1.z));


さほど難しい話でもないのに、この式を導き出すだけで3時間もかかってしまいました(爆)



◎パフォーマンス測定

改修が完了したので、パフォーマンスを見てみましょう。

シェーダーインスタンス1000枚

○両面板ポリアニメ:1000枚
 一度に描画するインスタンス数:100枚
 光源計算:なし
  メインPC:60fps、60fps0.79ms
  サブPC:40fps、34fps1.26ms
 光源計算:あり
  メインPC:60fps、60fps1.06ms
  サブPC:43fps、35fps1.32ms

○両面板ポリアニメ:10000枚
 一度に描画するインスタンス数:100枚
 光源計算:なし
  メインPC:44fps、15fps7.27ms
  サブPC:9fps、3fps9.69ms
 光源計算:あり
  メインPC:43fps、15fps9.61ms
  サブPC:8~9fps、4~5fps9.54ms

前回方式と比較すると、CPU処理時間が1.5~2倍ほど短縮された反面、fpsは若干低下したようですね。できればサブPCのfpsを上げたかったのですが、CPU負荷が軽減された分、GPUに負荷が集中してしまったようですね。


ちなみに、バッファへの動的書き込みをしない場合は、動的頂点バッファと静的頂点バッファのパフォーマンスは同等でした。また、板ポリの回転速度を変えた場合も、(画面更新量は変わるが)パフォーマンスへの影響はありませんでした。



◎画面表示数を制限する

fpsを向上させる条件を探るため、画面に表示する枚数を少なくしてみました。

画面表示数100枚

○両面板ポリアニメ:1000枚
 一度に描画するインスタンス数:100枚
 光源計算:あり
  メインPC:60fps、60fps1.03ms
  サブPC:60fps、60fps1.33ms

○両面板ポリアニメ:10000枚
 一度に描画するインスタンス数:100枚
 光源計算:あり
  メインPC:60fps、60fps7.75ms
  サブPC:60fps、60fps7.69ms

インスタンス数を10000枚、画面表示数を10×10=100枚としたら、ようやくサブPCで60fpsになりました。

インスタンス数がCPU時間、画面描画数がfpsに影響するということですね。これを踏まえてゲームデザインを調整すれば、60fpsも実現可能と思います。



◎サンプルプロジェクト

プログラム整理が完了したので、HPにサンプルプロジェクトをUPしました。

XNA3.1でUVアニメーション(シェーダインスタンス方式)



◎パフォーマンス向上計画

60fpsを達成するための具体的な指標が見えてきたので、板ポリアニメのシェーダーインスタンス化は今回で終了します。

次のテーマは「簡易モデルのパフォーマンス向上」です。

1.Gamefest2008デモプログラムの
  コンテントパイプラインを解析する

2.カスタムモデルクラスを使用して
  簡易モデルを読み込むサンプルを作成する

3.2のサンプルにシェーダーインスタンスを適用し、
  パフォーマンスを確認する


簡易モデルのパフォーマンスが向上したら、本番プロジェクトに実装してパフォーマンスを調整します。


4.カスタムモデルクラス実装

5.簡易モデルシェーダーインスタンス実装

6.板ポリアニメシェーダーインスタンス実装

7.パフォーマンス測定&ゲームデザイン調整


これで理想に近いパフォーマンスが実現できると思います。目標はメインPC:60fps、サブPC:30fps以上です。



◎次回予告

長いことパフォーマンスに苦戦してきましたが、ようやくまともなパフォーマンスが実現できる目途が立ったので嬉しいです(^^)

次回は、Gamefest2008デモプログラムのコンテントパイプラインを解析します。

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
縦横に並べた
バグ状況確認画面
先日のUVアニメサンプルがバグってたようなので、縦横に並べてUV座標の反映状況を確認した。



◎はじめに

前々回で、DrawIndexedPrimitivesのstartIndexの値がどうしても納得できませんでした。私のカンでは、これはかなり重要なポイントという気がしたので、今回は疑問が解消できるまで頂点/インデックスバッファを使用した描画をいろいろ試すつもりでした。

しかし、yohさんから頂いたコメントをきっかけに自問自答を繰り返したら、いろいろなことが分かってきました。startIndexも理解したので、今回はそれらについて説明します。



◎複製する数

先日のプログラムでは、動的頂点バッファにUVデータを格納するために、頂点データとインデックスデータを“全インスタンス数”複製しました。

しかし通常のシェーダインスタンスは“一度に描画するインスタンス数”だけ複製し、描画時はこれを何度も使いまわして、シェーダに渡すパラメータだけを変更します。

私はこの常識が頭から抜け落ちていたので、デモプログラムのstartIndexが0であることが納得できませんでした(今は納得しました)。



◎バグ?

通常は0で良いことは理解しました。しかし逆に、先日のプログラムはstartIndexをずらさないとマズイのでは?

試しに、「一度に描画するインスタンス数=10」として、縦横に並べてみました。

縦横に並べた

0、1、2…と横に並べ、11番目以降はその上に並べました。アニメのタイミングは全てランダムで設定したはずなのに、縦のタイミングがピタリ一致しています。これはつまり、最初の10個の頂点データ(UV座標)だけを使いまわして、11番目以降の頂点データ(UV座標)を無視している、ということです。

明らかに、バグってます。ガーン!



◎バグ修正

11個目以降の頂点データを読み取らせるには、やはりstartIndexをずらさないといけません。しかしstartIndexをずらすと、画像が乱れたりちらついたりします。

startIndexをずらしたら画像が乱れた

下から2行目の描画が抜け落ちた例です。

この現象が発生する原因は、頂点データ内のインスタンス番号を「通し番号」でセットしたのに、シェーダ側は“一度に描画するインスタンス数”を超えた番号をサポートしてないからです(シェーダ定数配列の上限を超えて、未使用メモリにアクセスした状態)。

手っ取り早く直したかったので、「通し番号」をやめて、“一度に描画するインスタンス数”を超える度にインスタンス番号を0から付け直しました。

また、startIndexだけずらすと画面が乱れましたが、minVertexIndexも併せてずらしたら直りました。

this.graphics.GraphicsDevice.DrawIndexedPrimitives(
  PrimitiveType.TriangleList,
  0,
  4 * (objIdx - drawInstanceCount),
  modelMeshPart.NumVertices * drawInstanceCount,
  6 * 2 * (objIdx - drawInstanceCount),
  modelMeshPart.PrimitiveCount * drawInstanceCount);


startIndex修正後

11番目以降のUV情報が正しく反映されて、全画像のアニメタイミングがバラバラになりました。


というわけで、HPにバグFIX版をUPしました。
ちなみに、パフォーマンスは以前と変わりません。
(メインPCとサブPCで確認済)



◎次回予告

今回はバグ報告となってしまいましたが、早めに修正出来て良かったです。バグに気付くきっかけを与えてくれたyohさん、どうもありがとうございました!


できればサブPCのパフォーマンスをもっと上げたいので、次回は普通のシェーダインスタンス版を作成して、パフォーマンスを比較します。

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
江陵陥落
江陵陥落
呉の策略に乗せられて油断した関羽は、本拠地防衛の兵を割いて最前線に投入した。呉の呂蒙は手薄になった関羽の本拠地を急襲、守将の傅士仁・糜芳は抵抗を諦め呉に降伏した。



◎2月の目標達成度

・活動時間月144時間以上

実績:125h(達成率:87%)

 (中間目標:2/15迄に72h以上)

実績:56h(達成率:78%)


・インスタンシング調査/解析/実装前検証(48h)

実績:52hで実装前検証中(達成率:80%)


・インスタンシング実装/計測/調整(40h)

実績:未着手(達成率:0%)


・ブログ更新/HP更新(48h)

実績:47hでブログ更新、HPに「XNA3.1でUVアニメーション( 動的頂点バッファ方式)」など3記事を追加(達成率:100%)


・その他(8h)

実績:1hでCG監修実施(達成率:12%)


・予定外の作業

実績:4hで遅延原因調査 完了
実績:12hでUVアニメ検証 完了



描画方式や測定方式の確立、性能改善等、成果は結構ありましたが、作業時間が目標未達成で進捗が遅いのは良くないですね。



◎作業時間分析

作業時間集計2010年2月(PDF形式)


○良かった点
・予定外の描画方式も確立した
・HP記事を9Hで3件登録した

○悪かった点
・活動時間が少ない(特に前半)
・実装前検証完了に至らなかった


予定外だったので他の作業時間として記録してしまいましたが、前半はUVアニメ動的頂点バッファ方式の確立や性能改善がメインでした。試行錯誤中とはいえ、作業時間少なすぎですね(ーー;

後半もなかなか作業時間が増えないのは、裏の家の解体工事が10日間ほど続き、騒音が酷かったからです。ヘッドホンでBGM全開にしてもガンガン響く。さすがにあれじゃ集中できません(>_<)



◎今後の方向性について

シェーダインスタンスは、自分のモノにできた気がします。(気がするだけかもしれませんがw)

しかし、頂点バッファを使用した描画に関しては、まだ前回の疑問が残っています。かなり重要なポイントと思われるので、いろいろ検証してしっかりマスターしておきたいです。

しかし、進捗を優先するなら、さっさと本番ゲームに実装して先に進むべきでしょう。

また、パフォーマンスを優先するなら、さらに速い方式の確立を目指すべきでしょう。

今後どうすべきか迷いましたが、どうせ本番実装後もパフォーマンスは簡単に上がらないだろうし(苦笑)、また試行錯誤モードに入るのがオチです。

であれば、予め疑問点を潰しておいた方が、効率が良さそうな気がします。というわけで、先に疑問点を潰すことから始めます。



◎パフォーマンス調整基準

現時点で一番悩ましいのは、パフォーマンス調整時の環境基準があいまいな点です。前回方式を例に説明します。

前回方式はメインPC:CPU側、サブPC:GPU側がネックになっていると判断しました。メインPCはイレギュラーなので、サブPCを基準に調整(=GPU負荷を減らす)したい所です。

(地形や簡易モデルもあるので正直キビシイですが)サブPCで60fps16.6msにバランス良く収めることが仮に出来たとしても、他のPC環境ではアルファや光源等の演算速度、バッファ書き込み速度などが異なるので、バランスが崩れる可能性大です。

そう考えると「頑張ってGPU負荷を減らす(≒CPU負荷が上がる)ことにどれだけの意味があるのか?」などと疑念が湧いてきます。

最終的にはどこかで腹をくくるしか無いのでしょうが…固定環境のXbox360が羨ましいw



◎進捗状況チェック

スケジュールは凍結中です。シェーダインスタンス実装後のパフォーマンスを見てから、企画とスケジュールの見直しを行います。

ちなみに、2D画像(顔画像&イベント画像)の制作は1月末に完了しました。鳳雛さん大変お疲れさまでした!!



◎3月の目標

・活動時間月128時間以上
 (中間目標:3/16迄に64h以上)

・バッファ動作検証(16h)

・インスタンシング調査/解析/実装前検証(24h)

・インスタンシング実装/計測/調整(40h)

・ブログ更新/HP更新(48h)

今月は用事があるので活動時間目標を少なめに設定しました。その分集中して成果を上げるよう頑張ります!



◎次回予告

次回はバッファ動作検証です。頂点バッファやインデックスバッファを使用した描画について、幾つかテストします。

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

| BLOG TOP |
DATE: CATEGORY:三国志軍記開発
UVアニメにデバッグコンポーネント組み込み
UVアニメサンプル改
改修により、インスタンス数の上限が無くなった。タイムルーラーを表示すると、見た目とパフォーマンスが変わる??



◎定数レジスタのデータレイアウト

前回は、頂点座標や回転をシェーダーインスタンス化する所までやりました。UV座標は動的頂点バッファで更新していたので、今回はこれをシェーダーインスタンス化します。

まず最初に考えることは、定数レジスタのデータレイアウトです。

現時点では、定数レジスタ2個でfloat8個分のデータ(Position:3、Scale:1、RotateAxis:3、rotation:1)を渡していますが、実はScaleとrotationは固定値なので、シェーダーに固定値を埋め込んで空きを作れば定数レジスタ2個でUV座標の受け渡しが可能です。

ただ、そのようなサンプルは実際のゲームでは使いにくいので、今回は素直に定数レジスタをもう1つ確保します。ちょっと勿体ない気もしますが、サンプルの最適化だけ進めても本番のゲームが遅かったら意味無いですからね。


で、実際にプログラムしようとしたら…先程の自分は大馬鹿者であることに気付きました。1モデルに4頂点あるので、UVデータも4つ必要ですよね。float8個分の定数レジスタが必要ってこと?



◎アニメ番号を渡す?

それならいっそアニメ番号だけシェーダに渡して、シェーダ側でアニメ番号からUV座標を算出させれば、CPU側の負荷が減るし定数レジスタの使用量も少なくて済むから一石二鳥かも?

ただ、今のままだと頂点番号がわからないので、インスタンス番号の代わりに頂点番号を埋め込む必要がありますね。
シェーダ側のプログラムは複雑化しそうです。

それに、シェーダインスタンス化後の負荷は、CPUとGPUどちらが高いのか不明です。ということは、今の動的頂点バッファ方式より遅くなる可能性もあるわけです。こうして考えると、インスタンス化後のパフォーマンスを測定してから決めた方が良さそうです。



◎定数レジスタ256対応

パフォーマンス測定はメインPCとサブPC両方で行いますが、メインPCはイレギュラーなので、サブPCのデータを重視したいですね。サブPCで測定可能とするために、定数レジスタ256に対応しましょう。

そこで問題になるのは「定数レジスタ数を超えた時の複数回描画をどうやるか?」という点です。

私は最初、インスタンス情報格納配列を予め定数レジスタ数で区切っておくことを考えました。これならインスタンス情報をそのままシェーダに渡せるからです。

しかしGamefest2008デモプログラムを見ると、定数レジスタ格納専用配列を別途用意し、描画前にゲームデータを毎回その配列にコピーしてから描画しています。

なぜそんな2度手間のようなことをやるのかな?と思ったのですが、よく考えると、変に区切られた配列なんて、通常のゲーム内では使いにくくてたまらないですね。



◎startIndexの値

改修を終えて、一度に描画するインスタンス数を100、総インスタンス数を1000と設定したら、100個だけ描画されました。

改めてコードを確認すると、DrawIndexedPrimitivesのstartIndexが0なので、最初の100個を10回表示しているような気がします。しかしデモプログラムを確認すると、こちらも同様でした。あれ?startIndexの値をずらすべきじゃないの?

コードを何度か見比べたら、インスタンス情報を定数レジスタに格納する時の指定を間違えたことに気付いたので修正しました。

シェーダーインスタンス1000枚02   シェーダーインスタンス10000枚

左側は1000枚、右側は10000枚の画像です。
無事に改修できたようですね。

当初の目的は果たしたものの、私の疑問は謎のままです。誰か教えて~!



◎アルファ処理

今回の改修により、インスタンス数に上限が無くなりました。また、サブPCでも測定可能となりました。早速、デバッグコンポーネントを組み込んで測定しましょう。

UVアニメにデバッグコンポーネント組み込み

デバッグコンポーネントを組み込んでfpsやタイムルーラーを表示したら、こんな画面になりました。

何かの影響で画面が崩れたかと思いましたが、よく見ると槍兵が普通にアニメしてます。槍兵背景のアルファ値は最初からセット済だったのですが、それがデバッグコンポーネントの影響で有効化されて、背景が無くなったようですね。

見た目が変わるだけなら問題無いのですが、10000枚でfpsやタイムルーラーを表示すると、59→17fpsまで下がりました。アルファ処理ってそこまで重いのか?!

これでは純粋なシェーダーインスタンスのパフォーマンスがわかりません。しかしゲームではアルファ処理するので、この方が参考になるかもしれません。結局、両方のパターンを測定することにしました。



◎サンプルプロジェクト

プログラム整理が完了したので、HPにサンプルプロジェクトをUPしました。

XNA3.1でUVアニメーション
(シェーダインスタンス&動的頂点バッファ方式)


パフォーマンス測定データやデータ分析等もありますので、興味がある方はご覧ください。



◎次回予告

シェーダーモデル2.0で汎用的に使えるようになり、インスタンス数の上限も無くなりました。10000枚59fpsは我ながら凄いと思います。

しかし、サブPCでは予想以上にパフォーマンスが悪く、メインPCでもアルファ処理を加えるとパフォーマンスが激減しました。さらなる改良が必要かもしれません。


ともあれ3月になったので、次回は「2月の総括と3月の目標」です。

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

| BLOG TOP |

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