トップページ > 記事閲覧
アニメーションの状態を行列で表す
名前:教えてくん 日時: 2015/10/22 16:25

プログラマブルシェーダーを使ってモーションブラーを作成したいのですが、スキニングメッシュのローカル→ワールド行列をどのように扱えばいいのかが分かりません 頂点シェーダーで過去の射影座標を求め、その値をもとにブラーを掛けようと考えているのですが、 そのためにはcpp側でモデルの座標変換行列を保持しておく必要があり、そこでつまづいております 管理人さんのサンプルプログラムのソースを読むと長さが162のcfLocalWorldMatrix:register(c94)という定数に渡されていますが、 ライブラリがここに渡している行列の内容を取得したり自前に求めたりする方法はありますか?
メンテ

Page: 1 |

Re: アニメーションの状態を行列で表す ( No.1 )
名前:管理人 日時:2015/10/26 00:12

> ライブラリがここに渡している行列の内容を取得したり自前に求めたりする方法はありますか? どちらの方法もなかったので、セットしている行列を取得する関数を追加しました よろしけれればこちらをダウンロードしてください https://dxlib.xsrv.jp/temp/DxLibVCTest.exe // VisualC++ 用 https://dxlib.xsrv.jp/temp/DxLibBCCTest.exe // BorlandC++ 用 https://dxlib.xsrv.jp/temp/DxLibGCC_DevCppTest.exe // Dev-C++ 用 https://dxlib.xsrv.jp/temp/DxLibGCC_MinGWTest.exe // MinGW 用 https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // .NET用 https://dxlib.xsrv.jp/temp/DxLibMakeTest.exe // ソース (中身を既存のライブラリのファイルに上書きして、BCCをお使いの 場合は『再構築』を、VCをお使いの場合は『リビルド』を、 Dev-C++をお使いの方は「Rebuild All(Ctrl+F11)」をして下さい) 以下の関数を追加しました // 指定のトライアングルリストが使用する座標変換行列の数を取得する int MV1GetTriangleListLocalWorldMatrixNum( int MHandle, int TListIndex ) ; // 指定のトライアングルリストが使用する座標変換行列( ローカル→ワールド )を取得する MATRIX MV1GetTriangleListLocalWorldMatrix( int MHandle, int TListIndex, int LWMatrixIndex ) ; まずDXライブラリはモデルを「トライアングルリスト」という単位で描画しているのですが、 各「トライアングルリスト」で cfLocalWorldMatrix:register(c94) に設定している行列が異なります なので、行列を保存する場合は全ての「トライアングルリスト」で使用されている行列を保存しておく必要がありますので、 追加した関数 MV1GetTriangleListLocalWorldMatrixNum で使用されている行列の数を取得して、 MV1GetTriangleListLocalWorldMatrix で使用されている行列を取得します モデルで使用されている「トライアングルリスト」の数は MV1GetTriangleListNum で取得することができますので、 行列を保存する際はこんな感じになると思います int i ; int j ; int TriangleListNum = MV1GetTriangleListNum( ModelHandle ) ; for( i = 0 ; i < TriangleListNum ; i ++ ) { int MatrixNum = MV1GetTriangleListLocalWorldMatrixNum( ModelHandle, i ) ; for( j = 0 ; j < MatrixNum ; j ++ ) { MATRIX LWMatrix = MV1GetTriangleListLocalWorldMatrix( ModelHandle, i, j ) ; LWMatrix をどこかに保存 } } そして「トライアングルリスト」毎に行列が変化するので、描画も MV1DrawTriangleList を使用して 「トライアングルリスト」単位で行う必要があります int i ; int TriangleListNum = MV1GetTriangleListNum( ModelHandle ) ; for( i = 0 ; i < TriangleListNum ; i ++ ) { ここで座標変換行列を設定 MV1DrawTriangleList( ModelHandle, i ) ; } 保存しておいた行列をシェーダー定数に設定するタイミングは各 MV1DrawTriangleList の前になります 尚、頂点シェーダーで使用される行列は行と列が逆転していて、且つ4行目が無いという形態になっていますので、 例えば MV1GetTriangleListLocalWorldMatrix で取得できる行列をそのまま使用する場合でも以下のように する必要があります int i ; int j ; int TriangleListNum = MV1GetTriangleListNum( ModelHandle ) ; for( i = 0 ; i < TriangleListNum ; i ++ ) { FLOAT4 MatrixTable[ 54 ][ 3 ] ; int MatrixNum = MV1GetTriangleListLocalWorldMatrixNum( ModelHandle, i ) ; for( j = 0 ; j < MatrixNum ; j ++ ) { MATRIX LWMatrix = MV1GetTriangleListLocalWorldMatrix( ModelHandle, i, j ) ; // 取得した行列の行と列を逆転、且つ4行目無し MatrixTable[ j ][ 0 ].x = LWMatrix.m[ 0 ][ 0 ] ; MatrixTable[ j ][ 0 ].y = LWMatrix.m[ 1 ][ 0 ] ; MatrixTable[ j ][ 0 ].z = LWMatrix.m[ 2 ][ 0 ] ; MatrixTable[ j ][ 0 ].w = LWMatrix.m[ 3 ][ 0 ] ; MatrixTable[ j ][ 1 ].x = LWMatrix.m[ 0 ][ 1 ] ; MatrixTable[ j ][ 1 ].y = LWMatrix.m[ 1 ][ 1 ] ; MatrixTable[ j ][ 1 ].z = LWMatrix.m[ 2 ][ 1 ] ; MatrixTable[ j ][ 1 ].w = LWMatrix.m[ 3 ][ 1 ] ; MatrixTable[ j ][ 2 ].x = LWMatrix.m[ 0 ][ 2 ] ; MatrixTable[ j ][ 2 ].y = LWMatrix.m[ 1 ][ 2 ] ; MatrixTable[ j ][ 2 ].z = LWMatrix.m[ 2 ][ 2 ] ; MatrixTable[ j ][ 2 ].w = LWMatrix.m[ 3 ][ 2 ] ; } SetVSConstFArray( 94, MatrixTable, MatrixNum * 3 ) ; MV1DrawTriangleList( ModelHandle, i ) ; } // 設定した行列をリセット ResetVSConstF( 94, 54 * 3 ) ; ところで「トライアングルリスト」が使用する行列の最大数は 54個( 定数としては 54 × 3 で 162個 )で、 仮に最大数の行列が使用されていた場合、頂点シェーダーには256個しか定数が無いので 「1フレーム前の行列も定数にセットする」といったことができないのですが、それは問題ないでしょうか?
メンテ
Re: アニメーションの状態を行列で表す ( No.2 )
名前:教えてくん 日時:2015/10/26 01:05

