オンラインゲームで良く見かける頭上名前表示をやっちゃおうというハナシ。


一般的な名称は知らないので、この記事ではネームプレートと勝手に呼ぶ事にします。
とりあえずこのネームプレートの条件としては、

1. 常にカメラに対する向きを向く四角形の板ポリゴン(ビルボード)。
2. 奥に居るキャラのネームプレートでも同じサイズで表示される。
3. zソートされる(つまりスプライトではない)。


と言う風にします。
条件3については、ゲームに依存。不要な場合はスプライトを使ってもっとカンタンに実装可能。


パッと思いつく方法としては、視点からの距離に応じてスケールをかけると言う方法。
ちゃんと試してないのでわからないけど、コレでもそれなりには見えそう。


ただ、この方法だと問題があるハズで、
ネームプレートの4頂点それぞれに透視投影変換が入るので、
若干斜めって見えるハズ。ハズです。試してないけど。


つまりは、サイズが透視投影変換で変わらず、スクリーンに映してもちゃんとした四角形になる
必要があります。とりあえずコレを実現するにはどうするか?
そう、プロテイン正射影変換だね。
正射影変換なら、スクリーンに映しても、奥行き関係なくサイズは一定です。


ただ、正射影変換すると、当然ネームプレートのワールド座標も正射影変換された位置に
なってしまい・・・。ではどうするのか。とりあえずの自分の回答としては、
正射影変換した時に、透視投影変換した時と
同じスクリーン座標に映る座標をワールド座標にして正射影変換する、です。


ではコード。

// ビルボードにする
D3DXMATRIX mBill;
D3DXMatrixInverse(&mBill, NULL, &mView); // ビュー行列の逆行列
mBill._41 = mBill._42 = mBill._43 = 0.0f; // カメラの平行移動成分は潰す
まずは、条件1をクリアしないといけないので、ビルボードにするマトリクスを準備。


次に、ネームプレートの表示用の(正射影変換用の)ワールド座標を計算していくコード。

// ネームプレートを置きたいワールド座標を設定する
D3DXVECTOR3 v(_vCharPos.x, _vCharPos.y + 1.0f, _vCharPos.z);

// 一旦ネームプレートの位置を透視投影まで行う
D3DXVec3TransformCoord(&v, &v, &mView); // ビュー変換
D3DXVec3TransformCoord(&v, &v, &mProj); // 透視投影変換

_vCharPosは、なんかのキャラクターの座標と思っていただいて。
その頭上(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」ってのがネームプレートなんですが、
どのヤカンのネームプレートもサイズが一定なのわかりますかね?
後、奥のヤカンのネームプレートが、前のヤカンに若干隠れているのも。


ネームプレート自体は、板ポリゴンに、
テキストをレンダリングしたテクスチャを張っているだけ。


そこらじゅうで見かけるネームプレートだけど、
いざ自作してみようとなるとなかなかタイヘンでした・・・。
実現のアプローチは、コレだけじゃないだろうから、参考程度という事で。