Vertex Shader:モーションブラー2


~テクスチャーに書き込みました~




■はじめに

今回は、残像の別の表現方法を行います。
こちらは、前のフレームの画像が薄く残って尾を引きます。

今回のソースは、次のものです。

内容は次のとおりになっています。

draw.cppメインの描画部分。主にここが説明される。
main.h基本的な定数など。今回は画面サイズを512x512にしました。。
main.cpp描画に関係しないシステム的な部分。変更が無いので、出番無し。
draw.h描画の各関数の定義。特に意味無いので出番無し。
vs.vsh頂点シェーダープログラム。平行光源ライト。

あと、モデルとして、nsx.xと、実行ファイルの MyBase.exe 及び、 VC++ でコンパイルするためのプロジェクトファイル MyBase.dsw MyBase.dsp が入っています。

今回の肝は、テクスチャーにレンダリングすることです。
しかもレンダリング用のテクスチャーを2枚用意して、前のフレームの画像を用いることによって、残像を表現します。
ちなみに今回の画像が 512x512 なのは、テクスチャーを確保するのが 2のべき乗でないと上手くいかなかったからです。
いや、ビューポートをちゃんと設定すれば可能ですが、面倒くさかったので。

■ソース

ソースを見ていきます。
先ず、使用するオブジェクトですが、レンダリングのためのテクスチャーと、関連するサーフェスが必要です。
さらに、元のバックバッファを保存するためのサーフェスのポインタも用意します。
テクスチャーは、ミップマップに対応するために、いくつかの画像の集まりになっています。
その画像の一つ一つがサーフェスです。

LPDIRECT3DVERTEXBUFFER8 pMeshVB = NULL;
LPDIRECT3DINDEXBUFFER8  pMeshIndex = NULL;
D3DXATTRIBUTERANGE      *pSubsetTable = NULL;
DWORD                   nMeshFaces = 0;
DWORD                   nMeshVertices = 0;
D3DMATERIAL8            *pMeshMaterials = NULL;     // メッシュの質感
LPDIRECT3DTEXTURE8      *pMeshTextures  = NULL;     // メッシュのテクスチャー
DWORD                   dwNumMaterials = 0L;        // マテリアルの数
FLOAT                   MeshRadius;                 // メッシュの大きさ
DWORD                   hVertexShader=~0;

LPDIRECT3DTEXTURE8      pTexture[2];                // レンダリングのためのテクスチャー
LPDIRECT3DSURFACE8      pTextureSurface[2];         // テクスチャーに関連するサーフェス
LPDIRECT3DSURFACE8      pBackbuffer = NULL;         // バックバッファを保存するためのサーフェス

それらの初期化ですが、下のソースの色で塗りつぶした部分が今回新しい部分です。
テクスチャーへのレンダリングと、通常のバックバッファへのレンダリングの切り替えのために、バックバッファのサーフェスへのポインタを確保します。
その後に、GetDesc を使って、画像フォーマットなどのバックバッファの情報を利用しながら、テクスチャーを生成します。
この辺の手順は、大体決まっているようです。
CreateTexture で、テクスチャーを生成した後、GetSurfaceLevel で、関連するサーフェスを定義します。
その後に、SetRenderTarget で、テクスチャーと深度バッファを組にします。

