ホーム < ゲームつくろー! < プログラマブルシェーダ編

その13 静的・動的キューブマップテクスチャの作り方

 環境マップをする上で基本となるのがキューブマップテクスチャです。環境マップはプログラマブルシェーダ編シェーダ編その6「0から学ぶ環境マップ」で使っていますが、そこではD3DXCreateCubeTextureFromFile関数+.ddsファイルでサクっと作りました。もちろんそれもありですが、例えば動的環境マップなどはそれでは作れません。

 この章ではキューブマップテクスチャの作り方を見て行きます。



@ 静的な環境マップならD3DXCreateCubeTextureFromFile関数が楽

 キューブマップは最終的にはIDirect3DCubeTexture9インターフェイスを作ります。作り方はいくつかあります。
 環境マップは一般に負荷が高いため、最初から周りの景色を描いた静的環境マップを用意する事が多いです。この場合.dds形式で保存しておけばD3DXCreateCubeTextureFromFile関数で一発でキューブマップが作れます:

HRESULT D3DXCreateCubeTextureFromFile(
    LPDIRECT3DDEVICE9       pDevice,
    LPCTSTR                 pSrcFile,
    LPDIRECT3DCUBETEXTURE9 *ppCubeTexture
);

pSrcFileにキューブマップファイル名を指定します。

 そのキューブマップファイルは.dss形式を読み込めます。.dss形式のキューブマップを作成するにはDirectX SDKに付属しているDirectX Texture Editor(Dxtex.exe)を用います。実際に適当ですがキューブマップテクスチャを作ってみましょう。

 Dxtex.exeを立ち上げると空っぽなウィンドウが出てきます。メニューの[File]->[New Texture]を選択し、New Textureウィンドウにある「Texture Type」からCubemap Textureを選択します。また出力するテクスチャの幅と高さもここで指定します。これで空のキューブマップが作成されます。

 次に[View]->[Cube Map Face]を選択すると、キューブマップの方向を指定できます。DirectXは左手系なので、自分の右面が+X軸方向、上面が+Y軸方向の時、正面が+Z軸方向となります:

キューブマップの方向を指定したら、次にその方向に該当する画像を読み込みます。これは[File]->[Opne Onto This Surface]で選択します。読み込みに成功すれば、先程まで真っ青な画面だったテクスチャが指定の絵に切り替わっているはずです。この作業を上図の6面すべてに対して行います。

 各面の絵を指定し終わったら、Saveで保存するとキューブマップテクスチャ(.dds)が完成します。



A 動的環境マップ

 静的環境マップは読み込んだ時点で変更は普通できません。そのため、適用したモデルに映り込む映像も同じになります。それで違和感があまり出ないのは、人は写りこんだ映像を割と適当に見ているためです。もちろんその適当にも限界がありますから、場面が変わった時などは静的環境マップもしれっと取り替えていたりします。

 一方で、あまり多用は出来ませんが、時に自分の周囲の環境を本当に反映したい場合があります。いわゆる「動的環境マップ」です。これはリアルタイムで上のような6面のテクスチャを作り環境マップ化します。単純に6回レンダリングをする事になるため、描画負荷は相当に高くなります。多用出来ないというのはそういう理由です。

 6面のテクスチャはレンダーターゲットにレンダリングして作成します。カメラを上下左右前後方向に向けて撮影するだけなので、作成自体は単純です。そのレンダーターゲットは「キューブマップテクスチャのサーフェイス」となります。そのためには空っぽのキューブマップテクスチャが必要です。これはIDirect3DDevice9::CreateCubeTextureメソッドを用います:

IDirect3DDevice9::CreateCubeTextureメソッド
HRESULT CreateCubeTexture(
    UINT EdgeLength,
    UINT Levels,
    DWORD Usage,
    D3DFORMAT Format,
    D3DPOOL Pool,
    IDirect3DCubeTexture9 ** ppCubeTexture,
    HANDLE* pSharedHandle
);

EdgeLengthはテクスチャの幅高さの長さを指定します。「Cube」なので正方形です(^-^)
Levelsはミップマップレベル数です。ここに0を指定すると1×1までのミップマップが6面すべてについて作成されます。
Usageはこのキューブマップテクスチャの使われ方をフラグ指定します。今は動的にレンダリングしようと考えているので「D3DUSAGE_RENDERTARGET」を指定する必要があります。
FormatはテクスチャフォーマットをD3DFORMAT列挙型で指定します。おおよそD3DFMT_A8R8G8B8かなと思います。
Poolはこのテクスチャの管理先を指定します。ただレンダーターゲットの場合はD3DPOOL_DEFAULTを指定し、解放等の管理を自前しなければなりません。
ppCubeTextureに空のキューブマップテクスチャが返ります。
pSharedHandleはNULLのみ許可です。

 以上の設定で空のキューブマップテクスチャが取得できますので、後はそこからサーフェイス(レンダーターゲット)を個別に得ます。これはIDirect3DCubeTexture9::GetCubeMapSurfaceメソッドを用います:

