「 2010年06月 」一覧

シェーダー定数(GLSL)

ボーンの行列60個

どこかのブログでボーン用マトリックスパレットが60個くらいまでしか使えなかったと見たので、その理由を書いてみます。
実はその制限は単純なもので、ShaderModel3系までだと、シェーダー定数は256個のレジスタが使えることになっています。
マトリックスだとfloatの16個分なので、float4に換算すると4つ使うことになります。

そして、Matrixが60個の場合 240個の定数レジスタを使ってしまうことになります。
頂点変換用のViewやProjectionのマトリックスも使うことを考えると、
この時点で250個くらいは使っていることになるので、約60個までボーン使えたという結果に合致していると思います。

ずいぶん前の記事だったようで、もうすでに解決しているかもしれません。

GLSLとHLSLとの違いで嵌った

DirectX(HLSL)の行列型は実はfloat4の設定APIを呼んでも設定が可能だったりします。
しかしながらOpenGL(GLSL)の行列型はそのような設定を許さないようです。それぞれで共通のシェーダーを作る場合には、この部分に注意が必要そうです。


GLSLスキニング

GLSLでスキニングシェーダー書いてみた

わりとGLSLでスキニングシェーダーをやったという記事を見かけないので
ここで書いてみます。

大変だった点はGLSLでは、Cのようなキャストを受け付けない点。
そのためブレンドインデックスをどうやってint型にするかで悩みました。
実際には、下記のソースコードに示すように int( )で囲ってやる程度で済む話なのですが、
知らないと結構悩みます。

備考

マトリックスを使わずにfloat4 (すなわちvec4)でやっているのは
単に自分のスタイルなだけです。
先輩のシェーダースタイルに影響された可能性が大です。
matrix型のほうが変換がmulで書ける分だけソースコードはすっきりします。
float4の4つという表現だと、シェーダー定数設定をするコードがすっきりします。
またMatrixをfloat4x3型として使っているような場合で効率よくシェーダー定数を使うことが出来ます。

他にはライティングが適当です。
平行光源と若干のアンビエント成分いれたつもりのコードです。

ソースコード(VertexProgram)

1頂点あたり4ボーンのスキニングシェーダーです。
1回のマトリックスパレットは36個です。

36個にした理由は、tiny.xがこの個数で描けるからです。


モデル読み込み

自前で読み込んでみる

DirectXの .xファイルを自前で読み込んで表示させてみました。
以前試行錯誤しながらがんばっていたConvertToIndexedBlendedMeshのニセモノを使って表示させることが出来るようになりました。

自分で変換するため、OpenGLの描画においても使用可能です。
以下にその例を表示してみます。

画像 補足
model_draw.png DirectでもOpenGLでもスキニングメッシュを読み込んで表示できてます。
さすがにシェーダーは別々に作成の必要がありますが…
モデルはキツネもどきさん作です。ありがとうございました。

OpenGLでも .x を使えるようになったため、プログラムが組みやすくなるかなと期待しています。

色々と作ったらこのWebに公開してみたいなと考えています。


Neo9Essentials

DVDRWドライブ購入

数年使っていたプレクスターのドライブが故障したので、
新しいドライブを購入しました。
DVDRWの機能だけでよかったので、バルクで。

最近バルクのDVDRWドライブって安いですね。
3000くらいで買えてしまうことに驚きです。

でも帰ってつないでみるとやっぱり安いやつは安いなりな感じでした。
トレイの開閉時の動作が若干気になります。
また表面の質感が安いプラスチックで…微妙。
単に使う分には問題ないんですけどね

付属ソフト

Nero9 Essentialsがついてました。
付いてないやつのほうが100円安かったんですが、せっかくなので。
Windows7対応っていってるので保険のために購入
(うちの環境ではCDBurnXPがフル動作しないため)

インストール後は手持ちのBDドライブも正常に認識したので
買って良かったと思います。2層書き込みの認識もしているようだし。
CDBurnXPではBDの1層でしか認識してくれなかったんですよね。

Nero9インストールの注意点

やけにディスク読み込みを繰り返すので不安になりました。
ドライブを変更しても同じなので、ディスクそのものの問題のようです。
ピックアップ部を痛めそうな感じだったので、
ディスクイメージisoを作成してそちらからインストールするようにしました。
これで問題なく動いてます。

このisoをマウントしてインストールしたのですが、問題の読み込み部分では
30秒は待たされた感じだったので、物理ドライブでやると
どれだけ待たされるのか不安を覚えました。

購入ドライブ

TWOTOPで AD7240S というNECドライブです


ESXi仮想マシンの自動起動

同僚から相談を受けたので、書いてみました。
同じように悩んでいる人の役に立てたら幸いです。

VMware ESXi 4での仮想マシンの自動起動設定

vSphere Clientの設定で自動起動・終了を設定できます。
vSphere Clientを起動して、構成タブを開きます。
開いた中に、ソフトウェアカテゴリで、仮想マシン起動/シャットダウンという項目があります。

ここで起動設定をするのですが、画面上の仮想マシンリストの部分では出来ず、
右上のプロパティのリンクをクリックして設定ダイアログを出します。
このあたりがちょっとわかりにくいかもしれませんね。

 


C++のインナークラス

“○×(まるぺけ)つくろーどっとコム”さんのところで、
C++でのインナークラス使った例が出ていました。

数時間前の自分の知識では、この記事まずいだろ…と思っていたので
あやうく掲示板に書き込みをしてしまうところでした。

問題点(懸念点)

C++の場合、インナークラスであっても所属する親のクラスに
自由にアクセスは出来ない。
Javaのようなインナークラスの扱いは出来ず、同様に処理したければ所属する親に対して、
friend class MyInner;
などとフレンド宣言が必要である。

この動作は gccにて確認出来た。
しかしながらマイクロソフト製のコンパイラ(VisualStudio,VisualC++)では
フレンド宣言がなくとも自由にアクセスが可能。
よって、結果はコンパイラに依存していることがわかる。

じゃ、どちらがC++規約として正しいのかと人に聞いたことがある。
そのときには、厳密に正しいのはgccで、VisualStudio側のほうがチェックが甘いという結論に達した。

調査してみた

掲示板に書く前にはやっぱり調査いるよね!ということで調べてみた。

結論は、自分の知識が古かったようです。
gcc(g++ 4.1.2)でもインナークラスは所属クラスに対してアクセスを自由に出来ました。

よって、このバージョンのgcc以降であればうまく動作するようです。
そういえば以前エラーを出していたのは gcc3系だった気がします。

このあたりの調査を進めていくと、どうやらいつの年度のC++基準なのかが関係してくるようです。

  • 1998年基準準拠では、インナークラスは親のクラスに対して自由なアクセス権を持たない
  • 2003年基準準拠では、インナークラスは親のクラスに対して自由なアクセス権を持つ

結論

よって、これからの環境であれば、インナークラスは特に問題にならないようです。
ただ移植性を考えると不味いのでしょうが、それは所属親にfriend 宣言加えることで何とかなるでしょう。

とりあえず、MS依存だ!という結論に達さなかったため、
自分のなかの知識を更新しておかないといけないですね。

最後に、この調査の機会を与えてくれた、IKDさんに感謝です。


発想転換、再実装

新・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秒くらいかかってしまうので、
全くダメダメですね。