📄 filterblit.cpp
字号:
D3DXMATRIX matProj;
D3DXMATRIX matViewProj;
D3DXMATRIX matWorldViewProj;
// write to constant memory which uv-offsets to use
D3DXVECTOR4 offset(0.0f, 0.0f, 0.0f, 0.0f);
switch (meDisplayOption)
{
case BOX9_FILTER:
offset.x = 4.0f;
break;
case SHARPEN_FILTER:
offset.x = 3.0f;
break;
case BOX16_FILTER:
offset.x = 2.0f;
break;
default:
offset.x = 1.0f;
break;
}
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
// Disable culling
m_pD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
// set render state
m_pD3DDev->SetVertexShader(m_dwVertexShader);
m_pD3DDev->SetStreamSource(0, m_pVertexBuffer, sizeof(QuadVertex));
D3DXVECTOR3 const vEyePt = D3DXVECTOR3( 0.0f, 0.0f, -5.0f );
D3DXVECTOR3 const vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 const vUp = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
// Set World, View, Projection, and combination matrices.
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUp);
D3DXMatrixOrthoLH(&matProj, 4.0f, 4.0f, 0.2f, 20.0f);
D3DXMatrixMultiply(&matViewProj, &matView, &matProj);
// draw a single quad to texture:
// the quad covers the whole "screen" exactly
D3DXMatrixScaling(&matWorld, 2.0f, 2.0f, 1.0f);
D3DXMatrixMultiply(&matWorldViewProj, &matWorld, &matViewProj);
D3DXMatrixTranspose(&matWorldViewProj, &matWorldViewProj);
m_pD3DDev->SetVertexShaderConstant(CV_WORLDVIEWPROJ_0, &matWorldViewProj(0, 0), 4);
// turn on our special filtering pixel shader
if (meDisplayOption != LUMINANCE_EDGE)
m_pD3DDev->SetPixelShader( (meDisplayOption == SHARPEN_FILTER) ?
m_dwSharpenPixelShader
: m_dwBlurPixelShader);
// draw multiple passes
for (int i = 0; i < kMaxNumPasses; ++i)
{
hr = m_pD3DDev->SetRenderTarget(mpFilterTarget[i], NULL);
hr = m_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0xFF, 0x0, 0x0 ), 1.0, 0);
if (meDisplayOption == LUMINANCE_EDGE)
{
switch (i)
{
case 0:
m_pD3DDev->SetPixelShader(m_dwLuminancePixelShader);
m_pD3DDev->SetTexture(0, mpTextureToFilter->GetTexture());
m_pD3DDev->SetTexture(1, mpTextureToFilter->GetTexture());
m_pD3DDev->SetTexture(2, mpTextureToFilter->GetTexture());
m_pD3DDev->SetTexture(3, mpTextureToFilter->GetTexture());
break;
case 1:
m_pD3DDev->SetPixelShader(m_dwLuminanceSensitiveDiagEdgePixelShader);
break;
case 2:
m_pD3DDev->SetPixelShader(m_dwLuminanceDiagEdgePixelShader);
break;
default:
break;
}
}
else
{
switch (i)
{
case 0:
m_pD3DDev->SetTexture(0, mpTextureToFilter->GetTexture());
m_pD3DDev->SetTexture(1, mpTextureToFilter->GetTexture());
m_pD3DDev->SetTexture(2, mpTextureToFilter->GetTexture());
m_pD3DDev->SetTexture(3, mpTextureToFilter->GetTexture());
break;
default:
m_pD3DDev->SetTexture(0, mpTextureFiltered[i-1]);
m_pD3DDev->SetTexture(1, mpTextureFiltered[i-1]);
m_pD3DDev->SetTexture(2, mpTextureFiltered[i-1]);
m_pD3DDev->SetTexture(3, mpTextureFiltered[i-1]);
break;
}
}
// draw the fan with displaced texture coordinates
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
}
// then switch back to normal rendering
hr = m_pD3DDev->SetRenderTarget(mpBackbufferColor, mpBackbufferDepth);
hr = m_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 0xAA, 0xAA, 0xAA ), 1.0, 0);
// turn off pixel shading
m_pD3DDev->SetPixelShader(0);
// draw quad in upper left corner: original texture
D3DXMatrixTranslation(&matWorld, -1.0f, 1.0f, 0.0f);
D3DXMatrixMultiply(&matWorldViewProj, &matWorld, &matViewProj);
D3DXMatrixTranspose(&matWorldViewProj, &matWorldViewProj);
m_pD3DDev->SetVertexShaderConstant(CV_WORLDVIEWPROJ_0, &matWorldViewProj(0, 0), 4);
// reset offsets to 0 (ie no offsets)
offset.x = 0.0f;
m_pD3DDev->SetVertexShaderConstant(CV_UV_OFFSET_TO_USE, &offset, 1);
m_pD3DDev->SetTexture(0, mpTextureToFilter->GetTexture());
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
// draw quads in the other corners, use generated textures
for (int j = 0; j < 3; ++j)
{
D3DXMatrixTranslation(&matWorld, (j == 2) ? -1.0f : 1.0f,
(j == 0) ? 1.0f : -1.0,
0.0f);
D3DXMatrixMultiply(&matWorldViewProj, &matWorld, &matViewProj);
D3DXMatrixTranspose(&matWorldViewProj, &matWorldViewProj);
m_pD3DDev->SetVertexShaderConstant(CV_WORLDVIEWPROJ_0, &matWorldViewProj(0, 0), 4);
m_pD3DDev->SetTexture(0, mpTextureFiltered[j]);
hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
}
return hr;
}
HRESULT CFilterBlit::CreateTextureRenderTarget()
{
HRESULT hr;
// get a pointer to the current back-buffer (so we can restore it later)
m_pD3DDev->GetRenderTarget( &mpBackbufferColor );
m_pD3DDev->GetDepthStencilSurface( &mpBackbufferDepth );
_ASSERT( mpBackbufferColor != NULL );
_ASSERT( mpBackbufferDepth != NULL );
// get the description for the texture we want to filter
D3DSURFACE_DESC ddsd;
mpTextureToFilter->GetTexture()->GetLevelDesc(0, &ddsd);
// create new textures just like the current texture
for (int i = 0; i < kMaxNumPasses; ++i)
{
hr = m_pD3DDev->CreateTexture(ddsd.Width, ddsd.Height, 1,
D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT, &mpTextureFiltered[i]);
if (FAILED(hr))
{
m_strLastError = "Can't CreateTexture!\n";
_ASSERT(false);
return E_FAIL;
}
hr = mpTextureFiltered[i]->GetSurfaceLevel(0, &mpFilterTarget[i]);
if (FAILED(hr))
{
m_strLastError = "Can't Get to top-level texture!\n";
_ASSERT(false);
return E_FAIL;
}
// set our render target to the new and shiny textures without depth
hr = m_pD3DDev->SetRenderTarget(mpFilterTarget[i], NULL);
if (FAILED(hr))
{
m_strLastError = "Can't SetRenderTarget to new surface!\n";
_ASSERT(false);
return E_FAIL;
}
}
// switch back to conventional back-buffer
hr = m_pD3DDev->SetRenderTarget(mpBackbufferColor, mpBackbufferDepth);
if (FAILED(hr))
{
m_strLastError = "Can't SetRenderTarget to original back-buffer surfaces!\n";
_ASSERT(false);
return E_FAIL;
}
return S_OK;
}
void CFilterBlit::CreateAndWriteUVOffsets(int width, int height)
{
// displace texture-uvs so that the sample points on the
// texture describe
// i) a square around the texel to sample.
// the edges of the square are distance s from the center texel.
// Due to bilinear filtering and application of equal weights (1/4)
// in the pixel shader, the following filter is implemented for the 9 samples
// abc
// def
// ghi:
// filtered pixel = (s*s)/4 (a+c+g+i) + (s-s*s)/2 (b+d+f+h) + (1-s)^2 e
// Thus, choosing s = 0 means no filtering (also no offsets)
// s = 2/3 results in an equally weighted, 9-sample box-filter (and is called
// type4) and s = 1/2 results in a circular cone-filter (and is called type1).
// ii) a square around the texel to sample, so as to include sixteen texels:
// abcd
// efgh
// ijkl
// mnop
// Center texel is assumed to be "j", and offsets are made so that the texels
// are the combinations of (a, b, e, f), (c, d, g, h), (i, j, m, n), and
// (k, l, o, p)
// iii) A quad-sample filter:
// a
// b
// cde
// Center texel is "b" and sampled dead center. The second sample is
// dead-center "a", and the last two samples are interpolations between
// (c,d) and (d,e). Connecting the samples with the center pixel should
// produce three lines that measure the same angle (120 deg) between them.
// This sampling pattern may be rotated around "b".
// first the easy one: no offsets
float const noOffsetX[4] = { 0.0f, 0.0f, 0.0f, 0.0f};
float const noOffsetY[4] = { 0.0f, 0.0f, 0.0f, 0.0f};
float const kPerTexelWidth = 1.0f/static_cast<float>(width);
float const kPerTexelHeight = 1.0f/static_cast<float>(height);
float s = 0.5f;
float const eps = 10.0e-4f;
float const rotAngle1 = D3DXToRadian( 0.0f );
float const rotAngle2 = rotAngle1 + D3DXToRadian(120.0f);
float const rotAngle3 = rotAngle1 + D3DXToRadian(240.0f);
// Change filter kernel for 9-sample box filtering, but for edge-detection we are
// going to use interpolated texels. Why? Because we detect diagonal edges only
// and the vertical and horizontal filtering seems to help.
float const type1OffsetX[4] = { -s * kPerTexelWidth,
-s * kPerTexelWidth,
s * kPerTexelWidth,
s * kPerTexelWidth };
float const type1OffsetY[4] = { -s * kPerTexelHeight,
s * kPerTexelHeight,
s * kPerTexelHeight,
-s * kPerTexelHeight };
// we have to bring the 16 texel-sample-filter a bit closer to the center to avoid
// separation due to floating point inaccuracies.
float const type2OffsetX[4] = { -.5f * kPerTexelWidth + eps,
-.5f * kPerTexelWidth + eps,
1.5f * kPerTexelWidth - eps,
1.5f * kPerTexelWidth - eps };
float const type2OffsetY[4] = { -.5f * kPerTexelHeight+ eps,
1.5f * kPerTexelHeight- eps,
1.5f * kPerTexelHeight- eps,
-.5f * kPerTexelHeight+ eps };
float const type3OffsetX[4] = {0.0f, sinf(rotAngle1)*kPerTexelWidth,
sinf(rotAngle2)*kPerTexelWidth,
sinf(rotAngle3)*kPerTexelWidth };
float const type3OffsetY[4] = {0.0f, -cosf(rotAngle1)*kPerTexelHeight,
-cosf(rotAngle2)*kPerTexelHeight,
-cosf(rotAngle3)*kPerTexelHeight };
s = 2.0f/3.0f; // same as type 1, except s is different
float const type4OffsetX[4] = { -s * kPerTexelWidth,
-s * kPerTexelWidth,
s * kPerTexelWidth,
s * kPerTexelWidth };
float const type4OffsetY[4] = { -s * kPerTexelHeight,
s * kPerTexelHeight,
s * kPerTexelHeight,
-s * kPerTexelHeight };
// 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);
}
}
void CFilterBlit::Keyboard(DWORD dwKey, UINT nFlags, bool bDown)
{
unsigned int option = static_cast<unsigned int>(meDisplayOption);
eEBKeyAction Action = TranslateEffectKey(dwKey, nFlags, bDown);
switch ( Action )
{
case EB_HELP:
{
::MessageBoxEx( NULL, " Help : F1 - Help \n\n Home - Reset To Defaults \n\n W - Wireframe Toggle \n\n +/- - Change Filter Mode",
"Help", MB_ICONINFORMATION | MB_TASKMODAL, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ) );
}
break;
case EB_WIREFRAME:
{
mbWireFrame = !mbWireFrame;
m_dwEffectDirtyFlags |= EBEFFECT_DIRTY_PUBLICSTATE;
}
break;
case EB_RESET:
{
meDisplayOption = meInitDisplayOption;
mbWireFrame = false;
m_dwEffectDirtyFlags |= EBEFFECT_DIRTY_PUBLICSTATE;
}
break;
case EB_ADD:
if (++option >= NUM_FILTER_OPTIONS)
option = FIRST_FILTER_OPTION;
meDisplayOption = static_cast<eFilterOptions>(option);
break;
case EB_SUBTRACT:
if (option == FIRST_FILTER_OPTION)
option = NUM_FILTER_OPTIONS-1;
else
--option;
meDisplayOption = static_cast<eFilterOptions>(option);
break;
default:
break;
}
switch (meDisplayOption)
{
case CONE_FILTER:
case BOX9_FILTER:
case BOX16_FILTER:
m_strEffectPixelShader = GetFilePath("BlurBlit.nvp");
break;
case SHARPEN_FILTER:
m_strEffectPixelShader = GetFilePath("SharpenBlit.nvp");
break;
case LUMINANCE_EDGE:
m_strEffectPixelShader = GetFilePath("LuminanceDiagEdgeBlit.nvp");
break;
default:
;
}
m_dwEffectDirtyFlags |= EBEFFECT_DIRTY_PIXELSHADERS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -