From d43a67a2ff165fe7592c32aa5cb40bae29b80adf Mon Sep 17 00:00:00 2001 From: Matt Haynie Date: Fri, 27 Jan 2017 13:57:44 -0800 Subject: [PATCH] Glow Outlines: Improved performance and fixed "glow when occluded" and "glow when unoccluded" functionality. --- .../materials/debug/debugfbtexture1.vmt | 5 + mp/src/game/client/glow_outline_effect.cpp | 477 ++++++++++-------- mp/src/game/client/glow_outline_effect.h | 5 +- .../materials/debug/debugfbtexture1.vmt | 5 + .../materials/debug/debugfbtexture1.vmt | 5 + sp/src/game/client/glow_outline_effect.cpp | 477 ++++++++++-------- sp/src/game/client/glow_outline_effect.h | 5 +- 7 files changed, 577 insertions(+), 402 deletions(-) create mode 100644 mp/game/mod_hl2mp/materials/debug/debugfbtexture1.vmt create mode 100644 sp/game/mod_episodic/materials/debug/debugfbtexture1.vmt create mode 100644 sp/game/mod_hl2/materials/debug/debugfbtexture1.vmt diff --git a/mp/game/mod_hl2mp/materials/debug/debugfbtexture1.vmt b/mp/game/mod_hl2mp/materials/debug/debugfbtexture1.vmt new file mode 100644 index 00000000..ae97b388 --- /dev/null +++ b/mp/game/mod_hl2mp/materials/debug/debugfbtexture1.vmt @@ -0,0 +1,5 @@ +"unlitgeneric" +{ + "$basetexture" "_rt_FullFrameFB1" + "%noToolTexture" 1 +} diff --git a/mp/src/game/client/glow_outline_effect.cpp b/mp/src/game/client/glow_outline_effect.cpp index c8f35b90..8c73036d 100644 --- a/mp/src/game/client/glow_outline_effect.cpp +++ b/mp/src/game/client/glow_outline_effect.cpp @@ -17,8 +17,27 @@ #ifdef GLOWS_ENABLE +// If you've added IMatRenderContext::OverrideDepthFunc (see ::DrawGlowOccluded below), +// then you can enable this and have single-pass glows for "glow when occluded" outlines. +// ***PLEASE*** increment MATERIAL_SYSTEM_INTERFACE_VERSION when you add this! +#define ADDED_OVERRIDE_DEPTH_FUNC 0 + +// If you've fixed IMatRenderContext::CopyTextureToRenderTargetEx +// (see CGlowObjectManager::RenderGlowModels below), then you can enable this and have +// code that's a bit cleaner. Also, then you won't have to ship debug/debugfbtexture1. +#define FIXED_COPY_TEXTURE_TO_RENDER_TARGET 0 + ConVar glow_outline_effect_enable( "glow_outline_effect_enable", "1", FCVAR_ARCHIVE, "Enable entity outline glow effects." ); -ConVar glow_outline_effect_width( "glow_outline_width", "10.0f", FCVAR_CHEAT, "Width of glow outline effect in screen space." ); + +// This doesn't actually do anything. +ConVar glow_outline_effect_width( "glow_outline_width", "10.0f", FCVAR_CHEAT | FCVAR_UNREGISTERED, "Width of glow outline effect in screen space." ); + +// Not really necessary, but it's two different styles. I prefer style 0, but style 1 "closes off" partially occluded glows. +static ConVar glow_outline_effect_stencil_mode("glow_outline_effect_stencil_mode", "0", 0, + "\n\t0: Draws partially occluded glows in a more 3d-esque way, making them look more like they're actually surrounding the model." + "\n\t1: Draws partially occluded glows in a more 2d-esque way, which can make them more visible." + "\n\tSee https://i.imgur.com/OJQkXei.gif", + true, 0, true, 1); extern bool g_bDumpRenderTargets; // in viewpostprocess.cpp @@ -46,14 +65,18 @@ struct ShaderStencilState_t void SetStencilState( CMatRenderContextPtr &pRenderContext ) { - pRenderContext->SetStencilEnable( m_bEnable ); - pRenderContext->SetStencilFailOperation( m_FailOp ); - pRenderContext->SetStencilZFailOperation( m_ZFailOp ); - pRenderContext->SetStencilPassOperation( m_PassOp ); - pRenderContext->SetStencilCompareFunction( m_CompareFunc ); - pRenderContext->SetStencilReferenceValue( m_nReferenceValue ); - pRenderContext->SetStencilTestMask( m_nTestMask ); - pRenderContext->SetStencilWriteMask( m_nWriteMask ); + pRenderContext->SetStencilEnable(m_bEnable); + + if (m_bEnable) + { + pRenderContext->SetStencilFailOperation(m_FailOp); + pRenderContext->SetStencilZFailOperation(m_ZFailOp); + pRenderContext->SetStencilPassOperation(m_PassOp); + pRenderContext->SetStencilCompareFunction(m_CompareFunc); + pRenderContext->SetStencilReferenceValue(m_nReferenceValue); + pRenderContext->SetStencilTestMask(m_nTestMask); + pRenderContext->SetStencilWriteMask(m_nWriteMask); + } } }; @@ -74,240 +97,294 @@ void CGlowObjectManager::RenderGlowEffects( const CViewSetup *pSetup, int nSplit } } -static void SetRenderTargetAndViewPort( ITexture *rt, int w, int h ) +void CGlowObjectManager::DrawGlowAlways(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext) { - CMatRenderContextPtr pRenderContext( materials ); - pRenderContext->SetRenderTarget(rt); - pRenderContext->Viewport(0,0,w,h); -} - -void CGlowObjectManager::RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext ) -{ - //==========================================================================================// - // This renders solid pixels with the correct coloring for each object that needs the glow. // - // After this function returns, this image will then be blurred and added into the frame // - // buffer with the objects stenciled out. // - //==========================================================================================// - pRenderContext->PushRenderTargetAndViewport(); - - // Save modulation color and blend - Vector vOrigColor; - render->GetColorModulation( vOrigColor.Base() ); - float flOrigBlend = render->GetBlend(); - - // Get pointer to FullFrameFB - ITexture *pRtFullFrame = NULL; - pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET ); - - SetRenderTargetAndViewPort( pRtFullFrame, pSetup->width, pSetup->height ); - - pRenderContext->ClearColor3ub( 0, 0, 0 ); - pRenderContext->ClearBuffers( true, false, false ); - - // Set override material for glow color - IMaterial *pMatGlowColor = NULL; - - pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true ); - g_pStudioRender->ForcedMaterialOverride( pMatGlowColor ); - ShaderStencilState_t stencilState; - stencilState.m_bEnable = false; - stencilState.m_nReferenceValue = 0; - stencilState.m_nTestMask = 0xFF; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 1; stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_KEEP; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + stencilState.SetStencilState(pRenderContext); - stencilState.SetStencilState( pRenderContext ); - - //==================// - // Draw the objects // - //==================// - for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) + pRenderContext->OverrideDepthEnable(false, false); + render->SetBlend(1); + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) { - if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || !current.m_bRenderWhenUnoccluded) continue; - render->SetBlend( m_GlowObjectDefinitions[i].m_flGlowAlpha ); - Vector vGlowColor = m_GlowObjectDefinitions[i].m_vGlowColor * m_GlowObjectDefinitions[i].m_flGlowAlpha; - render->SetColorModulation( &vGlowColor[0] ); // This only sets rgb, not alpha + Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(vGlowColor.Base()); // This only sets rgb, not alpha - m_GlowObjectDefinitions[i].DrawModel(); - } + current.DrawModel(); + } +} - if ( g_bDumpRenderTargets ) +void CGlowObjectManager::DrawGlowOccluded(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext) +{ +#if ADDED_OVERRIDE_DEPTH_FUNC // Enable this when the TF2 team has added IMatRenderContext::OverrideDepthFunc or similar. + ShaderStencilState_t stencilState; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; + stencilState.m_PassOp = glow_outline_effect_stencil_mode.GetBool() ? STENCILOPERATION_KEEP : STENCILOPERATION_REPLACE; + stencilState.m_FailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + stencilState.SetStencilState(pRenderContext); + + pRenderContext->OverrideDepthEnable(true, false); + + // Not implemented, we need this feature to be able to do this in 1 pass. Otherwise, + // we'd have to do 2 passes, 1st to mark on the stencil where the depth test failed, + // 2nd to actually utilize that information and draw color there. + pRenderContext->OverrideDepthFunc(true, SHADER_DEPTHFUNC_NEARER); + + for (int i = 0; i < glowObjectDefinitions.Count(); i++) { - DumpTGAofRenderTarget( pSetup->width, pSetup->height, "GlowModels" ); + auto& current = glowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || current.m_bRenderWhenUnoccluded) + continue; + + render->SetBlend(current.m_flGlowAlpha); + Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(&vGlowColor[0]); // This only sets rgb, not alpha + + current.DrawModel(); } - g_pStudioRender->ForcedMaterialOverride( NULL ); - render->SetColorModulation( vOrigColor.Base() ); - render->SetBlend( flOrigBlend ); - - ShaderStencilState_t stencilStateDisable; - stencilStateDisable.m_bEnable = false; - stencilStateDisable.SetStencilState( pRenderContext ); + pRenderContext->OverrideDepthFunc(false, SHADER_DEPTHFUNC_NEAREROREQUAL) +#else // 2-pass as a proof of concept so I can take a nice screenshot. + pRenderContext->OverrideDepthEnable(true, false); - pRenderContext->PopRenderTargetAndViewport(); + ShaderStencilState_t stencilState; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 2; + stencilState.m_nWriteMask = 2; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; + stencilState.m_FailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = STENCILOPERATION_KEEP; + stencilState.SetStencilState(pRenderContext); + + // Draw depthtest-passing pixels to the stencil buffer + { + render->SetBlend(0); + pRenderContext->OverrideAlphaWriteEnable(true, false); + pRenderContext->OverrideColorWriteEnable(true, false); + + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) + { + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || current.m_bRenderWhenUnoccluded) + continue; + + current.DrawModel(); + } + } + + pRenderContext->OverrideAlphaWriteEnable(false, true); + pRenderContext->OverrideColorWriteEnable(false, true); + + pRenderContext->OverrideDepthEnable(false, false); + + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 3; + stencilState.m_nTestMask = 2; + stencilState.m_nWriteMask = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_NOTEQUAL; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; + stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + stencilState.m_FailOp = glow_outline_effect_stencil_mode.GetBool() ? STENCILOPERATION_KEEP : STENCILOPERATION_REPLACE; + stencilState.SetStencilState(pRenderContext); + + // Draw color+alpha, stenciling out pixels from the first pass + render->SetBlend(1); + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) + { + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || current.m_bRenderWhenUnoccluded) + continue; + + const Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(vGlowColor.Base()); // This only sets rgb, not alpha + + current.DrawModel(); + } +#endif +} + +void CGlowObjectManager::DrawGlowVisible(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext) +{ + ShaderStencilState_t stencilState; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; + stencilState.m_FailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = glow_outline_effect_stencil_mode.GetBool() ? STENCILOPERATION_KEEP : STENCILOPERATION_REPLACE; + + stencilState.SetStencilState(pRenderContext); + + pRenderContext->OverrideDepthEnable(true, false); + render->SetBlend(1); + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) + { + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || current.m_bRenderWhenOccluded || !current.m_bRenderWhenUnoccluded) + continue; + + Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(vGlowColor.Base()); // This only sets rgb, not alpha + + current.DrawModel(); + } } void CGlowObjectManager::ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h ) { - //=======================================================// - // Render objects into stencil buffer // - //=======================================================// - // Set override shader to the same simple shader we use to render the glow models - IMaterial *pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true ); - g_pStudioRender->ForcedMaterialOverride( pMatGlowColor ); + const PIXEvent pixEvent(pRenderContext, "ApplyEntityGlowEffects"); - ShaderStencilState_t stencilStateDisable; - stencilStateDisable.m_bEnable = false; - float flSavedBlend = render->GetBlend(); - - // Set alpha to 0 so we don't touch any color pixels - render->SetBlend( 0.0f ); - pRenderContext->OverrideDepthEnable( true, false ); - - int iNumGlowObjects = 0; - - for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) + // Optimization: only do all the framebuffer shuffling if there's at least one glow to be drawn { - if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) - continue; + bool atLeastOneGlow = false; - if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded || m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) { - if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) - { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 1; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_REPLACE; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + if (m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw(nSplitScreenSlot)) + continue; - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); - } - else if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded ) - { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 1; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_KEEP; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; - - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); - } - else if ( m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) - { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 2; - stencilState.m_nTestMask = 0x1; - stencilState.m_nWriteMask = 0x3; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_EQUAL; - stencilState.m_PassOp = STENCILOPERATION_INCRSAT; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; - - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); - } + atLeastOneGlow = true; + break; } - iNumGlowObjects++; + if (!atLeastOneGlow) + return; } - // Need to do a 2nd pass to warm stencil for objects which are rendered only when occluded - for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) - { - if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) - continue; + ITexture* const pRtFullFrameFB0 = materials->FindTexture("_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET); + ITexture* const pRtFullFrameFB1 = materials->FindTexture("_rt_FullFrameFB1", TEXTURE_GROUP_RENDER_TARGET); - if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && !m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) + pRenderContext->PushRenderTargetAndViewport(); + + // Set backbuffer + hardware depth as MRT 0. We CANNOT CHANGE RENDER TARGETS after this point!!! + // In CShaderAPIDx8::CreateDepthTexture all depth+stencil buffers are created with the "discard" + // flag set to TRUE. Not sure about OpenGL, but according to + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174356(v=vs.85).aspx, if you change + // the depth+stencil buffer away from a buffer that has discard=TRUE, the contents become garbage. + pRenderContext->SetRenderTargetEx(0, nullptr); + + // Save current backbuffer to _rt_FullFrameFB1 + pRenderContext->CopyRenderTargetToTexture(pRtFullFrameFB1); + + // Clear backbuffer color and stencil, keep depth for testing + pRenderContext->ClearColor4ub(0, 0, 0, 0); + pRenderContext->ClearBuffers(true, false, true); + + // Draw glow models + { + // Save modulation color and blend + Vector vOrigColor; + render->GetColorModulation(vOrigColor.Base()); + const float flOrigBlend = render->GetBlend(); + + // Set override material for glow color + g_pStudioRender->ForcedMaterialOverride(materials->FindMaterial("dev/glow_color", TEXTURE_GROUP_OTHER, true)); + pRenderContext->OverrideColorWriteEnable(true, true); + pRenderContext->OverrideAlphaWriteEnable(true, true); + + // Draw "glow when visible" objects + DrawGlowVisible(nSplitScreenSlot, pRenderContext); + + // Draw "glow when occluded" objects + DrawGlowOccluded(nSplitScreenSlot, pRenderContext); + + // Draw "glow always" objects + DrawGlowAlways(nSplitScreenSlot, pRenderContext); + + // Unset override material + g_pStudioRender->ForcedMaterialOverride(NULL); + + // Restore modulation color and blend + render->SetColorModulation(vOrigColor.Base()); + render->SetBlend(flOrigBlend); + pRenderContext->OverrideDepthEnable(false, false); + } + + // Copy MSAA'd glow models to _rt_FullFrameFB0 + pRenderContext->CopyRenderTargetToTexture(pRtFullFrameFB0); + + // Move original contents of the backbuffer from _rt_FullFrameFB1 to the backbuffer + { +#if FIXED_COPY_TEXTURE_TO_RENDER_TARGET // Coordinates don't seem to be mapped 1:1 properly, screen becomes slightly blurry + pRenderContext->CopyTextureToRenderTargetEx(0, pRtFullFrameFB1, nullptr); +#else + pRenderContext->SetStencilEnable(false); + + IMaterial* const pFullFrameFB1 = materials->FindMaterial("debug/debugfbtexture1", TEXTURE_GROUP_RENDER_TARGET); + pFullFrameFB1->AddRef(); + pRenderContext->Bind(pFullFrameFB1); + + const int nSrcWidth = pSetup->width; + const int nSrcHeight = pSetup->height; + int nViewportX, nViewportY, nViewportWidth, nViewportHeight; + pRenderContext->GetViewport(nViewportX, nViewportY, nViewportWidth, nViewportHeight); + + pRenderContext->OverrideDepthEnable(true, false); { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 2; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_REPLACE; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_KEEP; - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); + pRenderContext->DrawScreenSpaceRectangle(pFullFrameFB1, + 0, 0, nViewportWidth, nViewportHeight, + 0, 0, nSrcWidth - 1, nSrcHeight - 1, + pRtFullFrameFB1->GetActualWidth(), pRtFullFrameFB1->GetActualHeight()); } + pRenderContext->OverrideDepthEnable(false, false); + + pFullFrameFB1->Release(); +#endif } - pRenderContext->OverrideDepthEnable( false, false ); - render->SetBlend( flSavedBlend ); - stencilStateDisable.SetStencilState( pRenderContext ); - g_pStudioRender->ForcedMaterialOverride( NULL ); - - // If there aren't any objects to glow, don't do all this other stuff - // this fixes a bug where if there are glow objects in the list, but none of them are glowing, - // the whole screen blooms. - if ( iNumGlowObjects <= 0 ) - return; - - //============================================= - // Render the glow colors to _rt_FullFrameFB - //============================================= + // Bloom glow models from _rt_FullFrameFB0 to backbuffer while stenciling out inside of models { - PIXEvent pixEvent( pRenderContext, "RenderGlowModels" ); - RenderGlowModels( pSetup, nSplitScreenSlot, pRenderContext ); - } - - // Get viewport - int nSrcWidth = pSetup->width; - int nSrcHeight = pSetup->height; - int nViewportX, nViewportY, nViewportWidth, nViewportHeight; - pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight ); - - // Get material and texture pointers - ITexture *pRtQuarterSize1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); - - { - //=======================================================================================================// - // At this point, pRtQuarterSize0 is filled with the fully colored glow around everything as solid glowy // - // blobs. Now we need to stencil out the original objects by only writing pixels that have no // - // stencil bits set in the range we care about. // - //=======================================================================================================// - IMaterial *pMatHaloAddToScreen = materials->FindMaterial( "dev/halo_add_to_screen", TEXTURE_GROUP_OTHER, true ); - - // Do not fade the glows out at all (weight = 1.0) - IMaterialVar *pDimVar = pMatHaloAddToScreen->FindVar( "$C0_X", NULL ); - pDimVar->SetFloatValue( 1.0f ); - // Set stencil state ShaderStencilState_t stencilState; stencilState.m_bEnable = true; - stencilState.m_nWriteMask = 0x0; // We're not changing stencil - stencilState.m_nTestMask = 0xFF; - stencilState.m_nReferenceValue = 0x0; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_EQUAL; + stencilState.m_nWriteMask = 0; // We're not changing stencil + stencilState.m_nReferenceValue = 1; + stencilState.m_nTestMask = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_NOTEQUAL; stencilState.m_PassOp = STENCILOPERATION_KEEP; stencilState.m_FailOp = STENCILOPERATION_KEEP; stencilState.m_ZFailOp = STENCILOPERATION_KEEP; - stencilState.SetStencilState( pRenderContext ); + stencilState.SetStencilState(pRenderContext); + + ITexture* const pRtQuarterSize1 = materials->FindTexture("_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET); + IMaterial* const pMatHaloAddToScreen = materials->FindMaterial("dev/halo_add_to_screen", TEXTURE_GROUP_OTHER, true); + + // Write to alpha + pRenderContext->OverrideAlphaWriteEnable(true, true); + + const int nSrcWidth = pSetup->width; + const int nSrcHeight = pSetup->height; + int nViewportX, nViewportY, nViewportWidth, nViewportHeight; + pRenderContext->GetViewport(nViewportX, nViewportY, nViewportWidth, nViewportHeight); // Draw quad - pRenderContext->DrawScreenSpaceRectangle( pMatHaloAddToScreen, 0, 0, nViewportWidth, nViewportHeight, - 0.0f, -0.5f, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, + pRenderContext->DrawScreenSpaceRectangle(pMatHaloAddToScreen, + 0, 0, nViewportWidth, nViewportHeight, + 0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, pRtQuarterSize1->GetActualWidth(), - pRtQuarterSize1->GetActualHeight() ); - - stencilStateDisable.SetStencilState( pRenderContext ); + pRtQuarterSize1->GetActualHeight()); } + + // Done with all of our "advanced" 3D rendering. + pRenderContext->SetStencilEnable(false); + pRenderContext->OverrideColorWriteEnable(false, false); + pRenderContext->OverrideAlphaWriteEnable(false, false); + pRenderContext->OverrideDepthEnable(false, false); + + pRenderContext->PopRenderTargetAndViewport(); } void CGlowObjectManager::GlowObjectDefinition_t::DrawModel() diff --git a/mp/src/game/client/glow_outline_effect.h b/mp/src/game/client/glow_outline_effect.h index aac399d7..1747f0a8 100644 --- a/mp/src/game/client/glow_outline_effect.h +++ b/mp/src/game/client/glow_outline_effect.h @@ -117,7 +117,6 @@ public: private: - void RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext ); void ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h ); struct GlowObjectDefinition_t @@ -150,6 +149,10 @@ private: static const int ENTRY_IN_USE = -2; }; + void DrawGlowVisible(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext); + void DrawGlowOccluded(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext); + void DrawGlowAlways(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext); + CUtlVector< GlowObjectDefinition_t > m_GlowObjectDefinitions; int m_nFirstFreeSlot; }; diff --git a/sp/game/mod_episodic/materials/debug/debugfbtexture1.vmt b/sp/game/mod_episodic/materials/debug/debugfbtexture1.vmt new file mode 100644 index 00000000..ae97b388 --- /dev/null +++ b/sp/game/mod_episodic/materials/debug/debugfbtexture1.vmt @@ -0,0 +1,5 @@ +"unlitgeneric" +{ + "$basetexture" "_rt_FullFrameFB1" + "%noToolTexture" 1 +} diff --git a/sp/game/mod_hl2/materials/debug/debugfbtexture1.vmt b/sp/game/mod_hl2/materials/debug/debugfbtexture1.vmt new file mode 100644 index 00000000..ae97b388 --- /dev/null +++ b/sp/game/mod_hl2/materials/debug/debugfbtexture1.vmt @@ -0,0 +1,5 @@ +"unlitgeneric" +{ + "$basetexture" "_rt_FullFrameFB1" + "%noToolTexture" 1 +} diff --git a/sp/src/game/client/glow_outline_effect.cpp b/sp/src/game/client/glow_outline_effect.cpp index c8f35b90..8c73036d 100644 --- a/sp/src/game/client/glow_outline_effect.cpp +++ b/sp/src/game/client/glow_outline_effect.cpp @@ -17,8 +17,27 @@ #ifdef GLOWS_ENABLE +// If you've added IMatRenderContext::OverrideDepthFunc (see ::DrawGlowOccluded below), +// then you can enable this and have single-pass glows for "glow when occluded" outlines. +// ***PLEASE*** increment MATERIAL_SYSTEM_INTERFACE_VERSION when you add this! +#define ADDED_OVERRIDE_DEPTH_FUNC 0 + +// If you've fixed IMatRenderContext::CopyTextureToRenderTargetEx +// (see CGlowObjectManager::RenderGlowModels below), then you can enable this and have +// code that's a bit cleaner. Also, then you won't have to ship debug/debugfbtexture1. +#define FIXED_COPY_TEXTURE_TO_RENDER_TARGET 0 + ConVar glow_outline_effect_enable( "glow_outline_effect_enable", "1", FCVAR_ARCHIVE, "Enable entity outline glow effects." ); -ConVar glow_outline_effect_width( "glow_outline_width", "10.0f", FCVAR_CHEAT, "Width of glow outline effect in screen space." ); + +// This doesn't actually do anything. +ConVar glow_outline_effect_width( "glow_outline_width", "10.0f", FCVAR_CHEAT | FCVAR_UNREGISTERED, "Width of glow outline effect in screen space." ); + +// Not really necessary, but it's two different styles. I prefer style 0, but style 1 "closes off" partially occluded glows. +static ConVar glow_outline_effect_stencil_mode("glow_outline_effect_stencil_mode", "0", 0, + "\n\t0: Draws partially occluded glows in a more 3d-esque way, making them look more like they're actually surrounding the model." + "\n\t1: Draws partially occluded glows in a more 2d-esque way, which can make them more visible." + "\n\tSee https://i.imgur.com/OJQkXei.gif", + true, 0, true, 1); extern bool g_bDumpRenderTargets; // in viewpostprocess.cpp @@ -46,14 +65,18 @@ struct ShaderStencilState_t void SetStencilState( CMatRenderContextPtr &pRenderContext ) { - pRenderContext->SetStencilEnable( m_bEnable ); - pRenderContext->SetStencilFailOperation( m_FailOp ); - pRenderContext->SetStencilZFailOperation( m_ZFailOp ); - pRenderContext->SetStencilPassOperation( m_PassOp ); - pRenderContext->SetStencilCompareFunction( m_CompareFunc ); - pRenderContext->SetStencilReferenceValue( m_nReferenceValue ); - pRenderContext->SetStencilTestMask( m_nTestMask ); - pRenderContext->SetStencilWriteMask( m_nWriteMask ); + pRenderContext->SetStencilEnable(m_bEnable); + + if (m_bEnable) + { + pRenderContext->SetStencilFailOperation(m_FailOp); + pRenderContext->SetStencilZFailOperation(m_ZFailOp); + pRenderContext->SetStencilPassOperation(m_PassOp); + pRenderContext->SetStencilCompareFunction(m_CompareFunc); + pRenderContext->SetStencilReferenceValue(m_nReferenceValue); + pRenderContext->SetStencilTestMask(m_nTestMask); + pRenderContext->SetStencilWriteMask(m_nWriteMask); + } } }; @@ -74,240 +97,294 @@ void CGlowObjectManager::RenderGlowEffects( const CViewSetup *pSetup, int nSplit } } -static void SetRenderTargetAndViewPort( ITexture *rt, int w, int h ) +void CGlowObjectManager::DrawGlowAlways(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext) { - CMatRenderContextPtr pRenderContext( materials ); - pRenderContext->SetRenderTarget(rt); - pRenderContext->Viewport(0,0,w,h); -} - -void CGlowObjectManager::RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext ) -{ - //==========================================================================================// - // This renders solid pixels with the correct coloring for each object that needs the glow. // - // After this function returns, this image will then be blurred and added into the frame // - // buffer with the objects stenciled out. // - //==========================================================================================// - pRenderContext->PushRenderTargetAndViewport(); - - // Save modulation color and blend - Vector vOrigColor; - render->GetColorModulation( vOrigColor.Base() ); - float flOrigBlend = render->GetBlend(); - - // Get pointer to FullFrameFB - ITexture *pRtFullFrame = NULL; - pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET ); - - SetRenderTargetAndViewPort( pRtFullFrame, pSetup->width, pSetup->height ); - - pRenderContext->ClearColor3ub( 0, 0, 0 ); - pRenderContext->ClearBuffers( true, false, false ); - - // Set override material for glow color - IMaterial *pMatGlowColor = NULL; - - pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true ); - g_pStudioRender->ForcedMaterialOverride( pMatGlowColor ); - ShaderStencilState_t stencilState; - stencilState.m_bEnable = false; - stencilState.m_nReferenceValue = 0; - stencilState.m_nTestMask = 0xFF; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 1; stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_KEEP; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + stencilState.SetStencilState(pRenderContext); - stencilState.SetStencilState( pRenderContext ); - - //==================// - // Draw the objects // - //==================// - for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) + pRenderContext->OverrideDepthEnable(false, false); + render->SetBlend(1); + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) { - if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || !current.m_bRenderWhenUnoccluded) continue; - render->SetBlend( m_GlowObjectDefinitions[i].m_flGlowAlpha ); - Vector vGlowColor = m_GlowObjectDefinitions[i].m_vGlowColor * m_GlowObjectDefinitions[i].m_flGlowAlpha; - render->SetColorModulation( &vGlowColor[0] ); // This only sets rgb, not alpha + Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(vGlowColor.Base()); // This only sets rgb, not alpha - m_GlowObjectDefinitions[i].DrawModel(); - } + current.DrawModel(); + } +} - if ( g_bDumpRenderTargets ) +void CGlowObjectManager::DrawGlowOccluded(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext) +{ +#if ADDED_OVERRIDE_DEPTH_FUNC // Enable this when the TF2 team has added IMatRenderContext::OverrideDepthFunc or similar. + ShaderStencilState_t stencilState; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; + stencilState.m_PassOp = glow_outline_effect_stencil_mode.GetBool() ? STENCILOPERATION_KEEP : STENCILOPERATION_REPLACE; + stencilState.m_FailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + stencilState.SetStencilState(pRenderContext); + + pRenderContext->OverrideDepthEnable(true, false); + + // Not implemented, we need this feature to be able to do this in 1 pass. Otherwise, + // we'd have to do 2 passes, 1st to mark on the stencil where the depth test failed, + // 2nd to actually utilize that information and draw color there. + pRenderContext->OverrideDepthFunc(true, SHADER_DEPTHFUNC_NEARER); + + for (int i = 0; i < glowObjectDefinitions.Count(); i++) { - DumpTGAofRenderTarget( pSetup->width, pSetup->height, "GlowModels" ); + auto& current = glowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || current.m_bRenderWhenUnoccluded) + continue; + + render->SetBlend(current.m_flGlowAlpha); + Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(&vGlowColor[0]); // This only sets rgb, not alpha + + current.DrawModel(); } - g_pStudioRender->ForcedMaterialOverride( NULL ); - render->SetColorModulation( vOrigColor.Base() ); - render->SetBlend( flOrigBlend ); - - ShaderStencilState_t stencilStateDisable; - stencilStateDisable.m_bEnable = false; - stencilStateDisable.SetStencilState( pRenderContext ); + pRenderContext->OverrideDepthFunc(false, SHADER_DEPTHFUNC_NEAREROREQUAL) +#else // 2-pass as a proof of concept so I can take a nice screenshot. + pRenderContext->OverrideDepthEnable(true, false); - pRenderContext->PopRenderTargetAndViewport(); + ShaderStencilState_t stencilState; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 2; + stencilState.m_nWriteMask = 2; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; + stencilState.m_FailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = STENCILOPERATION_KEEP; + stencilState.SetStencilState(pRenderContext); + + // Draw depthtest-passing pixels to the stencil buffer + { + render->SetBlend(0); + pRenderContext->OverrideAlphaWriteEnable(true, false); + pRenderContext->OverrideColorWriteEnable(true, false); + + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) + { + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || current.m_bRenderWhenUnoccluded) + continue; + + current.DrawModel(); + } + } + + pRenderContext->OverrideAlphaWriteEnable(false, true); + pRenderContext->OverrideColorWriteEnable(false, true); + + pRenderContext->OverrideDepthEnable(false, false); + + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 3; + stencilState.m_nTestMask = 2; + stencilState.m_nWriteMask = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_NOTEQUAL; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; + stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + stencilState.m_FailOp = glow_outline_effect_stencil_mode.GetBool() ? STENCILOPERATION_KEEP : STENCILOPERATION_REPLACE; + stencilState.SetStencilState(pRenderContext); + + // Draw color+alpha, stenciling out pixels from the first pass + render->SetBlend(1); + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) + { + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || !current.m_bRenderWhenOccluded || current.m_bRenderWhenUnoccluded) + continue; + + const Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(vGlowColor.Base()); // This only sets rgb, not alpha + + current.DrawModel(); + } +#endif +} + +void CGlowObjectManager::DrawGlowVisible(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext) +{ + ShaderStencilState_t stencilState; + stencilState.m_bEnable = true; + stencilState.m_nReferenceValue = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; + stencilState.m_PassOp = STENCILOPERATION_REPLACE; + stencilState.m_FailOp = STENCILOPERATION_KEEP; + stencilState.m_ZFailOp = glow_outline_effect_stencil_mode.GetBool() ? STENCILOPERATION_KEEP : STENCILOPERATION_REPLACE; + + stencilState.SetStencilState(pRenderContext); + + pRenderContext->OverrideDepthEnable(true, false); + render->SetBlend(1); + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) + { + auto& current = m_GlowObjectDefinitions[i]; + if (current.IsUnused() || !current.ShouldDraw(nSplitScreenSlot) || current.m_bRenderWhenOccluded || !current.m_bRenderWhenUnoccluded) + continue; + + Vector vGlowColor = current.m_vGlowColor * current.m_flGlowAlpha; + render->SetColorModulation(vGlowColor.Base()); // This only sets rgb, not alpha + + current.DrawModel(); + } } void CGlowObjectManager::ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h ) { - //=======================================================// - // Render objects into stencil buffer // - //=======================================================// - // Set override shader to the same simple shader we use to render the glow models - IMaterial *pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true ); - g_pStudioRender->ForcedMaterialOverride( pMatGlowColor ); + const PIXEvent pixEvent(pRenderContext, "ApplyEntityGlowEffects"); - ShaderStencilState_t stencilStateDisable; - stencilStateDisable.m_bEnable = false; - float flSavedBlend = render->GetBlend(); - - // Set alpha to 0 so we don't touch any color pixels - render->SetBlend( 0.0f ); - pRenderContext->OverrideDepthEnable( true, false ); - - int iNumGlowObjects = 0; - - for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) + // Optimization: only do all the framebuffer shuffling if there's at least one glow to be drawn { - if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) - continue; + bool atLeastOneGlow = false; - if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded || m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) + for (int i = 0; i < m_GlowObjectDefinitions.Count(); i++) { - if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) - { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 1; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_REPLACE; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; + if (m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw(nSplitScreenSlot)) + continue; - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); - } - else if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded ) - { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 1; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_KEEP; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; - - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); - } - else if ( m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) - { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 2; - stencilState.m_nTestMask = 0x1; - stencilState.m_nWriteMask = 0x3; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_EQUAL; - stencilState.m_PassOp = STENCILOPERATION_INCRSAT; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; - - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); - } + atLeastOneGlow = true; + break; } - iNumGlowObjects++; + if (!atLeastOneGlow) + return; } - // Need to do a 2nd pass to warm stencil for objects which are rendered only when occluded - for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) - { - if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) - continue; + ITexture* const pRtFullFrameFB0 = materials->FindTexture("_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET); + ITexture* const pRtFullFrameFB1 = materials->FindTexture("_rt_FullFrameFB1", TEXTURE_GROUP_RENDER_TARGET); - if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && !m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) + pRenderContext->PushRenderTargetAndViewport(); + + // Set backbuffer + hardware depth as MRT 0. We CANNOT CHANGE RENDER TARGETS after this point!!! + // In CShaderAPIDx8::CreateDepthTexture all depth+stencil buffers are created with the "discard" + // flag set to TRUE. Not sure about OpenGL, but according to + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174356(v=vs.85).aspx, if you change + // the depth+stencil buffer away from a buffer that has discard=TRUE, the contents become garbage. + pRenderContext->SetRenderTargetEx(0, nullptr); + + // Save current backbuffer to _rt_FullFrameFB1 + pRenderContext->CopyRenderTargetToTexture(pRtFullFrameFB1); + + // Clear backbuffer color and stencil, keep depth for testing + pRenderContext->ClearColor4ub(0, 0, 0, 0); + pRenderContext->ClearBuffers(true, false, true); + + // Draw glow models + { + // Save modulation color and blend + Vector vOrigColor; + render->GetColorModulation(vOrigColor.Base()); + const float flOrigBlend = render->GetBlend(); + + // Set override material for glow color + g_pStudioRender->ForcedMaterialOverride(materials->FindMaterial("dev/glow_color", TEXTURE_GROUP_OTHER, true)); + pRenderContext->OverrideColorWriteEnable(true, true); + pRenderContext->OverrideAlphaWriteEnable(true, true); + + // Draw "glow when visible" objects + DrawGlowVisible(nSplitScreenSlot, pRenderContext); + + // Draw "glow when occluded" objects + DrawGlowOccluded(nSplitScreenSlot, pRenderContext); + + // Draw "glow always" objects + DrawGlowAlways(nSplitScreenSlot, pRenderContext); + + // Unset override material + g_pStudioRender->ForcedMaterialOverride(NULL); + + // Restore modulation color and blend + render->SetColorModulation(vOrigColor.Base()); + render->SetBlend(flOrigBlend); + pRenderContext->OverrideDepthEnable(false, false); + } + + // Copy MSAA'd glow models to _rt_FullFrameFB0 + pRenderContext->CopyRenderTargetToTexture(pRtFullFrameFB0); + + // Move original contents of the backbuffer from _rt_FullFrameFB1 to the backbuffer + { +#if FIXED_COPY_TEXTURE_TO_RENDER_TARGET // Coordinates don't seem to be mapped 1:1 properly, screen becomes slightly blurry + pRenderContext->CopyTextureToRenderTargetEx(0, pRtFullFrameFB1, nullptr); +#else + pRenderContext->SetStencilEnable(false); + + IMaterial* const pFullFrameFB1 = materials->FindMaterial("debug/debugfbtexture1", TEXTURE_GROUP_RENDER_TARGET); + pFullFrameFB1->AddRef(); + pRenderContext->Bind(pFullFrameFB1); + + const int nSrcWidth = pSetup->width; + const int nSrcHeight = pSetup->height; + int nViewportX, nViewportY, nViewportWidth, nViewportHeight; + pRenderContext->GetViewport(nViewportX, nViewportY, nViewportWidth, nViewportHeight); + + pRenderContext->OverrideDepthEnable(true, false); { - ShaderStencilState_t stencilState; - stencilState.m_bEnable = true; - stencilState.m_nReferenceValue = 2; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; - stencilState.m_PassOp = STENCILOPERATION_REPLACE; - stencilState.m_FailOp = STENCILOPERATION_KEEP; - stencilState.m_ZFailOp = STENCILOPERATION_KEEP; - stencilState.SetStencilState( pRenderContext ); - - m_GlowObjectDefinitions[i].DrawModel(); + pRenderContext->DrawScreenSpaceRectangle(pFullFrameFB1, + 0, 0, nViewportWidth, nViewportHeight, + 0, 0, nSrcWidth - 1, nSrcHeight - 1, + pRtFullFrameFB1->GetActualWidth(), pRtFullFrameFB1->GetActualHeight()); } + pRenderContext->OverrideDepthEnable(false, false); + + pFullFrameFB1->Release(); +#endif } - pRenderContext->OverrideDepthEnable( false, false ); - render->SetBlend( flSavedBlend ); - stencilStateDisable.SetStencilState( pRenderContext ); - g_pStudioRender->ForcedMaterialOverride( NULL ); - - // If there aren't any objects to glow, don't do all this other stuff - // this fixes a bug where if there are glow objects in the list, but none of them are glowing, - // the whole screen blooms. - if ( iNumGlowObjects <= 0 ) - return; - - //============================================= - // Render the glow colors to _rt_FullFrameFB - //============================================= + // Bloom glow models from _rt_FullFrameFB0 to backbuffer while stenciling out inside of models { - PIXEvent pixEvent( pRenderContext, "RenderGlowModels" ); - RenderGlowModels( pSetup, nSplitScreenSlot, pRenderContext ); - } - - // Get viewport - int nSrcWidth = pSetup->width; - int nSrcHeight = pSetup->height; - int nViewportX, nViewportY, nViewportWidth, nViewportHeight; - pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight ); - - // Get material and texture pointers - ITexture *pRtQuarterSize1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); - - { - //=======================================================================================================// - // At this point, pRtQuarterSize0 is filled with the fully colored glow around everything as solid glowy // - // blobs. Now we need to stencil out the original objects by only writing pixels that have no // - // stencil bits set in the range we care about. // - //=======================================================================================================// - IMaterial *pMatHaloAddToScreen = materials->FindMaterial( "dev/halo_add_to_screen", TEXTURE_GROUP_OTHER, true ); - - // Do not fade the glows out at all (weight = 1.0) - IMaterialVar *pDimVar = pMatHaloAddToScreen->FindVar( "$C0_X", NULL ); - pDimVar->SetFloatValue( 1.0f ); - // Set stencil state ShaderStencilState_t stencilState; stencilState.m_bEnable = true; - stencilState.m_nWriteMask = 0x0; // We're not changing stencil - stencilState.m_nTestMask = 0xFF; - stencilState.m_nReferenceValue = 0x0; - stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_EQUAL; + stencilState.m_nWriteMask = 0; // We're not changing stencil + stencilState.m_nReferenceValue = 1; + stencilState.m_nTestMask = 1; + stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_NOTEQUAL; stencilState.m_PassOp = STENCILOPERATION_KEEP; stencilState.m_FailOp = STENCILOPERATION_KEEP; stencilState.m_ZFailOp = STENCILOPERATION_KEEP; - stencilState.SetStencilState( pRenderContext ); + stencilState.SetStencilState(pRenderContext); + + ITexture* const pRtQuarterSize1 = materials->FindTexture("_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET); + IMaterial* const pMatHaloAddToScreen = materials->FindMaterial("dev/halo_add_to_screen", TEXTURE_GROUP_OTHER, true); + + // Write to alpha + pRenderContext->OverrideAlphaWriteEnable(true, true); + + const int nSrcWidth = pSetup->width; + const int nSrcHeight = pSetup->height; + int nViewportX, nViewportY, nViewportWidth, nViewportHeight; + pRenderContext->GetViewport(nViewportX, nViewportY, nViewportWidth, nViewportHeight); // Draw quad - pRenderContext->DrawScreenSpaceRectangle( pMatHaloAddToScreen, 0, 0, nViewportWidth, nViewportHeight, - 0.0f, -0.5f, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, + pRenderContext->DrawScreenSpaceRectangle(pMatHaloAddToScreen, + 0, 0, nViewportWidth, nViewportHeight, + 0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, pRtQuarterSize1->GetActualWidth(), - pRtQuarterSize1->GetActualHeight() ); - - stencilStateDisable.SetStencilState( pRenderContext ); + pRtQuarterSize1->GetActualHeight()); } + + // Done with all of our "advanced" 3D rendering. + pRenderContext->SetStencilEnable(false); + pRenderContext->OverrideColorWriteEnable(false, false); + pRenderContext->OverrideAlphaWriteEnable(false, false); + pRenderContext->OverrideDepthEnable(false, false); + + pRenderContext->PopRenderTargetAndViewport(); } void CGlowObjectManager::GlowObjectDefinition_t::DrawModel() diff --git a/sp/src/game/client/glow_outline_effect.h b/sp/src/game/client/glow_outline_effect.h index aac399d7..1747f0a8 100644 --- a/sp/src/game/client/glow_outline_effect.h +++ b/sp/src/game/client/glow_outline_effect.h @@ -117,7 +117,6 @@ public: private: - void RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext ); void ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h ); struct GlowObjectDefinition_t @@ -150,6 +149,10 @@ private: static const int ENTRY_IN_USE = -2; }; + void DrawGlowVisible(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext); + void DrawGlowOccluded(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext); + void DrawGlowAlways(int nSplitScreenSlot, CMatRenderContextPtr& pRenderContext); + CUtlVector< GlowObjectDefinition_t > m_GlowObjectDefinitions; int m_nFirstFreeSlot; };