//-----------------------------------------------------------------------------
// Name: InitRender()
// Desc: Load the mesh and build the material and texture arrays
//-----------------------------------------------------------------------------
HRESULT InitRender(LPDIRECT3DDEVICE8 lpD3DDev)
{
    HRESULT hr;

    // モデルの読み込み
    if ( FAILED(hr = LoadXFile("nsx.x", lpD3DDev)) ) return hr;
    
    // バーテックスシェーダーを作成する
    if ( FAILED(hr = LoadVertexShader("vs.vsh", lpD3DDev, &hVertexShader, dwDecl)) ) return hr;

    // バックバッファのポインタを確保
    if ( FAILED(hr = lpD3DDev->GetRenderTarget(&pBackbuffer))) return hr;
    
    // バックバッファの情報を調べる
    D3DSURFACE_DESC Desc;
    if ( FAILED(hr = pBackbuffer->GetDesc( &Desc ))) return hr;
    
    // 深度バッファのサーフェスを確保する
    LPDIRECT3DSURFACE8 lpZbuffer = NULL;
    if( FAILED(hr = lpD3DDev->GetDepthStencilSurface( &lpZbuffer ))) return hr;
    
    // 描画用テクスチャーを用意する(2枚分)
    for( int i = 0; i < 2; i++ ){
        if( FAILED(hr = lpD3DDev->CreateTexture(WIDTH,HEIGHT, 1 ,
                                  D3DUSAGE_RENDERTARGET, Desc.Format, 
                                  D3DPOOL_DEFAULT, &pTexture[i]))) return hr;                   // テクスチャーの生成
        if( FAILED(hr = pTexture[i]->GetSurfaceLevel(0,&pTextureSurface[i]))) return hr;        // テクスチャー(のミップマップレベル0)とサーフェスを関連づける
        if( FAILED(hr = lpD3DDev->SetRenderTarget(pTextureSurface[i], lpZbuffer ))) return hr;  // テクスチャー用の描画と深度バッファを関連付ける

        lpD3DDev->BeginScene();
        lpD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );// 初期化としてテクスチャーを塗りつぶしておく
        lpD3DDev->EndScene();
    }
    // 描画を元の画面に戻す
    lpD3DDev->SetRenderTarget(pBackbuffer, lpZbuffer );

    return S_OK;
}

注意点としては、CreateTexture で、D3DUSAGE_RENDERTARGET を指定することです。
また、SetRenderTarget は、描画サーフェスと深度バッファのサーフェスを組にするのですが、同時に描画する場所も変更するので、 最後に元のバックバッファに戻しておかなくてはなりません。

では、描画部分です。
黄色の部分がテクスチャーの切り替えになっています。
static 変数 cnt を使って、描画するテクスチャーを交互に切り替えています。
また、最後に元に戻してから、作成したテクスチャーをべたっと張っています(そのための構造体が TLVERTEX です)。
途中で前フレームのテクスチャーを半透明で描画するために render state を変更しています(オレンジの部分です)。
深度バッファを無視して、半透明合成しています。
それ以外はいつもどおりのモデル表示やテクスチャーの描画です。

