if ( !CreateShader("Shaders\\Glow.fx", 3, 3) ) { MessageBox(hWnd,"cEffects::CreateShader FAILED - RealTime Glow","projectSilicon",MB_ICONERROR); return(false); } if ( m_bShader20 ) m_uiGlowPasses = 3; // Glow Alpha Source + 2 Convultion Filter RT's else m_uiGlowPasses = 5; // Glow Alpha Source + 4 Convultion Filter RT's m_ptexRenderTarget = new LPDIRECT3DTEXTURE9[m_uiGlowPasses]; m_pRT = new IDirect3DSurface9*[m_uiGlowPasses]; // Glow Alpha Source + 2 Convultion Filter RT's for ( unsigned int uiX = 0 ; uiX < m_uiGlowPasses ; ++uiX ) { if ( FAILED ( m_pd3dDevice->CreateTexture( RTRES, RTRES, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_ptexRenderTarget[uiX], NULL ))) { MessageBox(hWnd,"cEffects::CreateTexture FAILED - Glow RenderTarget","projectSilicon",MB_ICONERROR); return(false); } m_ptexRenderTarget[uiX]->GetSurfaceLevel( 0, &m_pRT[uiX] ); } // Screen-sized quad // Create vertex declaration for post-process if( FAILED( m_pd3dDevice->CreateVertexDeclaration( vertDeclPP, &m_pVertDeclPP ) ) ) return(false); if ( FAILED ( m_pd3dDevice->CreateVertexBuffer( 4 * sizeof(PPVERT), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, D3DFVF_PPVERT, D3DPOOL_DEFAULT, &m_pQuadPostProcess, NULL ) ) ) return(false); PPVERT* pSrc(NULL); float fU_adjust = 0.5f / (float)m_cD3D.getPP().BackBufferWidth; float fV_adjust = 0.5f / (float)m_cD3D.getPP().BackBufferWidth; if ( SUCCEEDED (m_pQuadPostProcess->Lock(0,4*sizeof(PPVERT),(VOID**)&pSrc,0))) { for ( unsigned int i = 0; i < 4; ++i) { pSrc->Position = D3DXVECTOR3((i==0 || i==3) ? -1.0f : 1.0f, (i<2) ? -1.0f : 1.0f, 0.0f); pSrc->Tex = D3DXVECTOR2(((i==0 || i==3) ? 0.0f : 1.0f) + fU_adjust, ((i<2) ? 1.0f : 0.0f) + fV_adjust); pSrc++; } m_pQuadPostProcess->Unlock(); } else return(false); if ( !SetParameterOptions_RealTimeGlow( m_vcFXShader.back().m_pd3dxEffect, m_vcFXShader.back().m_pd3dxHandle, m_vcFXShader.back().m_pd3dxTechnique ) ) { MessageBox(hWnd,"cEffects::SetParameterOptions_RealTimeGlow FAILED - VertexSkinning","projectSilicon",MB_ICONERROR); return(false); }bool cEffects::SetParameterOptions_RealTimeGlow( LPD3DXEFFECT const& pCurEffect, D3DXHANDLE* const& pCurHandles, D3DXHANDLE* const& pCurTechniques ){ // Current Diffuse Texture for Alpha Mask Technique to Sample if ( !(pCurHandles[GLPRETEX] = pCurEffect->GetParameterByName(NULL, "tAlphaSource")) ) return(false); // Alpha Mask Texture Sampler if ( !(pCurHandles[GLPOSTTEX] = pCurEffect->GetParameterByName(NULL, "tPostProcess")) ) return(false); // ViewProj Matrix (Half Precision) if ( !(pCurHandles[GLVIEWPROJL] = pCurEffect->GetParameterByName(NULL, "uViewProjL")) ) return(false); CreateAndWriteUVOffsets(RTRES,RTRES,pCurEffect); // Alpha Mask - PreProcessing if ( !(pCurTechniques[GLTECHMASK] = pCurEffect->GetTechnique(GLTECHMASK)) ) return(false); if ( m_bShader20 ) { // Glow - PostProcessing (ps2.0) if ( !(pCurTechniques[GLTECHGLOW] = pCurEffect->GetTechnique(3)) ) return(false); } else { // Glow - PostProcessing (ps1.1) if ( !(pCurTechniques[GLTECHGLOW] = pCurEffect->GetTechnique(2)) ) return(false); } // Passthru Quad Simple Shader if ( !(pCurTechniques[GLTECHPASS] = pCurEffect->GetTechnique(GLTECHPASS)) ) return(false); m_pd3dDevice->GetRenderTarget(0,&m_pRTBackBuffer); m_pd3dDevice->GetDepthStencilSurface( &m_pStencilBackBuffer ); return(true);}void cEffects::CreateAndWriteUVOffsets(int width, int height, LPD3DXEFFECT const& pCurEffect){ // 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] = { -1 * kPerTexelWidth + eps, -1 * kPerTexelWidth + eps, 1.0f * kPerTexelWidth - eps, 1.0f * kPerTexelWidth - eps }; float const type2OffsetY[4] = { -1 * kPerTexelHeight+ eps, 1.0f * kPerTexelHeight- eps, 1.0f * kPerTexelHeight- eps, -1 * 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, noOffsetY, 0.0f, 0.0f); D3DXVECTOR4 type1Offset(type1OffsetX, type1OffsetY, 0.0f, 0.0f); D3DXVECTOR4 type2Offset(type2OffsetX, type2OffsetY, 0.0f, 0.0f); D3DXVECTOR4 type3Offset(type3OffsetX, type3OffsetY, 0.0f, 0.0f); D3DXVECTOR4 type4Offset(type4OffsetX, type4OffsetY, 0.0f, 0.0f); // helpful comment: // the first 4 UvBase vectors are the 4 texture stage u/v's for "no-offset" sampling. // the next 4 UvBase vectors are the 4 texture stage u/v's for 9-sample box filter sampling, // and so on. char str[64]; sprintf(str, "fUvBase[%d]", i ); pCurEffect->SetVector(str, &noOffset); sprintf(str, "fUvBase[%d]", i + 4); pCurEffect->SetVector(str, &type1Offset); sprintf(str, "fUvBase[%d]", i + 8); pCurEffect->SetVector(str, &type2Offset); sprintf(str, "fUvBase[%d]", i + 12); pCurEffect->SetVector(str, &type3Offset); sprintf(str, "fUvBase[%d]", i + 16); pCurEffect->SetVector(str, &type4Offset); }}void cEffects::PreProcessGlow(){ // Pre-Processing Alpha Mask rendering for Real-Time Glow ( Render to Texture ) m_pd3dDevice->SetRenderTarget(0,m_pRT[0]); m_pd3dDevice->SetDepthStencilSurface(NULL); m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L ); m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE ); m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); // Everything Else m_vcFXShader[GLOW].m_pd3dxEffect->SetTechnique(m_vcFXShader[GLOW].m_pd3dxTechnique[GLTECHMASK]); m_vcFXShader[GLOW].m_pd3dxEffect->Begin( NULL, D3DXFX_DONOTSAVESTATE|D3DXFX_DONOTSAVESHADERSTATE ); for ( unsigned int iD = 0 ; iD < m_cMesh.getModelsAnimRigid().size() ; ++iD ) m_cMesh.getModelsAnimRigid()[iD].RenderAlphaMask(); m_vcFXShader[GLOW].m_pd3dxEffect->End(); m_pd3dDevice->SetVertexShader(NULL); m_pd3dDevice->SetPixelShader(NULL); // Skinned Models // Set View Projection Matrix m_cEffects.getShader()[SKINNING].m_pd3dxEffect->SetMatrix( m_vcFXShader[SKINNING].m_pd3dxHandle[SKVIEWPROJL], &m_matViewProj ); m_vcFXShader[SKINNING].m_pd3dxEffect->SetTechnique(m_vcFXShader[SKINNING].m_pd3dxTechnique[SKTECHMASK]); m_vcFXShader[SKINNING].m_pd3dxEffect->Begin( NULL, D3DXFX_DONOTSAVESTATE|D3DXFX_DONOTSAVESHADERSTATE ); m_cMesh.getClient()->getSkinnedModel().RenderAlphaMask(); m_vcFXShader[SKINNING].m_pd3dxEffect->End(); m_pd3dDevice->SetVertexShader(NULL); m_pd3dDevice->SetPixelShader(NULL);}void cEffects::PostProcessGlow(){ // Post-Processing Alpha Mask rendering for Real-Time Glow ( Render to Texture ) requires ps2.0 m_pd3dDevice->SetVertexDeclaration(m_pVertDeclPP); m_pd3dDevice->SetStreamSource( 0, m_pQuadPostProcess, 0, sizeof(PPVERT) ); m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE ); m_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE,FALSE); m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); float fOffset = 4.0f; m_vcFXShader[GLOW].m_pd3dxEffect->SetValue("fUvOffsetToUse", &fOffset, sizeof(float)); m_vcFXShader[GLOW].m_pd3dxEffect->SetTechnique(m_vcFXShader[GLOW].m_pd3dxTechnique[GLTECHGLOW]); for ( unsigned int uiX = 1 ; uiX < m_uiGlowPasses ; ++uiX ) { m_pd3dDevice->SetRenderTarget(0,m_pRT[uiX]); m_pd3dDevice->SetDepthStencilSurface(NULL); m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L ); m_vcFXShader[GLOW].m_pd3dxEffect->SetTexture( m_vcFXShader[GLOW].m_pd3dxHandle[GLPOSTTEX], m_ptexRenderTarget[uiX-1] ); m_vcFXShader[GLOW].m_pd3dxEffect->Begin( NULL, 0 ); m_vcFXShader[GLOW].m_pd3dxEffect->BeginPass( 0 ); // Draw the Screen-Aligned Quad m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2); m_vcFXShader[GLOW].m_pd3dxEffect->EndPass(); m_vcFXShader[GLOW].m_pd3dxEffect->End(); } m_pd3dDevice->SetVertexShader(NULL); m_pd3dDevice->SetPixelShader(NULL); m_pd3dDevice->SetRenderTarget(0,m_pRTBackBuffer); m_pd3dDevice->SetDepthStencilSurface(m_pStencilBackBuffer); m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); m_vcFXShader[GLOW].m_pd3dxEffect->SetTechnique(m_vcFXShader[GLOW].m_pd3dxTechnique[GLTECHPASS]); for ( unsigned int uiX = 1 ; uiX < m_uiGlowPasses ; ++uiX ) { m_vcFXShader[GLOW].m_pd3dxEffect->SetTexture( m_vcFXShader[GLOW].m_pd3dxHandle[GLPOSTTEX], m_ptexRenderTarget[uiX] ); m_vcFXShader[GLOW].m_pd3dxEffect->Begin( NULL, 0 ); m_vcFXShader[GLOW].m_pd3dxEffect->BeginPass( 0 ); // Draw the Screen-Aligned Quad m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2); m_vcFXShader[GLOW].m_pd3dxEffect->EndPass(); m_vcFXShader[GLOW].m_pd3dxEffect->End(); } m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );}
And if anyone is interested I have uploaded the latest build, test it out, tell me if it works. Requires Shader Model 1.1 or greater.