プログラミング一覧

発想転換、再実装

新・ConvertToIndexedBlendedMesh相当品

アルゴリズムを見直して再作成しました。
前回は4つのマトリックスパレットで分割しようとした際に、
デバッグビルドだとうちの環境で5分くらいかかって、
かつ、描画バッチ数も200弱という悲惨なことになりましたが、
新しい方はそんな状況にはならず動くようになりました。

実行速度はデバッグで計っています。

パレット数 頂点数 描画バッチ数 変換時間
4 4881 24個 4.2秒
13 4578 4個 3.3秒
26 4474 2個 2.9秒

リリースビルドだと、2桁くらいは高速に動いているので
ようやく実用的になったと思います(13個のパレットのRelease時は15msで変換を完了)。
従来方式が複雑かつ長時間かかるという点で使い物にならなかっただけという話ですが。

プログラムコードは400行ほどに(従来コードは700行)。
STLのデータ構造とアルゴリズムを使いまくってます。
そのせいでデバッグでこの速度を出すのが難しかったです。
ただ気を配って書けばSTL使った場合デバッグで重くて使い物にならない!という状況は避けられると感じました。

ポイント

考えをあらためたポイントは、以下の点です

  • 頂点を共有することにがんばらない
  • 変換アルゴリズムはシンプルに

前回の足りなくなったら頂点を複製という点から、頂点は最初から複製前提で考える
という考えに切り替えたのが大きいです。


ConvertToIndexedBlendedMesh調査中

先日の状態よりはうまくいくようになったのですが、
使用できるボーンのマトリックスパレット数が少ないときの挙動でどうにも限界が。

問題点

パレット内でのインデックス割り当てのコンフリクトを解消できない。

頂点において、以前使用したときのマトリックスパレットのインデックスと同じにしなくてはならないのですが、
時たまそのインデックスがコンフリクトして3角形を描けない状況へ陥ってしまいます。

例:
頂点1は、インデックス1,3にボーンID 4,5を割り当てて使用
頂点2は、インデックス0,1にボーンID 6,5を割り当てて使用
このときに、インデックス1にボーンID4,5がコンフリクト

本家ConvertToIndexedBlendedMesh関数

どうがんばってみても自力実装での
ConvertToIndexedBlendedMesh関数がうまくできなかったので、
本家のほうを調べてみました。

使用したプログラムはSDK付属のSkinnedMeshです。データはtiny.xを使用。

パレット数 面数 頂点数
26 6841 4474
13 6841 4578
8 6841 4646
4 6841 4903

ちなみにConvertToIndexedBlendedMesh関数を呼び出す前はこんな状態でした。

面数 頂点数
6841 4432

まとめ

どうやら入力時の頂点の数と、変換後の頂点の数は一致しなくてもよいらしい。
当たり前といえばあたりまえだけど、面の数が変わらないという点で、
インデックスデータの長さも変換前後で変化しないということみたい。

自前変換の手順

自前でやっていた内容をメモしておきます。意外と面倒だったりするので…。

  1. そのマテリアルで描けるデータ列(面データ)の頂点を随時処理
  2. このデータ列全て処理が完了したらループを抜けて、次のマテリアルへ。
  3. ある程度パレットが埋まるまで面単位で処理をしてみる。-(A)
    1. 以前に使用されていた場合の、パレット内インデックスを今回に予約。
    2. (このとき以前未使用なら、自由に再割り当てしてOKということで今スキップ)
    3. 今回のパレットがある程度うまったら、再度(A)ループをやり直す
    4. このとき前回はスキップした部分を埋めていく
    5. 未使用のデータについて現在のパレット内での空きを利用して再割り当てを行っていく

という状態で割り当てていったのですが。
自由に割り当てという部分でもうちょっと調停が必要だったのかもしれません。

まとめ

自分のやっていた方法では、変換前後で頂点の数は変化しません。
一方ConvertToIndexedBlendedMesh関数では、前後で数が変化します。

