今回は、残像の別の表現方法を行います。
こちらは、前のフレームの画像が薄く残って尾を引きます。
今回のソースは、次のものです。
内容は次のとおりになっています。
| 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 ) );
}
}
あとは、使ったオブジェクトを最後に開放します。
残像を再び取り扱いましたが、テクスチャーに描画することのほうがメインです。
テクスチャーに描画できれば、あんなことやこんなことが…