オンラインゲームで良く見かける頭上名前表示をやっちゃおうというハナシ。
一般的な名称は知らないので、この記事ではネームプレートと勝手に呼ぶ事にします。
とりあえずこのネームプレートの条件としては、
1. 常にカメラに対する向きを向く四角形の板ポリゴン(ビルボード)。
2. 奥に居るキャラのネームプレートでも同じサイズで表示される。
3. zソートされる(つまりスプライトではない)。
と言う風にします。
条件3については、ゲームに依存。不要な場合はスプライトを使ってもっとカンタンに実装可能。
パッと思いつく方法としては、視点からの距離に応じてスケールをかけると言う方法。
ちゃんと試してないのでわからないけど、コレでもそれなりには見えそう。
ただ、この方法だと問題があるハズで、
ネームプレートの4頂点それぞれに透視投影変換が入るので、
若干斜めって見えるハズ。ハズです。試してないけど。
つまりは、サイズが透視投影変換で変わらず、スクリーンに映してもちゃんとした四角形になる
必要があります。とりあえずコレを実現するにはどうするか?
そう、プロテイン正射影変換だね。
正射影変換なら、スクリーンに映しても、奥行き関係なくサイズは一定です。
ただ、正射影変換すると、当然ネームプレートのワールド座標も正射影変換された位置に
なってしまい・・・。ではどうするのか。とりあえずの自分の回答としては、
正射影変換した時に、透視投影変換した時と
同じスクリーン座標に映る座標をワールド座標にして正射影変換する、です。
ではコード。
// ビルボードにするまずは、条件1をクリアしないといけないので、ビルボードにするマトリクスを準備。
D3DXMATRIX mBill;
D3DXMatrixInverse(&mBill, NULL, &mView); // ビュー行列の逆行列
mBill._41 = mBill._42 = mBill._43 = 0.0f; // カメラの平行移動成分は潰す
次に、ネームプレートの表示用の(正射影変換用の)ワールド座標を計算していくコード。
// ネームプレートを置きたいワールド座標を設定する_vCharPosは、なんかのキャラクターの座標と思っていただいて。
D3DXVECTOR3 v(_vCharPos.x, _vCharPos.y + 1.0f, _vCharPos.z);// 一旦ネームプレートの位置を透視投影まで行う
D3DXVec3TransformCoord(&v, &v, &mView); // ビュー変換
D3DXVec3TransformCoord(&v, &v, &mProj); // 透視投影変換
その頭上(y+1.0f)を、(ゲーム的な空間の)ネームプレートのワールド座標とします。
次は正射影変換行列を作るコード。
// 正射影変換行列を作るここら辺は前回の記事と同じ。
D3DXMATRIX mOrtho;
float nearH = NEAR_Z * tan(FOV_ANGLE*0.5f) * 2.0f;
float nearW = nearH * (float)SCREEN_W / (float)SCREEN_H;
D3DXMatrixOrthoLH(&mOrtho, nearW, nearH, NEAR_Z, FAR_Z);
正射影変換行列が求まったら、ネームプレートの表示用ワールド座標を求めます。
// 正射影の逆行列をかけて、正射影した場合に同じ位置に
// 映る様なカメラ座標系の座標を計算する
D3DXMATRIX mInvOrtho;
D3DXMatrixInverse(&mInvOrtho, NULL, &mOrtho);
D3DXVec3TransformCoord(&v, &v, &mInvOrtho);// ビュー行列の逆行列をかけて、ワールド座標系に戻す
D3DXMATRIX mInvView;
D3DXMatrixInverse(&mInvView, NULL, &mView);
D3DXVec3TransformCoord(&v, &v, &mInvView);
さあこれで表示用のワールド座標が求まったので、後はレンダリングパイプラインに
SetTransformするだけ!
// 表示用ワールド座標から平行移動マトリクスを計算する
D3DXMatrixTranslation(&mTrans, v.x, v.y, v.z);// ワールドトランスフォーム
mWorld = mBill * mTrans;
_pDevice->SetTransform(D3DTS_WORLD, &mWorld);// ビュートランスフォーム
_pDevice->SetTransform(D3DTS_VIEW, &mView);// プロジェクショントランスフォーム
_pDevice->SetTransform(D3DTS_PROJECTION, &mOrtho); // 正射影変換行列でトランスフォーム
とりあえずこれで出るハズ!
パフォーマンス最適化については各自で頑張ってw
以下が自作のテストプログラムのSS。
ヤカンの上にある「hoge」ってのがネームプレートなんですが、
どのヤカンのネームプレートもサイズが一定なのわかりますかね?
後、奥のヤカンのネームプレートが、前のヤカンに若干隠れているのも。
ネームプレート自体は、板ポリゴンに、
テキストをレンダリングしたテクスチャを張っているだけ。
そこらじゅうで見かけるネームプレートだけど、
いざ自作してみようとなるとなかなかタイヘンでした・・・。
実現のアプローチは、コレだけじゃないだろうから、参考程度という事で。