おそらくコンフリクトしたときに、頂点を再生成し現在の状況にあうように変換しているのではないかと考えられます。
この機構をいれれば、変換はうまくいくのではないかと思います。

もうちょっとがんばらないとダメそうですね。


エディットコンティニューの制限

エディットコンティニュー

VisualStudio(VC)のなかなかな強力な機能であるエディットコンティニューについて。

簡単に説明すると、デバッグ実行しながらプログラムを修正できて、
修正コードを適用させた状態で実行を継続できる機能です。

その制限というか条件のひとつに、こんなことをやるとダメらしい
というのを発見したので、メモしておきます。

ローカルスコープにある変数の属性を変えるようなことは不可能らしい。

今までは単なる変数だったのを、同名で配列にしてみたりとかは不可能。
そういう場合には別の変数名を使えと言うことなんでしょう。


ConvertToIndexedBlendedMesh相当品

自力で ID3DXSkinInfo::ConvertToIndexedBlendedMesh相当品を作ってみてます。…が、結構難しい…。

設定したマトリックスパレット数で、モデル全体を描画できるように
ボーンコンビネーションテーブル相当品準備するところまでは出来たのだが、
まだまだバグが入ってる。
とりわけ、複数のサブセット描画で共有される境界の頂点がおかしい。

D3DXの機能では頂点キャッシュへの乗り具合や、
ボーン影響度の設定とかも出来てかなり高機能です。
これと同様の機能を自力実装はかなり骨が折れそうです。そこまではきっとやらないです。

また、現状のものでもデバッグ実行状態でtiny.xの変換で20秒くらいかかってしまうので、
全くダメダメですね。


VS2010のSTL

体感的にですが、VisualStudio2008と2010とを比較して、
STL(std::vector)が重くなった気配があります。

完全に同じコードではないので感覚的なものなのですが。

ただデバッグ用においてのみで、リリースビルド時には高速に動くため
巨大なデータにおいてはリリースで実行しようということになりそうです。

でも、10000程度の要素での出し入れが重いってのもなぁ…
(reserveを適切に使っているため、要素移動は起こらないはずなのに。)

そのうち同一コードでDebug/Release比較をやってみるとします。


VisualStudio2010でのWinSxS

VisualStudio2010では、またサイドバイサイド(Side by Side:SxS)の状況が変わっていた。どうやら、WinSxSを使っていないようです!
C:WindowsWinSxs の中を見ても vc100用のランタイムは入っていませんでしたし、
VS2010インストールフォルダ内のdll群をチェックしてみても、
埋め込みマニフェストすら入っていませんでした。

さらには、2010で作成されるexeの中の埋め込みマニフェストをみてみても
ランタイムのバージョンに関するものは記載がありませんでした。

ちなみに、VS2005/2008では、世間で有名になっているとおり
サイドバイサイドが使用されており、ランタイムの単純コピーでは動作しないようになっていました(一般的には)。
ユーザーはマイクロソフトより再配布ランタイムのパッケージをダウンロードして、インストールする必要がありました。

今回の2010での変更は、従来のdll単純コピーで済ますことが出来るという点では手軽になったと思います。
しかし、懸念されるのは一昔前のDLL-HELLです。また同じ状況が起こる可能性があります。

所感

WinSxSがそうとう厄介問題だったのは知っていましたが、
新バージョンで取りやめるほどとは思いませんでした。

以前のランタイムもマイナーバージョン間では単なるリダイレクトになっていたし、
全て新バージョンのランタイムで置き換えてきた現実からこのような方法に変更になったのかもしれません。
「今までも全て新しいものに置き換えてきた。今後も大丈夫だろう。
VCではWinSxSを廃止して、dll直配置を認めた方がサポートも楽になる。」
というような思惑だったりするのかも、と思ってます。

昔のようなバージョン依存は現在となっては低そうということでしょうか。


インデックスバッファ

