結果だけでなく過程も見てください

たい焼きさんの日々の奮闘を綴る日記です。

DirectXでテクスチャに対してレンダリングを行う方法


通常、DirectXで3Dオブジェクトをレンダリングするときの
レンダリング先はIDirect3DDevice9デバイスを作成したときに
一緒に作成されたバックバッファになります。


今回はレンダリング先をバックバッファからテクスチャに
変更する方法を紹介します。


まずは必要なメンバの宣言です。一部はセットアップ済みとします。

// ここからセットアップ済み
IDirect3DDevice9*	g_pD3DDevice;
// ここまでセットアップ済み

// 作成するテクスチャのサイズ
static const int TEXTURE_WIDTH = 512;
static const int TEXTURE_HEIGHT = 512;

// レンダリング対象のテクスチャ
LPDIRECT3DTEXTURE9	 g_pTexture=NULL;

// 上記レンダリング対象のテクスチャのサーフェイス	
LPDIRECT3DSURFACE9 g_pTextureSurface=NULL;

// レンダリング対象のテクスチャ用深度バッファ
LPDIRECT3DSURFACE9	 g_pDepthSurface=NULL;

// もともとレンダリングターゲットにセットされていたサーフェイス
LPDIRECT3DSURFACE9	 g_pBackBufferSurface=NULL;

// もともとレンダリングターゲットにセットされていたサーフェイス
LPDIRECT3DSURFACE9	 g_pBackDepthSurface=NULL;


今回はテクスチャに対してレンダリングをするワケですが、
いくつか注意する点があります。


1.テクスチャへのレンダリングが終わったあとは、元のバックバッファを
 レンダリングターゲットに戻す必要があるため、レンダリングターゲットを
 変更する前にバックバッファのサーフェイスを取得しておく必要があります。
 また、これは深度バッファも同様で、レンダリングターゲットを変更する前に
 設定されている深度バッファのサーフェイスを取得しておきます。


2.テクスチャにレンダリングすることを可能にするには、
 そのテクスチャを作るときにD3DUSAGE_RENDERTARGETを
 指定しなければなりません。


3.テクスチャを作成する場所はVRAM内でなければなりません。
 そのため今回はテクスチャ作成時にD3DPOOL_DEFAULTを指定します。


4.作成するテクスチャのサイズは2のべき乗でなければなりません。
 これは決まりなのでこういうものだと思ってください。

 今回はサイズを(TEXTURE_WIDTH,TEXTURE_HEIGHT)=(512,512)としており、
 512は2のべき乗なので問題ありませんが、例えば480x480のテクスチャを
 作成したいときなどは480以上で、最も近い2のべき乗(つまり512)を
 自分で計算してテクスチャを作成する必要があります。


5.テクスチャはレンダリングされる色の情報しか持ちません。
 実際の描画ではポリゴンの前後関係を考慮する必要があるため、
 必ず深度バッファを作成してデバイスに設定しなければなりません。
 また深度バッファのサイズは、レンダリングするテクスチャと
 同じサイズにしなければなりません。


まずは上記1.に書かれている元々設定されているバックバッファおよび
深度バッファのサーフェイス取得を行います。

// バックバッファのサーフェイスを記憶しておく
m_pD3DDevice->GetRenderTarget( 0, &g_pBackBufferSurface );

// ステンシルバッファを取得しておく
m_pD3DDevice->GetDepthStencilSurface( &g_pBackDepthSurface );


次に上記2. 3. 4.に気をつけてテクスチャを作成します。

if ( FAILED( D3DXCreateTexture( 
                g_pD3DDevice, 
                TEXTURE_WIDTH, 
                TEXTURE_HEIGHT, 
                0, 
                D3DUSAGE_RENDERTARGET, 
                D3DFMT_A8R8G8B8, 
                D3DPOOL_DEFAULT, 
                &g_pTexture )))
{
	// テクスチャの作成に失敗
	// エラーに対応するコードをここに書く
}


続いて、上記5.で言った通りステンシルバッファ(深度バッファ)を作成します。

if ( FAILED( m_pD3DDevice->CreateDepthStencilSurface( 
                 TEXTURE_WIDTH,
                 TEXTURE_HEIGHT,
                 D3DFMT_D16, 
                 D3DMULTISAMPLE_NONE, 
                 0, 
                 TRUE, 
                 &g_pDepthSurface, 
                 NULL ) ) )
{
	// ステンシルバッファの作成に失敗
	// エラーに対応するコードをここに書く
}


作成したステンシルバッファを設定します。

m_pD3DDevice->SetDepthStencilSurface( g_pDepthSurface );


作成したテクスチャにレンダリングターゲットを変更します。

LPDIRECT3DSURFACE9 pSurface;
if ( FAILED( pTexture->GetSurfaceLevel( 0, &pSurface ) ) )
{
	// サーフェイス取得失敗
	// エラーに対応するコード
}

m_pD3DDevice->SetRenderTarget( 0, pSurface );

// 記憶しておく
g_pTextureSurface = pSurface;


テクスチャとステンシルバッファを作成し、
レンダリングターゲットを変更した場合はクリアするのが一般的です。
クリアするときのポイントとしては、デバイスのシーンが終わっていなければ
ならないということです。つまりBeginScene()〜EndScene()外でクリアしな
ければならないということです。

// もしBeginScene内だった場合、いったん終了させる
if ( BeginScene内だった場合 )
{
	m_pD3DDevice->EndScene();
}

// テクスチャサーフェイスのクリア
m_pD3DDevice->Clear( 0, 
                     NULL, 
                     D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
                     D3DCOLOR_ARGB( 0, 255, 0, 0 ),
                     1.0f,
                     0 );

// シーンを再開させる
if ( もともとBeginScene内だった場合 )
{
    m_pD3DDevice->BeginScene();
}


さてメインの描画処理です。ポイントとしてはBeginScene()で確実シーンが
開始されていることを確認してから描画することです。
開始していない場合は開始させましょう。

m_pD3DDevice->BeginScene();

// ここで描画

m_pD3DDevice->EndScene();


次はある意味最も重要です。後始末です。
ここできちんと後始末をしておかないと、カウンタが狂って
解放されないことがあります。

まずレンダリングターゲットおよび深度バッファを元に戻します。
ポイントは、GetSurfaceLevelで内部カウンタが+1されているため、
Release()をコールしてカウンタを0にすることです。
ここでは、DirectXのマクロ(SAFE_RELEASE)を使っています。

m_pD3DDevice->SetRenderTarget( g_pBackBufferSurface );
m_pD3DDevice->SetDepthStencilSurface( g_pBackDepthSurface );

SAFE_RELEASE( g_pTextureSurface );
SAFE_RELEASE( g_pDepthSurface );


これでテクスチャに描画することができました。
あとはこれをポリゴンに貼り付けて描画したり、さらに加工したりと
煮るなり焼くなりしてください。