📄 ca_waterwaves.cpp
字号:
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
ASSERT_IF_FAILED(hr);
}
HRESULT CA_Water::DoSingleTimeStep()
{
HRESULT hr;
int i;
// variable for writing to constant memory which uv-offsets to use
D3DXVECTOR4 offset(0.0f, 1.0f, 0.0f, 0.0f);
m_pD3DDev->SetVertexShaderConstant( CV_ONOFF_1, &offset, 1);
// Set force restoring values to equilibrium (0.5 = gray)
float eq_rest[4] = { m_fEqRestore_factor,m_fEqRestore_factor,m_fEqRestore_factor, 1.0f };
hr = m_pD3DDev->SetPixelShaderConstant( PCN_EQ_REST_FAC, &eq_rest, 1);
ASSERT_IF_FAILED(hr);
//////////////////////////////////////////////////////////
// Set constant for offseting values to 0.5 for gray = 0
m_fhalfoff = 0.500f;
float pix_offset[4] = { m_fhalfoff,m_fhalfoff,m_fhalfoff, 1.0f };
hr = m_pD3DDev->SetPixelShaderConstant( PCN_ADDHALF, &(pix_offset[0]) , 1);
ASSERT_IF_FAILED(hr);
// no need to clear temporary target
// even if wireframe mode, render to texture as solid
m_pD3DDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID );
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);
for(i = 0; i < 4; i++ )
{
m_pD3DDev->SetTextureStageState(i, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pD3DDev->SetTextureStageState(i, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
}
/////////////////////////////////////////////////////////
// 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);
ASSERT_IF_FAILED( hr );
hr = m_pD3DDev->SetPixelShader( m_dwNeighborForceCalc_1 );
ASSERT_IF_FAILED( hr );
// write first part of force calc
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
hr = m_pD3DDev->SetTexture(0, m_pTex_HeightSrc );
ASSERT_IF_FAILED(hr);
hr = m_pD3DDev->SetTexture(1, m_pTex_HeightSrc );
ASSERT_IF_FAILED(hr);
hr = m_pD3DDev->SetTexture(2, m_pTex_HeightSrc );
ASSERT_IF_FAILED(hr);
hr = m_pD3DDev->SetTexture(3, m_pTex_HeightSrc );
ASSERT_IF_FAILED(hr);
// Render using offset set 1
offset.x = 1.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
// draw the quad with texture coords doing difference of
// nearby texels for force calc.
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
////////////////////////////////////////////////////////////////
// Now add in last component of force for the 4th neighbor
// that we didn't have enough texture lookups to do in the
// first pass
// Additive blending to add the new component of force
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
m_pD3DDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
m_pD3DDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
//; t0 = center
//; t1 = 2nd axis final point
//; t2 = previous partial result texture sampled at center
m_pD3DDev->SetTexture(0, m_pTex_HeightSrc );
m_pD3DDev->SetTexture(1, m_pTex_HeightSrc );
m_pD3DDev->SetTexture(2, m_pIntermediateSrc );
m_pD3DDev->SetTexture(3, m_pTex_HeightSrc );
hr = m_pD3DDev->SetRenderTarget( m_pInt_FinalTarg, NULL);
ASSERT_IF_FAILED( hr );
hr = m_pD3DDev->SetPixelShader( m_dwNeighborForceCalc_2 );
ASSERT_IF_FAILED( hr );
offset.x = 2.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
/////////////////////////////////////////////////////////////////
// Apply the force with a scale factor to reduce it's magnitude.
// Add this to the current texture representing the water height.
hr = m_pD3DDev->SetPixelShader( m_dwApplyForceShader );
ASSERT_IF_FAILED( hr );
// use offsets of zero
offset.x = 0.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
m_pD3DDev->SetTexture(0, m_pTex_VelPrevSrc ); // 1st is offset to 0 with offsets 1.0
m_pD3DDev->SetTexture(1, m_pInt_FinalSrc );
m_pD3DDev->SetTexture(2, m_pInt_FinalSrc );
m_pD3DDev->SetTexture(3, m_pInt_FinalSrc );
hr = m_pD3DDev->SetRenderTarget( m_pTex_VelTarg, NULL);
ASSERT_IF_FAILED( hr );
// no more additive blending - math for signed values has to be
// done in the pixel shader
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
// Set constant at which to apply force to velocity
float pix_force_mult[4] = { m_fBlend, m_fBlend, m_fBlend, 1.0f }; // RGBA
hr = m_pD3DDev->SetPixelShaderConstant( PCN_MULTFACTOR_1, &pix_force_mult, 1 );
ASSERT_IF_FAILED(hr);
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
///////////////////////////////////////////////////////////////////
// With velocity texture selected, render new excitation droplets
// at random freq.
float rnd_freq;
rnd_freq = (float)rand()/((float)RAND_MAX);
if( m_fDropletFreq > rnd_freq )
{
// a drop falls - decide where
float x = (float)rand()/((float)RAND_MAX);
float y = (float)rand()/((float)RAND_MAX);
DrawDroplet( x,y, true );
}
//////////////////////////////////////////////////////////////////////
// Now apply velocity to position
hr = m_pD3DDev->SetRenderTarget( m_pTex_HeightTarg, NULL);
ASSERT_IF_FAILED( hr );
hr = m_pD3DDev->SetPixelShader( m_dwApplyVelocityShader );
ASSERT_IF_FAILED( hr );
m_pD3DDev->SetTexture(0, m_pTex_HeightSrc );
m_pD3DDev->SetTexture(1, m_pTex_VelCurSrc );
m_pD3DDev->SetTexture(2, 0);
m_pD3DDev->SetTexture(3, 0);
// no more additive blending - math for signed values has to be
// done in the pixel shader
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
// Set constant at which to apply velocity to height
float pix_vel_mult[4] = { m_fVelFactor, m_fVelFactor, m_fVelFactor, 1.0f }; // RGBA
hr = m_pD3DDev->SetPixelShaderConstant( PCN_MULTFACTOR_1, &pix_vel_mult, 1 );
ASSERT_IF_FAILED(hr);
// use offsets of zero
offset.x = 0.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
ASSERT_IF_FAILED(hr);
///////////////////////////////////////////////////////////////////
// blur positions to smooth noise & generaly dampen things
// degree of blur is controlled by magnitude of 4 neighbor texel
// offsets with bilinear on
// Swap height targ & src
int tmp;
tmp = m_nTex_HeightTarg;
m_nTex_HeightTarg = m_nTex_HeightSrc;
m_nTex_HeightSrc = tmp;
m_pTex_HeightTarg = mpFilterTarget[m_nTex_HeightTarg];
m_pTex_HeightSrc = mpTextureFiltered[m_nTex_HeightSrc];
// Now blur height
m_pD3DDev->SetTexture(0, m_pTex_HeightSrc );
m_pD3DDev->SetTexture(1, m_pTex_HeightSrc );
m_pD3DDev->SetTexture(2, m_pTex_HeightSrc );
m_pD3DDev->SetTexture(3, m_pTex_HeightSrc );
m_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, false );
hr = m_pD3DDev->SetRenderTarget( m_pTex_HeightTarg, NULL);
ASSERT_IF_FAILED( hr );
hr = m_pD3DDev->SetPixelShader( m_dwEqualWeight_PostMultShader );
ASSERT_IF_FAILED( hr );
offset.x = 3.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
ASSERT_IF_FAILED(hr);
////////////////////////////
// Draw fixed objects within the texture
if( m_bApplyInteriorBoundaries )
{
DrawInteriorBoundaryObjects();
}
///////////////////////////////////////////////////////////////////
// If selected, create a normal map from the height
if( m_bCreateNormalMap )
{
Do_CreateNormalMapSteps();
}
/////////////////////////////////////////////////////////////////
// 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 water simulation at all, it's only
// a prettier version for the user to see.
/*
if( m_bFarther )
{
hr = 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;
}
IDirect3DTexture8 * CA_Water::GetOutputTexture()
{
assert( mpTextureFiltered[m_nTex_NrmlTarg] != NULL );
return( mpTextureFiltered[m_nTex_NrmlTarg] );
}
void CA_Water::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 simulation.
// Label the texels as follows, with the source texel being
// rendered as "e"
// (0.0)
// abc
// def
// ghi
// (1,1)
m_fPerTexelWidth = 1.0f/static_cast<float>(width);
m_fPerTexelHeight = 1.0f/static_cast<float>(height);
// A multiplier for sampling beyond the 8 nearest neighbors
// For this, we don't use the multiplier, so it is just 1.0
m_fPerTexelWidth *= 1.0f; // 1 = no change - run standard game of life
m_fPerTexelHeight *= 1.0f;
// Half-texel width and height offsets for sampling from the
// exact center of a texel when rendering a full-coverage quad
// to a texture target. Without these, a full-coverage quad with
// texture coords from 0 to 1.0 would sample from the lower corner
// of each source texel in rendering to a texture target of the
// same resolution as the source texture.
float woff, hoff;
woff = m_fPerTexelWidth/2.0f;
hoff = m_fPerTexelHeight/2.0f;
// Offsets set 0 - all zeros for center of texel sampling.
// first the easy one: no offsets for sampling the center
// 'e' texel
// Use index offset value 0.0 to access these in the
// vertex shader.
float noOffsetX[4] = { 0.0f + woff, 0.0f + woff, 0.0f + woff, 0.0f + woff};
float noOffsetY[4] = { 0.0f + hoff, 0.0f + hoff, 0.0f + hoff, 0.0f + hoff};
// Offset set 1: For use with neighbor force pixel shader 1
// samples center with 0, +u, -u, and +v,
// ie the 'e','f','d', and 'h' texels
float type1OffsetX[4] = { 0.0f + woff,
- m_fPerTexelWidth + woff,
m_fPerTexelWidth + woff,
0.0f + woff };
float type1OffsetY[4] = { 0.0f + hoff,
0.0f + hoff,
0.0f + hoff,
m_fPerTexelHeight + hoff };
// Offset set 2: for use with neighbor force pixel shader 2
// samples center with 0, and -v texels
// ie the 'e' and 'b' texels
// This completes a pattern of sampling center texel and it's
// 4 nearest neighbors to run the height-based water simulation
// 3rd must be 0 0 to sample texel center from partial result
// texture.
float type2OffsetX[4] = { 0.0f + woff,
0.0f + woff,
0.0f + woff,
0.0f + woff };
float type2OffsetY[4] = { 0.0f + hoff,
- m_fPerTexelHeight + hoff,
0.0f + hoff,
0.0f + hoff };
// Now we change all that & instead of sampling 1 texel from center, we
// sample 1.5 texels from center and use bilinear sampling. This reduces
// some of the noise and high-frequency oscillation from sampling the
// exact texel neighbor only, giving smoother results in the height field
// and smoother water ripples
// Displacements 1.5 texels from center texel
//
// 0 = 0 0
// 1 = -s -t
// 2 = +s -t
// 3 = +s +t
// /*
type1OffsetX[0] = 0.0f + woff;
type1OffsetX[1] = - m_fPerTexelWidth ;
type1OffsetX[2] = m_fPerTexelWidth + 2.0f * woff;
type1OffsetX[3] = m_fPerTexelWidth + 2.0f * woff;
type1OffsetY[0] = 0.0f + hoff;
type1OffsetY[1] = - m_fPerTexelHeight;
type1OffsetY[2] = - m_fPerTexelHeight;
type1OffsetY[3] = m_fPerTexelHeight + 2.0f * woff;
// Displacements 1.5 texels from center texel
// 0 = 0 0
// 1 = -s +t
// 2 = 0 0
type2OffsetX[0] = 0.0f + woff;
type2OffsetX[1] = - m_fPerTexelWidth;
type2OffsetX[2] = 0.0f + woff;
type2OffsetX[3] = 0.0f + woff;
type2OffsetY[0] = 0.0f + hoff;
type2OffsetY[1] = m_fPerTexelHeight + 2.0f * hoff;
type2OffsetY[2] = 0.0f + hoff;
type2OffsetY[3] = 0.0f + hoff;
// */
// Offsets 3 are set in separate function
//////////////////////////////////////////////////////////////
// Nearest neighbor offsets:
float type4OffsetX[4] = { - m_fPerTexelWidth + woff,
m_fPerTexelWidth + woff,
0.0f + woff,
0.0f + woff };
float type4OffsetY[4] = { 0.0f + hoff,
0.0f + hoff,
m_fPerTexelHeight + hoff,
- m_fPerTexelHeight + hoff };
int i;
// write all these offsets to constant memory
for ( 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 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_TYPE4 + 5*i, &type4Offset, 1);
}
// Set offsets set 3.0 used to further filter the high-frequency
// oscillations
UpdateBlurVertOffset();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -