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