IDrect3DCubeTexture9::GetCubeMapSurfaceメソッド
HRESULT GetCubeMapSurface(
    D3DCUBEMAP_FACES FaceType,
    UINT Level,
    IDirect3DSurface9 ** ppCubeMapSurface
);

FaceTypeで取得したい方向を指定します。D3DCUBEMAP_FACES列挙型は次のようになっています:

D3DCUBEMAP_FACES列挙型
enum D3DCUBEMAP_FACES {
    D3DCUBEMAP_FACE_POSITIVE_X = 0,
    D3DCUBEMAP_FACE_NEGATIVE_X = 1,
    D3DCUBEMAP_FACE_POSITIVE_Y = 2,
    D3DCUBEMAP_FACE_NEGATIVE_Y = 3,
    D3DCUBEMAP_FACE_POSITIVE_Z = 4,
    D3DCUBEMAP_FACE_NEGATIVE_Z = 5,
    D3DCUBEMAP_FACE_FORCE_DWORD = 0xffffffff,
};

見た目そのまんまなので説明は省略します。

 指定の方向のサーフェイスを取得したら参照カウントが上がりますので、レンダーターゲットを使い終わったらReleaseする事を忘れずにです。



B 動的環境マップ作成時のカメラと射影変換

 動的に環境マップを作成する時に注意したいのがカメラと射影変換行列の設定です。キューブマップは正方形です。そして各マップのエッジは連結しています。そういう絵を6枚きちっと描画しないといけないわけです。

 まずカメラは当然6方角を向きます。この時気を付けたいのがUpベクトルです。前面、後面、左右面のUpベクトルはY軸方向、これは問題ありません。一方上面と下面を撮影する時にはUpベクトルはそれぞれ-Zと+Z軸方向になります。D3DCUBEMAP_FACES列挙型の要素番号に対応したカメラの向きとUpベクトルは次のようになります:

D3DXVECTOR3 lookAt[6] = {
    D3DXVECTOR3( 1.0f,  0.0f,  0.0f), // +X
    D3DXVECTOR3(-1.0f,  0.0f,  0.0f), // -X
    D3DXVECTOR3( 0.0f,  1.0f,  0.0f), // +Y
    D3DXVECTOR3( 0.0f, -1.0f,  0.0f), // -Y
    D3DXVECTOR3( 0.0f,  0.0f,  1.0f), // +Z
    D3DXVECTOR3( 0.0f,  0.0f, -1.0f) // -Z
};

D3DXVECTOR3 up[6] = {
    D3DXVECTOR3( 0.0f, 1.0f,  0.0f), // +X(Up = +Y)
    D3DXVECTOR3( 0.0f, 1.0f,  0.0f), // -X(Up = +Y)
    D3DXVECTOR3( 0.0f, 0.0f, -1.0f), // +Y(Up = -Z)
    D3DXVECTOR3( 0.0f, 0.0f,  1.0f), // -X(Up = +Z)
    D3DXVECTOR3( 0.0f, 1.0f,  0.0f), // +Z(Up = +Y)
    D3DXVECTOR3( 0.0f, 1.0f,  0.0f), // -Z(Up = +Y)
};

カメラの位置は世界を撮影するので一般的に原点、もしくは環境マップを適用するオブジェクトの位置になります。

 次に射影変換行列ですが、カメラの視野角はきっちり90度にしないと継ぎ目が不連続になります。これは正方形な空間のど真ん中にカメラがいる時の各面を綺麗に捉える角度をイメージすると当然です。またレンダリング先のマップは正方形なのでアスペクト比は1.0fです。これらを考慮した射影変換行列は次のようになります:

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, D3DXToRadian(90.0f), 1.0f, 1.0f, 10000.0f);

 後はこれらを描画デバイスに設定しキューブマップの各面をレンダーターゲットに指定して撮影をすれば、周りの景色が綺麗に環境マップ化されます。

 以上を踏まえたサンプルを用意しましたので試してみて下さい。