日別アーカイブ: 2009年1月12日

HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説

設定等をもう少し説明してみます。(→前回)

Direct3DMobile Touch Diamond

ダウンロードはこちら
bumptest v1.00

スマートフォン HTC Touch Diamond は Qualcomm MSM7201A 内蔵の
3D アクセラレータを搭載しており ATI の GPU core となっています。

Direct3DMobile は PC でいう DirectX8 のサブセットですが、機能自体は
DirectX7 世代相当で固定パイプラインのみ。シェーダーは使えません。

caps を調べるとハードウエアアクセラレータとして認識していることがわかります。

D3DMDEVCAPS_HWTRANSFORMANDLIGHT
D3DMDEVCAPS_HWRASTERIZATION
D3DMDEVCAPS_NATIVEFLOAT

また使用できるマルチテクスチャは 2枚で TextureStage 数も 2段です。
無理矢理シェーダーにたとえれば ShaderModel 1.0 でピクセル単位に

・テクスチャ命令 x2
・演算命令 x2

を実行できるということ。DirectX7 世代のレジスタコンバイナそのままです。
TextureStage で使用できる演算として D3DMTEXOPCAPS_DOTPRODUCT3 が有効に
なっていたのでこれを使用します。

TextureStage には外部から与えられる定数がありません。
試しに D3DMRS_DIFFUSEMATERIALSOURCE を使って頂点カラーの代わりに Material
からパラメータを取れないか試しましたがだめでした。
これはライティングユニットへの入力時にのみ機能するようです。

外部から定数を渡すことが出来ればすぐにでもオブジェクトスペースの
ノーマルマップでテストできるのですがあきらめます。
パラメータは頂点カラーとして渡すことにします。
どうせ頂点を経由するなら最初からタンジェントスペースにします。

頂点の光源計算はもともと CPU で行っていました。
このときオブジェクトスペースに変換した光源ベクトルを使いますが、ここで
DotProduct せずにベクトルをそのままカラー値として出力します。

まずは D3DMTEXOPCAPS_DOTPRODUCT3 のベクトルフォーマットを確認します。
いくつかのカラー値を通してみてどのような復号が行われているか推測。
マニュアルには符号付きとだけ書かれており明確ではありませんでしたが、
やはりシェーダー同様 _bx2 相当 ( (x-0.5)*2 ) でした。

モデルデータの頂点にはオブジェクトスペースからタンジェントスペースへの変換
マトリクスを埋め込んでおきます。
1軸は法線と共有するため +2 vect。CrossProduct するなら +1 のみ。
データの生成と export は昔 3ds Max 用に作った自作プラグインを使用しています。

WorldSpace (GlobalSpace)
   → ObjectSpace (LocalSpace)
       → TangentSpace (TextureSpace)

元の光源ベクトルは WorldSpace に配置されているため、頂点法線も WorldMatrix
(3×3) で変換してから演算する必要があります。
一般的に光源の数より頂点の方が多いので、CPU 演算の手抜きライトの場合は光源の方を
WorldMatrix(3×3) の逆行列で ObjectSpace に変換した方が演算量が減ります。
同じようにノーマルマップからサンプリングした法線を適用するため、
光源ベクトルを TangentSpace まで逆変換します。

頂点単位のライティングの代わりに、オブジェクトスペースへ変換した光源ベクトルを
タンジェントスペースへ変換する演算を追加します。変換 matrix (3×3)をかけるだけ。
結果を 0~1.0 に収まるよう _bx2 の逆変換で格納します。
固定小数演算なのでビットシフト等で最適化します。

vect3 tslightdir;
TangentMatrix.Transformation( tslightdir, oslightdir );
colorr= ((tslightdir.x+ FIXED16(1.0f))* 255)>>17;
colorg= ((tslightdir.y+ FIXED16(1.0f))* 255)>>17;
colorb= ((tslightdir.z+ FIXED16(1.0f))* 255)>>17;

シェーダーがあれば VertexShader でやっているところです。
またノーマルマップ生成ツールによって座標系が異なってることがあるので
軸の符号などは合わせます。

テクスチャステージでは読み込んだノーマルマップの値と頂点から出力した上記の
光源ベクトルを DotProduct します。

一段目はノーマルマップで光源演算。

SetTexture( 0, iTexture0 );
SetTextureStageState( 0, D3DMTSS_COLORARG1, D3DMTA_TEXTURE );
SetTextureStageState( 0, D3DMTSS_COLORARG2, D3DMTA_DIFFUSE );
SetTextureStageState( 0, D3DMTSS_RESULTARG, D3DMTA_CURRENT );
SetTextureStageState( 0, D3DMTSS_COLOROP, D3DMTOP_DOTPRODUCT3 );
SetTextureStageState( 0, D3DMTSS_ALPHAOP, D3DMTOP_DISABLE );
SetTextureStageState( 0, D3DMTSS_TEXCOORDINDEX, 0|D3DMTSS_TCI_PASSTHRU );

二段目はカラーマップの合成。

SetTexture( 1, iTexture1 );
SetTextureStageState( 1, D3DMTSS_COLORARG1, D3DMTA_TEXTURE );
SetTextureStageState( 1, D3DMTSS_COLORARG2, D3DMTA_CURRENT );
SetTextureStageState( 1, D3DMTSS_RESULTARG, D3DMTA_CURRENT );
SetTextureStageState( 1, D3DMTSS_COLOROP, D3DMTOP_MODULATE );
SetTextureStageState( 1, D3DMTSS_ALPHAOP, D3DMTOP_DISABLE );
SetTextureStageState( 1, D3DMTSS_TEXCOORDINDEX, 0|D3DMTSS_TCI_PASSTHRU );

まだ ALPHA があいています。
PowerVR だと適用可能なオペレーションの種類がもっと多いのでいろいろ出来るでしょう。

関連エントリ
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