追加関数まで用意してくださったのですか! ありがとうございます トライアングルリストごとに処理しているだろうというのは大体予想ができていましたが、実際ソースコードで見ると壮絶ですね。。。 また、パラメーターの数が足りないというのは予想していませんでした (162個すべて使っているとは思わず、てっきり冗長的に256番まで全部取った結果が162なのかと。。) なにか解決策が思いつけばいいんですけど。。。 トライアングルリストが使用する行列の数は何に依存しているものですか? モデリングやウェイトの設定で削減できるものでしょうか?
メンテ
Re: アニメーションの状態を行列で表す ( No.3 )
名前:管理人 日時:2015/10/26 01:36

> トライアングルリストが使用する行列の数は何に依存しているものですか? スキニングメッシュの1スキンで使用しているボーンの数に依存していて、 「トライアングルリスト」が使用する行列の最大数が多ければ多いほど1ドローコールで 描画できる範囲が増えて効率よく描画処理を実行することができるので、1スキンで 使用しているボーン数が54を超える場合は「トライアングルリスト」で使用する行列の数が 54になる可能性があります( 実際は1スキンの中でマテリアルの異なる箇所や、頂点数が 65535を超える場合には何回かのドローコールに分かれるので単純に54ボーン以上の 1スキンメッシュだったら必ず行列数が54になるというわけではありませんが ) ただ、描画処理の効率を考えないのであれば「トライアングルリスト」が使用する 行列の最大数は1頂点に影響を与えるボーンの最大数まで減らすことができます つまり1頂点が影響を受けるボーンの最大数が4のモデルであれば「トライアングルリスト」が 使用する行列の最大数は4まで減らすことができます( その代わりドローコールの数は すごく増えて描画処理は重くなりますが・・・ ) なので、実は先ほどのバージョンにはこちらの関数も追加してあります // 読み込むモデルのひとつのトライアングルリストで使用できる最大ボーン数を設定する // ( UseMaxBoneNum で指定できる値の範囲は 8 〜 54、 0 を指定するとデフォルト動作に戻る ) int MV1SetLoadModelTriangleListUseMaxBoneNum( int UseMaxBoneNum ) ; MV1LoadModel でモデルファイルを読み込む前にこの関数でひとつのトライアングルリストで使用する 最大ボーン数を少なくしておけばトライアングルリストの描画時に占有される定数の数が減るので、 減った分の定数領域を使用することができる( そこに1フレーム前の行列をセットできる )ようになります ( 例えば MV1SetLoadModelTriangleListUseMaxBoneNum( 27 ) ; を実行しておけば、使用される 範囲は 94 〜 125 になるので、126 〜 255 までの範囲が使用できるようになります ) 因みに MV1ファイルはすでにトライアングルリスト化が済んでしまっているので、この関数が 有効なのは X, PMD, PMX などのトライアングルリスト化がされる前の3Dモデルファイルを 読み込む場合のみです あと、この関数による使用する行列の数を制限する設定をする項目を増やしたモデルビューアーも アップしましたので、もし MV1ファイルの状態で使用されたい場合はこちらをお使いください m(_ _)m ( 『読み込みオプション』→『トライアングルリストひとつで使用する最大ボーン数の変更』という項目です ) // ビューアー https://dxlib.xsrv.jp/temp/DxLibModelViewerTest.zip
メンテ
Re: アニメーションの状態を行列で表す ( No.4 )
名前:教えてくん 日時:2015/10/26 12:24

流石管理人さん、私より一歩先を想定していらっしゃる。。。 これなら問題なく1フレーム前の定数を扱えますね 一つ疑問に思ったのですが、ボーンではなくシェイプ(頂点モーフ)を利用したアニメーションはどういった処理で扱っているのでしょう? シェーダー側には記述がみられませんが、ライブラリ側で計算してから渡しているのでしょうか?
メンテ
Re: アニメーションの状態を行列で表す ( No.5 )
名前:管理人 日時:2015/10/27 01:29

> 一つ疑問に思ったのですが、ボーンではなくシェイプ(頂点モーフ)を利用したアニメーションはどういった処理で扱っているのでしょう? > シェーダー側には記述がみられませんが、ライブラリ側で計算してから渡しているのでしょうか? はい、その通りです シェイプは描画の前にCPUでシェイプ後の頂点座標を毎回計算しています( そして頂点バッファに書き込んでから描画処理を実行 ) なのでシェイプの範囲が広くなると重いです
メンテ
Re: アニメーションの状態を行列 ( No.6 )
名前:教えてくん(解決) 日時:2015/10/27 19:57

なるほど、そういう実装だとシェイプを多用したアニメーションの移動ベクトルをシェーダ側で求めるのは諦めた方が良さそうですね 手厚い対応ありがとうございました
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存