DirectX9では16bitインデックスバッファが主流だったように思います。しかし、DirectX10以降はインデックスバッファも32bitインデックスとなってしまったようです。
生成時に16bitであると設定するものが見あたらない…。

DirectX9から10以降まで1つのAPIセットで対応するライブラリ作ってますが、
このような違いってどうしたものやら。

  1. フォーマット指定を追加。使えない環境(DX10世代)ならエラーにする
  2. あくまでフォーマット指定はヒント。
    1. 書き込み時とかには生成された結果フラグをみてUSHORT, UINTでインデックスを書き込み

どちらの方法にしてもキレイにならない気がします…。

  • 過去日記に同じようにこの情報を書いていました。10環境では16bitインデックスがなくなった、と。

VS2010対応 – ゲームプログラマになる前に~のやつ –

「ゲームプログラマになる前に覚えておきたい技術」に付属しているソース類で、
VisualC++2010用に変換したプロジェクトおよびソースコードを用意してみました。手元の環境でデバッグ/リリース構成のビルドを確認しています。2011/02 新バージョンを公開しました。
こちらからどうぞ

プロジェクト変換について

  • ライブラリの全体最適化を無効化してあります。
  • ワーニングレベルを3に落としたものがいくつかあります。
  • 算術関連(sin,cos,tan,など)を名前空間付きに変更しました。

サンプルコードによっては、全体最適化適用による不具合のため、
exe用のプロジェクトでも無効化してあるものがあります。

なお、変換に用いたのはVisulStudio2010Professionalです。
ですが、expressエディションでも開くことは可能かと思われます。

注意事項

公開しているアーカイブの再配布を禁止します。
また、公開したアーカイブを使用したことによる損害については、
筆者および公開した私自身がその責務を負わないこととします。
利用する際には、自己責任でご利用下さい。

ダウンロード、および、使用方法

CDROM内にある、VisualStudio2008用のソースコードを展開した後、
srcフォルダでダウンロードしたファイルを上書きで展開してください。

2011/02 新バージョンを公開しました。
こちらからどうぞ

注意事項に同意のうえ、ダウンロードする

このファイル中にソースコードが一部含まれますが、
これについては著者の許可をもらって公開しております。

動作確認環境

  • Windows7 Ultimate x64環境
  • VisualStudio2010Professional
  • PentiumDualCore E6500
  • Geforce9800GT

この環境で、含まれるサンプルソースコードの実行等を確認しております。
少なくともこのプロジェクト類を用いて 64bit環境での動作を確認できております。


ミップマップについて

どうやらミップマップが使われるためには、
射影行列(透視変換行列)適用してからじゃないとダメな模様。行列適用しないで、通常の射影空間(0-1)に収まるように頂点を準備して、
テクスチャ貼り付けてみたけど、これではミップマップがポリゴンの面積に応じて使われることがなかった。

しかしその準備した頂点で作られるポリゴンの面積によっては、
ミップマップが使われたりしたが、頂点単位?でしか判定していないのか?

一方、透視変換行列適用してみると、
ピクセルごとにどのミップマップレベルを使用するかが判定されており、
画面に描画してみるとその使用状況がわかった。

根本的に何が原因だったのかはわからんなぁ…。


アルファブレンディングについて【教えてほしい】

アルファブレンドで下記の合成はわかった

  • 通常合成(1*srcColor + 0*destColor)
  • 加算合成(1*srcColor + 1*destColor)
  • 半加算合成(srcA * srcColor + 1*destColor)
  • 半透明合成(srcA * srcColor + (1-srcA) * destColor)

しかしこれらがアルファ値だけ別の計算が出来るという仕組みが用意されていたりする。
これの有効な利用が思いつかない。

OpenGLやDirectXではアルファブレンドセパレートとして機能があったりするが。

D3DRS_SEPARATEALPHABLENDENABLE を有効化して
なにかやっているサンプルもまた見つからなかったし。
有効に使えている例、使える例を知っている人は是非教えてください。