|
|
結局のところ、Retained Mode にはなにができるのか
|
あー。
脳味噌があずさ命状態にシフトしてから早くも2週間が経ってしまいました。思えばこの二週間は毎日が刺激的だったなぁ。刑事マンを買うわ、SADISTV!作るわ、Jedi Knightにハマリまくるわで(といってもやっと13面まできただけだけど)、すでに脳内はプログラムー?ナニソレー状態ですが、そんなボケボケな俺の頭蓋を著しく刺激しやがったのは、他でもない4月から社会人となる生徒達の最後の雄叫び・・・・というかテストの答案用紙でした。
あぁ。そして答案の採点をして100人の生徒の成績をつけるまであと3日。他人の成績なんかつけてる場合なのか<俺。こっちも期末テストが控えてるというのに(そして勉強はしてない)。
リハビリと思って、ゲームのプロトタイプの仕様設計をアレコレと考えていたのですが、やはり、俺の考えは甘かったと痛感しました。
もともと、RMという汎用ライブラリがあるので、それを使えばいいや(&面倒な最適化は神聖にして犯さざるべきMicrosfotがやってくれる)とかるーく考えていたのですが、RMでできることというのは、いうまでもなく、極めて限定的なので、それでも最初から、AnimationSetのようなものは自作しないといけないことくらいは解っていたつもりでした(そして自作したのが昨年11月のPrejudiceね。もうアーカイブないけど)。
ああ、しかしあれですな。たとえばProgressiveMeshひとつとっても、Animationで使えなかったり、妙な仕様が目につきます。
なにより疑問なのは、DirectXがCOMをまるで便利な新兵器のようにバカスカ作りはじめることです。前々からMeshBuilderやAnimationSetのローディングにどうしてこんな時間が必要なんだと思っていたのですが、驚くべきことに、なんとローディング時間の大半はファイルアクセスではなく、COMを作ることに費やされているようです(もしくはフレーム毎のQuaternion補完に使われているのか・・・にしても遅すぎる)。
遊びでRokem3Dみたいなゲーム(?)を作るならともかく、本気で遊びたいおれには物足りない仕様であることは確かです。
また、当然モノコック・モデルのワンスキン・アニメーションもつくれません(Interpolateを使えって?ご冗談でしょうファインマンさん)
で、結局RMではなにができるんでしょうか。
とあいなったときに、RMでできるのはメッシュの描画だけだという当然の結論を引き出すことができます。
他にも、フレームを管理してくれたりします。これはけっこう重要で、件のランドスケープにしてもフレーム管理はすべてRMに一任していました。
しかしながら、大変残念なことに、いまのところRMが用いているような手段・・・つまり内部的に計算機ハードウェアをエミュレートして将来ジオメトリと光源処理がハードウェア化されるときに備える・・・というものでは、そうでない場合に比べて極めてお粗末な結果になってしまうことがよく知られています。
そして、これも僕のあさはかなところですが、僕はてっきり、そういうハードウェアはすぐに市場に並ぶものと思っていました。ところが一向にそんなニュースは聞こえてきません。
さて、RMの不便な点を列挙したら切りがありません。というか、それはDirect3D全般に言えることと思います。
やれCOMがどーの、インターフェースがどーのと、ソースコードの可読性を著しくそこねてしまうCOMを、うまくC++言語コードで書くのは至難のわざです。
それに、致命的なことには、Direct3Dがそれ単体で用意している浮動小数点演算法を用いたやりかたでは、MMXテクノロジを使った最適化に不向きであることもあります。
そんなわけで、今日は少し、メッシュなどの描画をRMを使わずに行うクラスライブラリを作るとして、そのためにはどのような工夫があるか考えてみようと思います。
|
|
さて、しかしながらいざRMの致命的な弱点を挙げろといわれると、ややしどろもどろになってしまいます。
せいぜい思い付きで挙げられるのは、「メッシュしか描画できない」「自由度が低い」という程度でしょう。
しかしながら、RMの代替となるライブラリを、わざわざ自分で作ろうというのであれば、基本機能である「メッシュ描画」においても、大きなアドバンテージが必要です。
そしてRMはよく工夫されているほうで、汎用として考えたらおそよ考えうる限りの工夫がなされています。たとえば、ポリゴンの描画ひとつとっても、内部でマテリアル単位でまとめて描画したりという具合にです。
また、高度な計算を必要としないならば、SetQualityメソッドで質を落としてやることで容易に計算を省略できますし、動的に形の変化するものとしてのMeshBuilderと、静的な実行バッファを持つMeshとで明確に線引きがなされ、隙がありません。
こうしてみると一見RMは非の打ちどころのないライブラリに見えます。そして事実そうでしょう。少なくとも、マイクロソフトが想定する使い方においては、満点をとれる優等生です。
しかし、優等生よりも、少々出来の悪いゴロツキのほうが世の中の役に立つこともあります。
いまはまさにこの場合です。
RMの持つ根本的な制約は、必ず内部でIMを、しかも正しい方法で使わなくてはならないことです。
ここで言う「ただしい方法」というのは、つまりIMが用意しているレンダリング・パイプラインを頭からお尻まできちんと使っているということです。
この条件を無視すると、将来、ジオメトリ処理までハードウェアで実装できるようになった場合、そのこと自体が無意味になってしまいます。
しかし多くのゲームベンダは暗黙のうちにルール違反を行います。それはジオメトリ処理ハードウェアを最初から想定しないことです。
そのため、レンダリング・パイプラインの途中に割り込む形でデータを送ることになります。
では、ジオメトリ処理を自前でやると、どのようなメリットがあるのでしょうか。
まず、考えうる限りの手抜きができますし、MMX命令を使用して最高で通常の4倍の速度で計算させることができます(Direct3DのMMX-HELはジオメトリ演算にまでMMX命令を適用していないと信じています。なぜなら今のMMX命令は浮動小数点演算ができないからです)。
要するに、優等生に成績(速度)で勝つためには、ダメな落ちこぼれ学生は少々汚い(しかし利口な)手を使うしかないということです。
考えうる限りの手抜きとはどういうことでしょうか?
簡単な例を出しましょうか。
一般に頂点座標の変換は、まずワールド座標系変換、次に視野座標系変換、最後にスクリーン座標系変換をかけます。
スクリーン座標系変換はX軸とY軸にしかかかりませんので、そのぶん処理は簡単なものといえます。
たとえば、図のようにジクザグ状に頂点をならべて板を表現するとします。
このとき、たとえば固有座標系において、1,3,5のベクトルが同一(平行で長さも同じ)だったとしましょう。
このとき、頂点すべてにたいして、ワールド座標系変換と視野座標系変換を行う必要があるでしょうか?
通常はあらかじめ行列レベルで二つを掛け合わせておくので、それぞれの頂点に関する乗算のコストは4×4=16です。実際には同じ回数の積和演算ということになります。
そしてRMの内部ルーチンはなんの疑問も抱かずに6回の行列計算を行うでしょう。つまり16x6=96回の積和演算を、です。
しかし少し考えれば、そのような計算は3回で済むことがわかります。まず最初の頂点座標を変換し、次に1番と2番のベクトルについて行えば、2番目の頂点の視野座標は最初の頂点座標・・・たとえばこれをPと呼びましょう・・・に1番のベクトルを足したものであり、その次の頂点はさらに2番のベクトルを足したもの、その次の頂点はさらに1番のベクトルを足したもの・・・となり、結局のところ計算コストのかかる乗算については16x3=48回に減らすことができます。
実のところ、Pentiumプロセッサでは、浮動小数点乗算はわずか2クロックで終了するので以前ほど乗算についてナーバスにならなくても良いという背景があります。
しかしながら、MMX命令は最大で同時に4つの整数積和演算を行うことしかできないので、乗算回数が減れば、それだけメリットも大きくなります。
ただし、今の話ですとスクリーン座標変換に関する部分が巧妙に削除されています。
実は、普通に計算するとスクリーン座標変換もいっしょにひとつの行列にしてしまえるので、スクリーン座標というのは、そもそも求める必要がありません。反対に、今言ったような姑息な方法を使うと、それぞれの頂点に対してスクリーン座標変換を行わなくてはならなくなります。
ここはトレードオフとなりそうな要因に見えますが、実際にはそうではありません。
ここでRMのメッシュにはない限定条件を追加しましょう。描画となる対象は常にConvexなメッシュのみとします。そしてもうひとつ、このシステムでは重なり合う図形があった場合、Zソート法に近い手段で解決するものとします。
この二つの重要な前提を置くと、スクリーン座標系変換は正確なZバッファリングのための同次座標計算から開放されます。
Direct3DIMを駆動する場合、Zバッファリングを正確にするためには、画面上のX座標とY座標だけではだめで、Z座標とW座標まで求めなくてはいけません。すると求める要素は4つとなってしまい、計算は複雑になります(それでも工夫次第ですが)。しかし、同時に描画するメッシュが全てConvexなものであれば、Culling処理をしておくことで、描画するポリゴンの正確なZバッファ値は不要になります。
この二つの条件によって、求める必要があるのはX座標とY座標のみとなり、これならば単純な乗算で求めることができます。つまり乗算コストは2回。これならば、頂点数が増えれば増えるほどRMより有利になっていきます。
ただし、注意が必要なのはこの話は作為的であることです。
必ずしもすべてのメッシュに平行なベクトルが多数あるとは考えられませんし、事実そのような場合には今言った方法ではかえって遅くなってしまいます。
しかしながら、ゲームに登場するメッシュのほとんどはシンメトリックなものですから、通常最低二つは平行で長さも同一のベクトルがあるハズです。
このへんのところに着目すると、より優れた高速化が可能かもしれません。
中途半端ですが、夕飯のしたくをしないといけないので、今日はここまで。また機会があったらその他の方法についても考えてみましょう。
|
|
|