//-----------------------------------------------------------------------------
// 板ポリを描画するための構造体
typedef struct {
    float x,y,z,rhw;
    D3DCOLOR color;
    float tu,tv;
}TLVERTEX;
#define FVF_TLVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render(LPDIRECT3DDEVICE8 lpD3DDev)
{
    LPDIRECT3DSURFACE8 lpZbuffer = NULL;
    static int cnt = 0;
    ++cnt;

    lpD3DDev->GetDepthStencilSurface( &lpZbuffer );
    lpD3DDev->SetRenderTarget(pTextureSurface[cnt & 1], lpZbuffer );
    lpD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,32,32), 1.0f, 0 );

    D3DXMATRIX mWorld, mView, mProj, m;

    D3DXMatrixTranslation(&m, MeshRadius, 0,0);
    D3DXMatrixRotationY( &mWorld, timeGetTime()/300.0f );
    mWorld = m * mWorld;
    
    // ビュー行列
    D3DXVECTOR3 eye    = D3DXVECTOR3(0.0f,MeshRadius,2.5f*MeshRadius);
    D3DXVECTOR3 lookAt = D3DXVECTOR3(0.0f,  0.0f,  0.0f);
    D3DXVECTOR3 up     = D3DXVECTOR3(0.0f,  1.0f,  0.0f);
    // 通常表示
    D3DXMatrixLookAtLH(&mView, &eye, &lookAt, &up);
    D3DXMatrixPerspectiveFovLH(&mProj
        ,60.0f*PI/180.0f                        // 視野角
        ,(float)WIDTH/(float)HEIGHT             // アスペクト比
        ,0.01f,100.0f                           // 最近接距離,最遠方距離
        );

    m = mWorld * mView * mProj;
    D3DXMatrixTranspose( &m ,  &m);
    lpD3DDev->SetVertexShaderConstant(0,&m, 4);

    D3DXVECTOR4 lightDir(1.0f, 1.0f, 0.5f, 0.0f);
    D3DXVec4Normalize(&lightDir, &lightDir);
    D3DXMatrixInverse( &m,  NULL, &mWorld);
    D3DXVec4Transform(&lightDir, &lightDir, &m);
    lightDir[3] = 0.3f;// 環境光の強さ
    lpD3DDev->SetVertexShaderConstant(13, &lightDir, 1);

    //メッシュの描画
    lpD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);
    lpD3DDev->SetVertexShader(hVertexShader);
    lpD3DDev->SetStreamSource(0, pMeshVB, sizeof(D3D_CUSTOMVERTEX));
    lpD3DDev->SetIndices(pMeshIndex,0);
    for(DWORD i=0;i//色をセット
        D3DXVECTOR4 vl;
        vl.x = pMeshMaterials[i].Diffuse.r;
        vl.y = pMeshMaterials[i].Diffuse.g;
        vl.z = pMeshMaterials[i].Diffuse.b;
        lpD3DDev->SetVertexShaderConstant(14, &vl, 1);

        lpD3DDev->SetTexture(0,pMeshTextures[i]);
        lpD3DDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 
                                        pSubsetTable[i].VertexStart,
                                        pSubsetTable[i].VertexCount,
                                        pSubsetTable[i].FaceStart * 3,
                                        pSubsetTable[i].FaceCount);
    }
    
    // 残像(位置フレーム前の画面を半透明で描く)
    lpD3DDev->SetRenderState( D3DRS_ZENABLE, FALSE );
    lpD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    lpD3DDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    lpD3DDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    lpD3DDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
    
    TLVERTEX Vertex[4] = {
        // x  y  z rhw           color                     tu tv
        {  0,  0,0,1,D3DCOLOR_ARGB( 200, 255, 255, 255 ),0,0,},
        {512,  0,0,1,D3DCOLOR_ARGB( 200, 255, 255, 255 ),1,0,},
        {  0,512,0,1,D3DCOLOR_ARGB( 200, 255, 255, 255 ),0,1,},
        {512,512,0,1,D3DCOLOR_ARGB( 200, 255, 255, 255 ),1,1,},
    };
    lpD3DDev->SetTransform( D3DTS_VIEW, &mView );
    lpD3DDev->SetTransform( D3DTS_PROJECTION, &mProj );
    lpD3DDev->SetTexture( 0, pTexture[(cnt + 1)&1] );
    lpD3DDev->SetVertexShader( FVF_TLVERTEX );
    lpD3DDev->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, Vertex, sizeof( TLVERTEX ) );

    // 描画をバックバッファに戻す
    lpD3DDev->GetDepthStencilSurface( &lpZbuffer );
    lpD3DDev->SetRenderTarget(pBackbuffer, lpZbuffer );
    lpD3DDev->SetRenderState( D3DRS_ZENABLE, TRUE );
    lpD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);

    // 描画した画面をテクスチャーとして描く
    {
        TLVERTEX Vertex[4] = {
            // x  y  z rhw           color                     tu tv
            {  0,  0,0, 1, D3DCOLOR_ARGB( 255, 255, 255, 255 ), 0, 0,},
            {512,  0,0, 1, D3DCOLOR_ARGB( 255, 255, 255, 255 ), 1, 0,},
            {  0,512,0, 1, D3DCOLOR_ARGB( 255, 255, 255, 255 ), 0, 1,},
            {512,512,0, 1, D3DCOLOR_ARGB( 255, 255, 255, 255 ), 1, 1,},
        };
        lpD3DDev->SetTexture( 0, pTexture[cnt&1] );
        lpD3DDev->SetVertexShader( FVF_TLVERTEX );
        lpD3DDev->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, Vertex, sizeof( TLVERTEX ) );
    }
}

あとは、使ったオブジェクトを最後に開放します。

■最後に

残像を再び取り扱いましたが、テクスチャーに描画することのほうがメインです。
テクスチャーに描画できれば、あんなことやこんなことが…




もどる

imagire@gmail.com