📄 gameoflife.cpp
字号:
// select source texture:
// targ ind 0 = neighbor calc
// targ ind 1 = result storage #1
// targ ind 2 = result storage #2
// targ ind 3 = result with re-mapped colors for display only
switch( m_nFlipState )
{
case 0:
m_pTexSrc = m_pInitialStateTexture->GetTexture();
m_pTexFinalTarg = mpFilterTarget[1];
m_pIntermediateTarg = mpFilterTarget[0];
m_pIntermediateSrc = mpTextureFiltered[0];
m_pOutputSrc = mpTextureFiltered[3];
m_pOutputTarg = mpFilterTarget[3];
break;
case 1:
m_pTexSrc = mpTextureFiltered[1];
m_pTexFinalTarg = mpFilterTarget[2];
m_pIntermediateTarg = mpFilterTarget[0];
m_pIntermediateSrc = mpTextureFiltered[0];
m_pOutputSrc = mpTextureFiltered[3];
m_pOutputTarg = mpFilterTarget[3];
break;
case 2:
m_pTexSrc = mpTextureFiltered[2];
m_pTexFinalTarg = mpFilterTarget[1];
m_pIntermediateTarg = mpFilterTarget[0];
m_pIntermediateSrc = mpTextureFiltered[0];
m_pOutputSrc = mpTextureFiltered[3];
m_pOutputTarg = mpFilterTarget[3];
break;
}
// Clear intermediate target
hr = m_pD3DDev->SetRenderTarget( m_pIntermediateTarg, NULL);
ASSERT_IF_FAILED( hr );
// Clear 1st target to small fraction of green and blue to offset the
// dependent lookup to the proper values
// 0xff / 16 = 0x10
hr = m_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0x0, 0x04, 0x02 ), 1.0, 0);
// even if wireframe mode, render to texture as solid
m_pD3DDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID );
/////////////////////////////////////////////////////////
// First, write the source texture into the blue channel
// I do this in preparation for a 2D dependent green-blue lookup
// into a "rules" texture which governs how old pixels spawn
// or die into new pixels.
// The logic for the game of life depends on 9 pixels: the source
// pixel and it's 8 neightbors. These are accumulated in three
// passes.
hr = m_pD3DDev->SetRenderTarget( m_pIntermediateTarg, NULL);
m_pD3DDev->SetPixelShader( 0 ); // no pixel shader
// Add into dest texture - texture cleared to small fraction of
// green and blue to offset the dependent lookup to the proper values
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
m_pD3DDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
m_pD3DDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
// Setup traditional pipeline to copy the white source pixels
// into just the blue of the dest:
m_pD3DDev->SetTexture(0, m_pTexSrc );
m_pD3DDev->SetRenderState( D3DRS_TEXTUREFACTOR, 0x000000FF ); // ARGB
m_pD3DDev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pD3DDev->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDev->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TFACTOR ); // blue only
m_pD3DDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pD3DDev->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
// Render using offsets of zero
offset.x = 0.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
// draw the fan with normal texture coords
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
/////////////////////////////////////////////////////////////////
// Setup neighbor blending modes:
hr = m_pD3DDev->SetPixelShader( m_dwEqualWeightCombineShader );
ASSERT_IF_FAILED( hr );
DWORD wrapval = m_bWrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP;
m_pD3DDev->SetTextureStageState(0, D3DTSS_ADDRESSU, wrapval);
m_pD3DDev->SetTextureStageState(0, D3DTSS_ADDRESSV, wrapval);
m_pD3DDev->SetTextureStageState(1, D3DTSS_ADDRESSU, wrapval);
m_pD3DDev->SetTextureStageState(1, D3DTSS_ADDRESSV, wrapval);
m_pD3DDev->SetTextureStageState(2, D3DTSS_ADDRESSU, wrapval);
m_pD3DDev->SetTextureStageState(2, D3DTSS_ADDRESSV, wrapval);
m_pD3DDev->SetTextureStageState(3, D3DTSS_ADDRESSU, wrapval);
m_pD3DDev->SetTextureStageState(3, D3DTSS_ADDRESSV, wrapval);
/////////////////////////////////////////////////////////////////
// Render 1st set of neighbors
// Add in nearest neighbors
hr = m_pD3DDev->SetRenderTarget( m_pIntermediateTarg, NULL);
ASSERT_IF_FAILED(hr);
// Add result of pixel operations into the dest texture:
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
m_pD3DDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
m_pD3DDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
// Render set of neighbors using bilinear filtering to sample
// equal weight of all 8 neighbors in just 4 texture samples
offset.x = 3.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
float pix_mult[4] = { 0.0f, 0.25f, 0.0f, 0.0f };
//@@
hr = m_pD3DDev->SetPixelShaderConstant( PCN_MULTFACTOR, &pix_mult, 1 );
ASSERT_IF_FAILED(hr);
m_pD3DDev->SetTexture(0, m_pTexSrc );
m_pD3DDev->SetTexture(1, m_pTexSrc );
m_pD3DDev->SetTexture(2, m_pTexSrc );
m_pD3DDev->SetTexture(3, m_pTexSrc );
for( i=0; i < 4; i++ )
{
m_pD3DDev->SetTextureStageState(i, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pD3DDev->SetTextureStageState(i, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
}
// draw the fan with displaced texture coordinates
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
for( i=0; i < 4; i++ )
{
m_pD3DDev->SetTextureStageState(i, D3DTSS_MAGFILTER, D3DTEXF_POINT );
m_pD3DDev->SetTextureStageState(i, D3DTSS_MINFILTER, D3DTEXF_POINT );
}
/*
/////////////////////////////////////////////////////////////////
// Render 2nd set of neighbors
// Add diagonal neighbors
hr = m_pD3DDev->SetRenderTarget( m_pIntermediateTarg, NULL);
ASSERT_IF_FAILED(hr);
// Don't clear - we're still adding the 8 neighbors
// Render 2nd using offsets set 2 - for diagonal pixels
offset.x = 2.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
m_pD3DDev->SetTexture(0, m_pTexSrc );
m_pD3DDev->SetTexture(1, m_pTexSrc );
m_pD3DDev->SetTexture(2, m_pTexSrc );
m_pD3DDev->SetTexture(3, m_pTexSrc );
// draw the fan with displaced texture coordinates
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
ASSERT_IF_FAILED(hr);
*/
// If wrapping was on, reset to clamp now for the rest of the
// rendering
if( m_bWrap )
{
m_pD3DDev->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(2, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(2, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(3, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
m_pD3DDev->SetTextureStageState(3, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
}
/////////////////////////////////////////////////////////////////
// Now do dependent 2D lookup into rules texture to set new
// source pixels;
hr = m_pD3DDev->SetPixelShader( m_dwDependentGB );
ASSERT_IF_FAILED( hr );
hr = m_pD3DDev->SetRenderTarget( m_pTexFinalTarg, NULL);
ASSERT_IF_FAILED(hr);
// Render using offsets of zero
offset.x = 0.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
m_pD3DDev->SetTexture(0, m_pIntermediateSrc );
m_pD3DDev->SetTexture(1, m_pRulesTexture->GetTexture() );
ASSERT_IF_FAILED( hr );
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
// draw the fan with displaced texture coordinates
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
ASSERT_IF_FAILED(hr);
/////////////////////////////////////////////////////////////////
// If further processing is requested, do 1 more dependent
// lookup into a colormap for display to the user only.
// This remapping doesn't affect the logic at all, it's only
// a prettier version for the user to see.
if( m_bFarther )
{
hr = m_pD3DDev->SetPixelShader( m_dwDependentGB );
ASSERT_IF_FAILED( hr );
hr = m_pD3DDev->SetRenderTarget( m_pOutputTarg, NULL);
ASSERT_IF_FAILED(hr);
// Render using offsets of zero
offset.x = 0.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
hr = m_pD3DDev->SetTexture(0, m_pIntermediateSrc );
hr = m_pD3DDev->SetTexture(1, m_pOutputMapTexture->GetTexture() );
ASSERT_IF_FAILED( hr );
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
// draw the fan with displaced texture coordinates
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
ASSERT_IF_FAILED(hr);
}
// Flip the state variable for the next round of rendering
switch( m_nFlipState )
{
case 0:
m_nFlipState = 1;
break;
case 1:
m_nFlipState = 2;
break;
case 2:
m_nFlipState = 1;
break;
}
return hr;
}
void CGameOfLife::CreateAndWriteUVOffsets(int width, int height)
{
// This sets vertex shader constants used to displace the
// source texture over several additive samples. This is
// used to accumulate neighboring texel information that we
// need to run the game - the 8 surrounding texels, and the
// single source texel which will either spawn or die in the
// next generation.
// Label the texels as follows, for a source texel "e" that
// we want to compute for the next generation:
//
// abc
// def
// ghi:
// first the easy one: no offsets for sampling center
// occupied or unoccupied
// Use index offset value 0.0 to access these in the
// vertex shader.
float noOffsetX[4] = { 0.0f, 0.0f, 0.0f, 0.0f};
float noOffsetY[4] = { 0.0f, 0.0f, 0.0f, 0.0f};
float kPerTexelWidth;
float kPerTexelHeight;
kPerTexelWidth = 1.0f/static_cast<float>(width);
kPerTexelHeight = 1.0f/static_cast<float>(height);
HRESULT hr;
// Change the multiplier to 2 or 3 to spread the texel sampling. This
// has the effect of running several independent "games" in neighboring
// texels, which do not affect eachother.
kPerTexelWidth *= 1; // 1 = no change - run standard game of life
kPerTexelHeight *= 1;
// Offset set 1: Nearest neighbors - b,d,f,h texels
// Use index offset 1.0 to access these
float type1OffsetX[4] = { - kPerTexelWidth,
kPerTexelWidth,
0.0f,
0.0f };
float type1OffsetY[4] = { 0.0f,
0.0f,
kPerTexelHeight,
- kPerTexelHeight };
// These are a,c,g,i texels == diagonal neightbors
// Use index offset 2.0 to use these
float type2OffsetX[4] = { - kPerTexelWidth,
- kPerTexelWidth,
kPerTexelWidth,
kPerTexelWidth };
float type2OffsetY[4] = { - kPerTexelHeight,
kPerTexelHeight,
kPerTexelHeight,
- kPerTexelHeight };
// These offsets are for use with bilinear filtering
// of the neighbors, to sample all 8 neighbors in
// one pass instead of two. Bilinear averages the
// two bordering texels, but the coordinate must be
// exactly on the texel border to make this work.
// [0] = on the border of the ab texels
// [1] = between cf texels
// [2] = between ih texels
// [3] = between gd texels
float woff, hoff;
woff = kPerTexelWidth/2.0f;
hoff = kPerTexelHeight/2.0f;
float type3OffsetX[4] = { - kPerTexelWidth/2.0f + woff,
kPerTexelWidth + woff,
kPerTexelWidth/2.0f + woff,
- kPerTexelWidth + woff };
float type3OffsetY[4] = { - kPerTexelHeight + hoff,
- kPerTexelHeight/2.0f + hoff,
kPerTexelHeight + hoff,
kPerTexelHeight/2.0f + hoff };
float type4OffsetX[4] = { 0.0f, 0.0f, 0.0f, 0.0f};
float type4OffsetY[4] = { 0.0f, 0.0f, 0.0f, 0.0f};
// write all these offsets to constant memory
for (int i = 0; i < 4; ++i)
{
D3DXVECTOR4 noOffset( noOffsetX[i], noOffsetY[i], 0.0f, 0.0f);
D3DXVECTOR4 type1Offset(type1OffsetX[i], type1OffsetY[i], 0.0f, 0.0f);
D3DXVECTOR4 type2Offset(type2OffsetX[i], type2OffsetY[i], 0.0f, 0.0f);
D3DXVECTOR4 type3Offset(type3OffsetX[i], type3OffsetY[i], 0.0f, 0.0f);
D3DXVECTOR4 type4Offset(type4OffsetX[i], type4OffsetY[i], 0.0f, 0.0f);
m_pD3DDev->SetVertexShaderConstant(CV_UV_T0_NO_OFFSET + 5*i, &noOffset, 1);
m_pD3DDev->SetVertexShaderConstant(CV_UV_T0_TYPE1 + 5*i, &type1Offset, 1);
m_pD3DDev->SetVertexShaderConstant(CV_UV_T0_TYPE2 + 5*i, &type2Offset, 1);
m_pD3DDev->SetVertexShaderConstant(CV_UV_T0_TYPE3 + 5*i, &type3Offset, 1);
m_pD3DDev->SetVertexShaderConstant(CV_UV_T0_TYPE4 + 5*i, &type4Offset, 1);
}
// Set pixel shader consts:
// This constant is a color mask - It is used to only keep the
// green component for the accumulation of neighbor texel info.
// Mult the source green by 1/8 to get the average of the 8 samples,
// with a max of 1.0f
/// R,G,B,A
float pix_mult[4] = { 0.0f, 0.125f, 0.0f, 0.0f };
hr = m_pD3DDev->SetPixelShaderConstant( PCN_MULTFACTOR, &pix_mult, 1 );
ASSERT_IF_FAILED(hr);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -