2951 lines
116 KiB
C++
2951 lines
116 KiB
C++
|
//========== Copyright <20> 2008, Valve Corporation, All rights reserved. ==========
|
|||
|
//
|
|||
|
// Purpose:
|
|||
|
//
|
|||
|
//==============================================================================
|
|||
|
|
|||
|
#include "cbase.h"
|
|||
|
|
|||
|
#include "materialsystem/imaterialsystem.h"
|
|||
|
#include "materialsystem/itexture.h"
|
|||
|
#include "materialsystem/imaterialvar.h"
|
|||
|
#include "materialsystem/imaterialsystemhardwareconfig.h"
|
|||
|
#include "materialsystem/materialsystem_config.h"
|
|||
|
#include "tier1/callqueue.h"
|
|||
|
#include "colorcorrectionmgr.h"
|
|||
|
#include "view_scene.h"
|
|||
|
#include "c_world.h"
|
|||
|
#include "renderparm.h"
|
|||
|
#include "shaderapi/ishaderapi.h"
|
|||
|
#include "ProxyEntity.h"
|
|||
|
#include "imaterialproxydict.h"
|
|||
|
#include "model_types.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// NOTE: This has to be the last file included!
|
|||
|
#include "tier0/memdbgon.h"
|
|||
|
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Globals
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
|
|||
|
// mapmaker controlled autoexposure
|
|||
|
bool g_bUseCustomAutoExposureMin = false;
|
|||
|
bool g_bUseCustomAutoExposureMax = false;
|
|||
|
bool g_bUseCustomBloomScale = false;
|
|||
|
float g_flCustomAutoExposureMin = 0;
|
|||
|
float g_flCustomAutoExposureMax = 0;
|
|||
|
float g_flCustomBloomScale = 0.0f;
|
|||
|
float g_flCustomBloomScaleMinimum = 0.0f;
|
|||
|
|
|||
|
extern void GetTonemapSettingsFromEnvTonemapController( void );
|
|||
|
|
|||
|
// mapmaker controlled depth of field
|
|||
|
bool g_bDOFEnabled = false;
|
|||
|
float g_flDOFNearBlurDepth = 50.0f;
|
|||
|
float g_flDOFNearFocusDepth = 200.0f;
|
|||
|
float g_flDOFFarFocusDepth = 250.0f;
|
|||
|
float g_flDOFFarBlurDepth = 1000.0f;
|
|||
|
float g_flDOFNearBlurRadius = 0.0f;
|
|||
|
float g_flDOFFarBlurRadius = 5.0f;
|
|||
|
|
|||
|
bool g_bFlashlightIsOn = false;
|
|||
|
|
|||
|
// hdr parameters
|
|||
|
ConVar mat_bloomscale( "mat_bloomscale", "1" );
|
|||
|
|
|||
|
ConVar mat_hdr_level( "mat_hdr_level", "2" );
|
|||
|
ConVar mat_bloomamount_rate( "mat_bloomamount_rate", "0.05f", FCVAR_CHEAT );
|
|||
|
static ConVar debug_postproc( "mat_debug_postprocessing_effects", "0", FCVAR_NONE, "0 = off, 1 = show post-processing passes in quadrants of the screen, 2 = only apply post-processing to the centre of the screen" );
|
|||
|
static ConVar mat_dynamic_tonemapping( "mat_dynamic_tonemapping", "1", FCVAR_CHEAT );
|
|||
|
static ConVar mat_tonemapping_occlusion_use_stencil( "mat_tonemapping_occlusion_use_stencil", "0" );
|
|||
|
|
|||
|
static ConVar mat_autoexposure_max( "mat_autoexposure_max", "2" );
|
|||
|
static ConVar mat_autoexposure_min( "mat_autoexposure_min", "0.5" );
|
|||
|
static ConVar mat_show_histogram( "mat_show_histogram", "0" );
|
|||
|
ConVar mat_hdr_uncapexposure( "mat_hdr_uncapexposure", "0", FCVAR_CHEAT );
|
|||
|
ConVar mat_force_bloom("mat_force_bloom","0", FCVAR_CHEAT);
|
|||
|
|
|||
|
ConVar mat_disable_bloom("mat_disable_bloom","0");
|
|||
|
ConVar mat_debug_bloom("mat_debug_bloom","0", FCVAR_CHEAT);
|
|||
|
ConVar mat_colorcorrection( "mat_colorcorrection", "1", FCVAR_DEVELOPMENTONLY );
|
|||
|
|
|||
|
ConVar mat_accelerate_adjust_exposure_down( "mat_accelerate_adjust_exposure_down", "3.0", FCVAR_CHEAT );
|
|||
|
ConVar mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate", "1.0" );
|
|||
|
|
|||
|
// fudge factor to make non-hdr bloom more closely match hdr bloom. Because of auto-exposure, high
|
|||
|
// bloomscales don't blow out as much in hdr. this factor was derived by comparing images in a
|
|||
|
// reference scene.
|
|||
|
ConVar mat_non_hdr_bloom_scalefactor("mat_non_hdr_bloom_scalefactor",".3");
|
|||
|
|
|||
|
// Apply addition scale to the final bloom scale
|
|||
|
static ConVar mat_bloom_scalefactor_scalar( "mat_bloom_scalefactor_scalar", "1.0", FCVAR_RELEASE );
|
|||
|
|
|||
|
//ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.75", FCVAR_CHEAT );
|
|||
|
//ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.80", FCVAR_CHEAT );
|
|||
|
|
|||
|
ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.9", FCVAR_CHEAT );
|
|||
|
ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.85", FCVAR_CHEAT );
|
|||
|
|
|||
|
ConVar mat_tonemap_algorithm( "mat_tonemap_algorithm", "1", FCVAR_CHEAT, "0 = Original Algorithm 1 = New Algorithm" );
|
|||
|
ConVar mat_tonemap_percent_target( "mat_tonemap_percent_target", "60.0", FCVAR_CHEAT );
|
|||
|
ConVar mat_tonemap_percent_bright_pixels( "mat_tonemap_percent_bright_pixels", "2.0", FCVAR_CHEAT );
|
|||
|
ConVar mat_tonemap_min_avglum( "mat_tonemap_min_avglum", "3.0", FCVAR_CHEAT );
|
|||
|
ConVar mat_force_tonemap_scale( "mat_force_tonemap_scale", "0.0", FCVAR_CHEAT );
|
|||
|
ConVar mat_fullbright( "mat_fullbright", "0", FCVAR_CHEAT );
|
|||
|
|
|||
|
ConVar mat_grain_enable( "mat_grain_enable", "0" );
|
|||
|
ConVar mat_vignette_enable( "mat_vignette_enable", "0" );
|
|||
|
ConVar mat_local_contrast_enable( "mat_local_contrast_enable", "0" );
|
|||
|
|
|||
|
static void SetRenderTargetAndViewPort(ITexture *rt)
|
|||
|
{
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->SetRenderTarget(rt);
|
|||
|
if ( rt )
|
|||
|
{
|
|||
|
pRenderContext->Viewport(0,0,rt->GetActualWidth(),rt->GetActualHeight());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
enum HistogramEntryState_t
|
|||
|
{
|
|||
|
HESTATE_INITIAL = 0,
|
|||
|
HESTATE_FIRST_QUERY_IN_FLIGHT,
|
|||
|
HESTATE_QUERY_IN_FLIGHT,
|
|||
|
HESTATE_QUERY_DONE,
|
|||
|
};
|
|||
|
|
|||
|
#define NUM_HISTOGRAM_BUCKETS 31
|
|||
|
#define NUM_HISTOGRAM_BUCKETS_NEW 17
|
|||
|
#define MAX_QUERIES_PER_FRAME 1
|
|||
|
|
|||
|
class CHistogramBucket
|
|||
|
{
|
|||
|
public:
|
|||
|
HistogramEntryState_t m_state;
|
|||
|
OcclusionQueryObjectHandle_t m_hOcclusionQueryHandle;
|
|||
|
int m_nFrameQueued; // when this query was last queued
|
|||
|
int m_nPixels; // # of pixels this histogram represents
|
|||
|
int m_nPixelsInRange;
|
|||
|
float m_flMinLuminance, m_flMaxLuminance; // the luminance range this entry was queried with
|
|||
|
float m_flScreenMinX, m_flScreenMinY, m_flScreenMaxX, m_flScreenMaxY; // range is 0..1 in fractions of the screen
|
|||
|
|
|||
|
bool ContainsValidData( void )
|
|||
|
{
|
|||
|
return ( m_state == HESTATE_QUERY_DONE ) || ( m_state == HESTATE_QUERY_IN_FLIGHT );
|
|||
|
}
|
|||
|
|
|||
|
void IssueQuery( int nFrameNum );
|
|||
|
};
|
|||
|
|
|||
|
void CHistogramBucket::IssueQuery( int nFrameNum )
|
|||
|
{
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
if ( !m_hOcclusionQueryHandle )
|
|||
|
{
|
|||
|
m_hOcclusionQueryHandle = pRenderContext->CreateOcclusionQueryObject();
|
|||
|
}
|
|||
|
|
|||
|
int nViewportX, nViewportY, nViewportWidth, nViewportHeight;
|
|||
|
pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
|
|||
|
|
|||
|
// Find min and max gamma-space text range
|
|||
|
float flTestRangeMin = ( m_flMinLuminance == 0.0f ) ? -1e20f : m_flMinLuminance; // Count all pixels < 0.0 as 0.0 (for float HDR buffers)
|
|||
|
float flTestRangeMax = ( m_flMaxLuminance == 1.0f ) ? 1e20f : m_flMaxLuminance; // Count all pixels >1.0 as 1.0
|
|||
|
|
|||
|
// Set stencil bits where the colors match
|
|||
|
IMaterial *pLumCompareMaterial = materials->FindMaterial( "dev/lumcompare", TEXTURE_GROUP_OTHER, true );
|
|||
|
|
|||
|
IMaterialVar *pMinVar = pLumCompareMaterial->FindVar( "$C0_X", NULL );
|
|||
|
pMinVar->SetFloatValue( flTestRangeMin );
|
|||
|
|
|||
|
IMaterialVar *pMaxVar = pLumCompareMaterial->FindVar( "$C0_Y", NULL );
|
|||
|
pMaxVar->SetFloatValue( flTestRangeMax );
|
|||
|
|
|||
|
int nScreenMinX = FLerp( nViewportX, ( nViewportX + nViewportWidth - 1 ), 0, 1, m_flScreenMinX );
|
|||
|
int nScreenMaxX = FLerp( nViewportX, ( nViewportX + nViewportWidth - 1 ), 0, 1, m_flScreenMaxX );
|
|||
|
int nScreenMinY = FLerp( nViewportY, ( nViewportY + nViewportHeight - 1 ), 0, 1, m_flScreenMinY );
|
|||
|
int nScreenMaxY = FLerp( nViewportY, ( nViewportY + nViewportHeight - 1 ), 0, 1, m_flScreenMaxY );
|
|||
|
|
|||
|
float flExposureWidthScale, flExposureHeightScale;
|
|||
|
|
|||
|
// Shrink region of interest if the flashlight is on
|
|||
|
flExposureWidthScale = ( 0.5f * ( 1.0f - mat_exposure_center_region_x.GetFloat() ) );
|
|||
|
flExposureHeightScale = ( 0.5f * ( 1.0f - mat_exposure_center_region_y.GetFloat() ) );
|
|||
|
|
|||
|
int nBorderWidth = ( nScreenMaxX - nScreenMinX + 1 ) * flExposureWidthScale;
|
|||
|
int nBorderHeight = ( nScreenMaxY - nScreenMinY + 1 ) * flExposureHeightScale;
|
|||
|
|
|||
|
// Do luminance compare
|
|||
|
m_nPixels = ( 1 + nScreenMaxX - nScreenMinX ) * ( 1 + nScreenMaxY - nScreenMinY );
|
|||
|
|
|||
|
ShaderStencilState_t state;
|
|||
|
if ( mat_tonemapping_occlusion_use_stencil.GetInt() )
|
|||
|
{
|
|||
|
state.m_nWriteMask = 1;
|
|||
|
state.m_bEnable = true;
|
|||
|
state.m_PassOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|||
|
state.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
|
|||
|
state.m_FailOp = SHADER_STENCILOP_KEEP;
|
|||
|
state.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|||
|
state.m_nReferenceValue = 1;
|
|||
|
pRenderContext->SetStencilState( state );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pRenderContext->BeginOcclusionQueryDrawing( m_hOcclusionQueryHandle );
|
|||
|
}
|
|||
|
|
|||
|
int nWindowWidth = 0;
|
|||
|
int nWindowHeight = 0;
|
|||
|
pRenderContext->GetWindowSize( nWindowWidth, nWindowHeight );
|
|||
|
|
|||
|
nScreenMinX += nBorderWidth;
|
|||
|
nScreenMinY += nBorderHeight;
|
|||
|
nScreenMaxX -= nBorderWidth;
|
|||
|
nScreenMaxY -= nBorderHeight;
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( pLumCompareMaterial,
|
|||
|
nScreenMinX - nViewportX, nScreenMinY - nViewportY,
|
|||
|
1 + nScreenMaxX - nScreenMinX,
|
|||
|
1 + nScreenMaxY - nScreenMinY,
|
|||
|
nScreenMinX, nScreenMinY,
|
|||
|
nScreenMaxX, nScreenMaxY,
|
|||
|
nWindowWidth, nWindowHeight );
|
|||
|
|
|||
|
if ( mat_tonemapping_occlusion_use_stencil.GetInt() )
|
|||
|
{
|
|||
|
// Start counting how many pixels had their stencil bit set via an occlusion query
|
|||
|
pRenderContext->BeginOcclusionQueryDrawing( m_hOcclusionQueryHandle );
|
|||
|
|
|||
|
// Issue an occlusion query using stencil as the mask
|
|||
|
state.m_bEnable = true;
|
|||
|
state.m_nTestMask = 1;
|
|||
|
state.m_PassOp = SHADER_STENCILOP_KEEP;
|
|||
|
state.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
|
|||
|
state.m_FailOp = SHADER_STENCILOP_KEEP;
|
|||
|
state.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|||
|
state.m_nReferenceValue = 1;
|
|||
|
pRenderContext->SetStencilState( state );
|
|||
|
|
|||
|
IMaterial *pLumCompareStencilMaterial = materials->FindMaterial( "dev/no_pixel_write", TEXTURE_GROUP_OTHER, true);
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( pLumCompareStencilMaterial,
|
|||
|
nScreenMinX, nScreenMinY,
|
|||
|
1 + nScreenMaxX - nScreenMinX,
|
|||
|
1 + nScreenMaxY - nScreenMinY,
|
|||
|
nScreenMinX, nScreenMinY,
|
|||
|
nScreenMaxX, nScreenMaxY,
|
|||
|
nWindowWidth, nWindowHeight );
|
|||
|
|
|||
|
ShaderStencilState_t stateDisable;
|
|||
|
stateDisable.m_bEnable = false;
|
|||
|
pRenderContext->SetStencilState( stateDisable );
|
|||
|
}
|
|||
|
|
|||
|
pRenderContext->EndOcclusionQueryDrawing( m_hOcclusionQueryHandle );
|
|||
|
if ( m_state == HESTATE_INITIAL )
|
|||
|
m_state = HESTATE_FIRST_QUERY_IN_FLIGHT;
|
|||
|
else
|
|||
|
m_state = HESTATE_QUERY_IN_FLIGHT;
|
|||
|
m_nFrameQueued = nFrameNum;
|
|||
|
}
|
|||
|
|
|||
|
#define HISTOGRAM_BAR_SIZE 200
|
|||
|
|
|||
|
class CTonemapSystem
|
|||
|
{
|
|||
|
CHistogramBucket m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS];
|
|||
|
int m_nCurrentQueryFrame;
|
|||
|
int m_nCurrentAlgorithm;
|
|||
|
|
|||
|
float m_flTargetTonemapScale;
|
|||
|
float m_flCurrentTonemapScale;
|
|||
|
|
|||
|
int m_nNumMovingAverageValid;
|
|||
|
float m_movingAverageTonemapScale[10];
|
|||
|
|
|||
|
bool m_bOverrideTonemapScaleEnabled;
|
|||
|
float m_flOverrideTonemapScale;
|
|||
|
|
|||
|
public:
|
|||
|
void IssueAndReceiveBucketQueries();
|
|||
|
void UpdateBucketRanges();
|
|||
|
float FindLocationOfPercentBrightPixels( float flPercentBrightPixels, float flPercentTarget );
|
|||
|
float ComputeTargetTonemapScalar( bool bGetIdealTargetForDebugMode );
|
|||
|
|
|||
|
void UpdateMaterialSystemTonemapScalar();
|
|||
|
void SetTargetTonemappingScale( float flTonemapScale );
|
|||
|
void ResetTonemappingScale( float flTonemapScale );
|
|||
|
void SetTonemapScale( IMatRenderContext *pRenderContext, float newvalue, float minvalue, float maxvalue );
|
|||
|
|
|||
|
float GetTargetTonemappingScale() { return m_flTargetTonemapScale; }
|
|||
|
float GetCurrentTonemappingScale() { return m_flCurrentTonemapScale; }
|
|||
|
|
|||
|
void SetOverrideTonemapScale( bool bEnableOverride, float flTonemapScale );
|
|||
|
|
|||
|
// Dev functions
|
|||
|
void DisplayHistogram();
|
|||
|
|
|||
|
// Constructor
|
|||
|
CTonemapSystem()
|
|||
|
{
|
|||
|
m_nCurrentQueryFrame = 0;
|
|||
|
m_nCurrentAlgorithm = -1;
|
|||
|
m_flTargetTonemapScale = 1.0f;
|
|||
|
m_flCurrentTonemapScale = 1.0f;
|
|||
|
|
|||
|
m_nNumMovingAverageValid = 0;
|
|||
|
for ( int i = 0; i < ARRAYSIZE( m_movingAverageTonemapScale ) - 1; i++ )
|
|||
|
{
|
|||
|
m_movingAverageTonemapScale[i] = 1.0f;
|
|||
|
}
|
|||
|
|
|||
|
m_bOverrideTonemapScaleEnabled = false;
|
|||
|
m_flOverrideTonemapScale = 1.0f;
|
|||
|
|
|||
|
UpdateBucketRanges();
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
CTonemapSystem * GetCurrentTonemappingSystem()
|
|||
|
{
|
|||
|
static CTonemapSystem s_HDR_HistogramSystem[ MAX_SPLITSCREEN_PLAYERS ];
|
|||
|
int slot = GET_ACTIVE_SPLITSCREEN_SLOT();
|
|||
|
return &( s_HDR_HistogramSystem[ slot ] );
|
|||
|
}
|
|||
|
|
|||
|
void CTonemapSystem::IssueAndReceiveBucketQueries()
|
|||
|
{
|
|||
|
UpdateBucketRanges();
|
|||
|
|
|||
|
// Find which histogram entries should have something done this frame
|
|||
|
int nQueriesIssuedThisFrame = 0;
|
|||
|
m_nCurrentQueryFrame++;
|
|||
|
|
|||
|
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS;
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 )
|
|||
|
nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW;
|
|||
|
|
|||
|
for ( int i = 0; i < nNumHistogramBuckets; i++ )
|
|||
|
{
|
|||
|
switch ( m_histogramBucketArray[i].m_state )
|
|||
|
{
|
|||
|
case HESTATE_INITIAL:
|
|||
|
if ( nQueriesIssuedThisFrame<MAX_QUERIES_PER_FRAME )
|
|||
|
{
|
|||
|
m_histogramBucketArray[i].IssueQuery(m_nCurrentQueryFrame);
|
|||
|
nQueriesIssuedThisFrame++;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case HESTATE_FIRST_QUERY_IN_FLIGHT:
|
|||
|
case HESTATE_QUERY_IN_FLIGHT:
|
|||
|
if ( m_nCurrentQueryFrame > m_histogramBucketArray[i].m_nFrameQueued + 2 )
|
|||
|
{
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
int np = pRenderContext->OcclusionQuery_GetNumPixelsRendered(
|
|||
|
m_histogramBucketArray[i].m_hOcclusionQueryHandle );
|
|||
|
if ( np != -1 ) // -1 = Query not finished...wait until next time
|
|||
|
{
|
|||
|
m_histogramBucketArray[i].m_nPixelsInRange = np;
|
|||
|
m_histogramBucketArray[i].m_state = HESTATE_QUERY_DONE;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Now, issue queries for the oldest finished queries we have
|
|||
|
while ( nQueriesIssuedThisFrame < MAX_QUERIES_PER_FRAME )
|
|||
|
{
|
|||
|
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS;
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 )
|
|||
|
nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW;
|
|||
|
|
|||
|
int nOldestSoFar = -1;
|
|||
|
for ( int i = 0; i < nNumHistogramBuckets; i++ )
|
|||
|
{
|
|||
|
if ( ( m_histogramBucketArray[i].m_state == HESTATE_QUERY_DONE ) &&
|
|||
|
( ( nOldestSoFar == -1 ) || ( m_histogramBucketArray[i].m_nFrameQueued < m_histogramBucketArray[nOldestSoFar].m_nFrameQueued ) ) )
|
|||
|
{
|
|||
|
nOldestSoFar = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( nOldestSoFar == -1 ) // Nothing to do
|
|||
|
break;
|
|||
|
|
|||
|
m_histogramBucketArray[nOldestSoFar].IssueQuery( m_nCurrentQueryFrame );
|
|||
|
nQueriesIssuedThisFrame++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
float CTonemapSystem::FindLocationOfPercentBrightPixels( float flPercentBrightPixels, float flPercentTargetToSnapToIfInSameBin = -1.0f )
|
|||
|
{
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm
|
|||
|
{
|
|||
|
int nTotalValidPixels = 0;
|
|||
|
for ( int i = 0; i < ( NUM_HISTOGRAM_BUCKETS_NEW - 1 ); i++ )
|
|||
|
{
|
|||
|
if ( m_histogramBucketArray[i].ContainsValidData() )
|
|||
|
{
|
|||
|
nTotalValidPixels += m_histogramBucketArray[i].m_nPixelsInRange;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( nTotalValidPixels == 0 )
|
|||
|
{
|
|||
|
return -1.0f;
|
|||
|
}
|
|||
|
|
|||
|
// Find where percent range border is
|
|||
|
float flTotalPercentRangeTested = 0.0f;
|
|||
|
float flTotalPercentPixelsTested = 0.0f;
|
|||
|
for ( int i = ( NUM_HISTOGRAM_BUCKETS_NEW - 2 ); i >= 0; i-- ) // Start at the bright end
|
|||
|
{
|
|||
|
if ( !m_histogramBucketArray[i].ContainsValidData() )
|
|||
|
return -1.0f;
|
|||
|
|
|||
|
float flPixelPercentNeeded = ( flPercentBrightPixels / 100.0f ) - flTotalPercentPixelsTested;
|
|||
|
float flThisBinPercentOfTotalPixels = float( m_histogramBucketArray[i].m_nPixelsInRange ) / float( nTotalValidPixels );
|
|||
|
float flThisBinLuminanceRange = m_histogramBucketArray[i].m_flMaxLuminance - m_histogramBucketArray[i].m_flMinLuminance;
|
|||
|
if ( flThisBinPercentOfTotalPixels >= flPixelPercentNeeded ) // We found the bin needed
|
|||
|
{
|
|||
|
if ( flPercentTargetToSnapToIfInSameBin >= 0.0f )
|
|||
|
{
|
|||
|
if ( ( m_histogramBucketArray[i].m_flMinLuminance <= ( flPercentTargetToSnapToIfInSameBin / 100.0f ) ) && ( m_histogramBucketArray[i].m_flMaxLuminance >= ( flPercentTargetToSnapToIfInSameBin / 100.0f ) ) )
|
|||
|
{
|
|||
|
// Sticky bin...We're in the same bin as the target so keep the tonemap scale where it is
|
|||
|
return ( flPercentTargetToSnapToIfInSameBin / 100.0f );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
float flPercentOfThesePixelsNeeded = flPixelPercentNeeded / flThisBinPercentOfTotalPixels;
|
|||
|
float flPercentLocationOfBorder = 1.0f - ( flTotalPercentRangeTested + ( flThisBinLuminanceRange * flPercentOfThesePixelsNeeded ) );
|
|||
|
flPercentLocationOfBorder = MAX( m_histogramBucketArray[i].m_flMinLuminance, MIN( m_histogramBucketArray[i].m_flMaxLuminance, flPercentLocationOfBorder ) ); // Clamp to this bin just in case
|
|||
|
return flPercentLocationOfBorder;
|
|||
|
}
|
|||
|
|
|||
|
flTotalPercentPixelsTested += flThisBinPercentOfTotalPixels;
|
|||
|
flTotalPercentRangeTested += flThisBinLuminanceRange;
|
|||
|
}
|
|||
|
|
|||
|
return -1.0f;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Don't know what to do for other algorithms yet
|
|||
|
return -1.0f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
float CTonemapSystem::ComputeTargetTonemapScalar( bool bGetIdealTargetForDebugMode = false )
|
|||
|
{
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm
|
|||
|
{
|
|||
|
float flPercentLocationOfTarget;
|
|||
|
if ( bGetIdealTargetForDebugMode == true)
|
|||
|
flPercentLocationOfTarget = FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat() ); // Don't pass in the second arg so the scalar doesn't snap to a bin
|
|||
|
else
|
|||
|
flPercentLocationOfTarget = FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetFloat() );
|
|||
|
if ( flPercentLocationOfTarget < 0.0f ) // This is the return error code
|
|||
|
{
|
|||
|
flPercentLocationOfTarget = mat_tonemap_percent_target.GetFloat() / 100.0f; // Pretend we're at the target
|
|||
|
}
|
|||
|
|
|||
|
// Make sure this is > 0.0f
|
|||
|
flPercentLocationOfTarget = MAX( 0.0001f, flPercentLocationOfTarget );
|
|||
|
|
|||
|
// Compute target scalar
|
|||
|
float flTargetScalar = ( mat_tonemap_percent_target.GetFloat() / 100.0f ) / flPercentLocationOfTarget;
|
|||
|
|
|||
|
// Compute secondary target scalar
|
|||
|
float flAverageLuminanceLocation = FindLocationOfPercentBrightPixels( 50.0f );
|
|||
|
if ( flAverageLuminanceLocation > 0.0f )
|
|||
|
{
|
|||
|
float flTargetScalar2 = ( mat_tonemap_min_avglum.GetFloat() / 100.0f ) / flAverageLuminanceLocation;
|
|||
|
|
|||
|
// Only override it if it's trying to brighten the image more than the primary algorithm
|
|||
|
if ( flTargetScalar2 > flTargetScalar )
|
|||
|
{
|
|||
|
flTargetScalar = flTargetScalar2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Apply this against last frames scalar
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
float flLastScale = m_flCurrentTonemapScale;
|
|||
|
flTargetScalar *= flLastScale;
|
|||
|
|
|||
|
flTargetScalar = MAX( 0.001f, flTargetScalar );
|
|||
|
return flTargetScalar;
|
|||
|
}
|
|||
|
else // Original tonemapping algorithm
|
|||
|
{
|
|||
|
float flScaleValue = 1.0f;
|
|||
|
if ( m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS-1].ContainsValidData() )
|
|||
|
{
|
|||
|
flScaleValue = m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS-1].m_nPixels * ( 1.0f / m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS-1].m_nPixelsInRange );
|
|||
|
}
|
|||
|
|
|||
|
if ( !IsFinite( flScaleValue ) )
|
|||
|
{
|
|||
|
flScaleValue = 1.0f;
|
|||
|
}
|
|||
|
|
|||
|
float flTotal = 0.0f;
|
|||
|
int nTotalPixels = 0;
|
|||
|
for ( int i=0; i<NUM_HISTOGRAM_BUCKETS-1; i++ )
|
|||
|
{
|
|||
|
if ( m_histogramBucketArray[i].ContainsValidData() )
|
|||
|
{
|
|||
|
flTotal += flScaleValue * m_histogramBucketArray[i].m_nPixelsInRange * AVG( m_histogramBucketArray[i].m_flMinLuminance, m_histogramBucketArray[i].m_flMaxLuminance );
|
|||
|
nTotalPixels += m_histogramBucketArray[i].m_nPixels;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
float flAverageLuminance = 0.5f;
|
|||
|
if ( nTotalPixels > 0 )
|
|||
|
flAverageLuminance = flTotal * ( 1.0f / nTotalPixels );
|
|||
|
else
|
|||
|
flAverageLuminance = 0.5f;
|
|||
|
|
|||
|
// Make sure this is > 0.0f
|
|||
|
flAverageLuminance = MAX( 0.0001f, flAverageLuminance );
|
|||
|
|
|||
|
// Compute target scalar
|
|||
|
float flTargetScalar = 0.005f / flAverageLuminance;
|
|||
|
|
|||
|
return flTargetScalar;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static float GetCurrentBloomScale( void )
|
|||
|
{
|
|||
|
// Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings.
|
|||
|
float flCurrentBloomScale = 1.0f;
|
|||
|
if ( g_bUseCustomBloomScale )
|
|||
|
{
|
|||
|
flCurrentBloomScale = g_flCustomBloomScale;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
flCurrentBloomScale = mat_bloomscale.GetFloat();
|
|||
|
}
|
|||
|
return flCurrentBloomScale;
|
|||
|
}
|
|||
|
|
|||
|
static void GetExposureRange( float *pflAutoExposureMin, float *pflAutoExposureMax )
|
|||
|
{
|
|||
|
// Get min
|
|||
|
if ( ( g_bUseCustomAutoExposureMin ) && ( g_flCustomAutoExposureMin > 0.0f ) )
|
|||
|
{
|
|||
|
*pflAutoExposureMin = g_flCustomAutoExposureMin;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*pflAutoExposureMin = mat_autoexposure_min.GetFloat();
|
|||
|
}
|
|||
|
|
|||
|
// Get max
|
|||
|
if ( ( g_bUseCustomAutoExposureMax ) && ( g_flCustomAutoExposureMax > 0.0f ) )
|
|||
|
{
|
|||
|
*pflAutoExposureMax = g_flCustomAutoExposureMax;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*pflAutoExposureMax = mat_autoexposure_max.GetFloat();
|
|||
|
}
|
|||
|
|
|||
|
// Override
|
|||
|
if ( mat_hdr_uncapexposure.GetInt() )
|
|||
|
{
|
|||
|
*pflAutoExposureMax = 100.0f;
|
|||
|
*pflAutoExposureMin = 0.0f;
|
|||
|
}
|
|||
|
|
|||
|
// Make sure min <= max
|
|||
|
if ( *pflAutoExposureMin > *pflAutoExposureMax )
|
|||
|
{
|
|||
|
*pflAutoExposureMax = *pflAutoExposureMin;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CTonemapSystem::UpdateBucketRanges()
|
|||
|
{
|
|||
|
// Only update if our mode changed
|
|||
|
if ( m_nCurrentAlgorithm == mat_tonemap_algorithm.GetInt() )
|
|||
|
return;
|
|||
|
m_nCurrentAlgorithm = mat_tonemap_algorithm.GetInt();
|
|||
|
|
|||
|
//==================================================================//
|
|||
|
// Force fallback to original tone mapping algorithm for these mods //
|
|||
|
//==================================================================//
|
|||
|
static bool s_bFirstTime = true;
|
|||
|
if ( engine == NULL )
|
|||
|
{
|
|||
|
// Force this code to get hit again so we can change algorithm based on the client
|
|||
|
m_nCurrentAlgorithm = -1;
|
|||
|
}
|
|||
|
else if ( s_bFirstTime == true )
|
|||
|
{
|
|||
|
s_bFirstTime = false;
|
|||
|
|
|||
|
// This seems like a bad idea but it's fine for now
|
|||
|
const char *sModsForOriginalAlgorithm[] = { "dod", "cstrike", "lostcoast" };
|
|||
|
for ( int i=0; i<3; i++ )
|
|||
|
{
|
|||
|
if ( strlen( engine->GetGameDirectory() ) >= strlen( sModsForOriginalAlgorithm[i] ) )
|
|||
|
{
|
|||
|
if ( stricmp( &( engine->GetGameDirectory()[strlen( engine->GetGameDirectory() ) - strlen( sModsForOriginalAlgorithm[i] )] ), sModsForOriginalAlgorithm[i] ) == 0 )
|
|||
|
{
|
|||
|
mat_tonemap_algorithm.SetValue( 0 ); // Original algorithm
|
|||
|
m_nCurrentAlgorithm = mat_tonemap_algorithm.GetInt();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Get num buckets
|
|||
|
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS;
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 )
|
|||
|
nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW;
|
|||
|
|
|||
|
m_nCurrentQueryFrame = 0;
|
|||
|
for ( int nBucket = 0; nBucket < nNumHistogramBuckets; nBucket++ )
|
|||
|
{
|
|||
|
CHistogramBucket *pBucket = &( m_histogramBucketArray[ nBucket ] );
|
|||
|
pBucket->m_state = HESTATE_INITIAL;
|
|||
|
pBucket->m_flScreenMinX = 0.0f;
|
|||
|
pBucket->m_flScreenMaxX = 1.0f;
|
|||
|
pBucket->m_flScreenMinY = 0.0f;
|
|||
|
pBucket->m_flScreenMaxY = 1.0f;
|
|||
|
if ( nBucket != ( nNumHistogramBuckets - 1 ) ) // Last bucket is special
|
|||
|
{
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 0 ) // Original algorithm
|
|||
|
{
|
|||
|
// Use a logarithmic ramp for high range in the low range
|
|||
|
pBucket->m_flMinLuminance = -0.01f + exp( FLerp( log( 0.01f ), log( 0.01f + 1.0f ), 0.0f, nNumHistogramBuckets - 1.0f, nBucket ) );
|
|||
|
pBucket->m_flMaxLuminance = -0.01f + exp( FLerp( log( 0.01f ), log( 0.01f + 1.0f ), 0.0f, nNumHistogramBuckets - 1.0f, nBucket + 1.0f ) );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Use even distribution
|
|||
|
pBucket->m_flMinLuminance = float( nBucket ) / float( nNumHistogramBuckets - 1 );
|
|||
|
pBucket->m_flMaxLuminance = float( nBucket + 1 ) / float( nNumHistogramBuckets - 1 );
|
|||
|
|
|||
|
// Use a distribution with slightly more bins in the low range
|
|||
|
pBucket->m_flMinLuminance = pBucket->m_flMinLuminance > 0.0f ? powf( pBucket->m_flMinLuminance, 2.5f ) : pBucket->m_flMinLuminance;
|
|||
|
pBucket->m_flMaxLuminance = pBucket->m_flMaxLuminance > 0.0f ? powf( pBucket->m_flMaxLuminance, 2.5f ) : pBucket->m_flMaxLuminance;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// The last bucket is used as a test to determine the return range for occlusion
|
|||
|
// queries to use as a scale factor. some boards (nvidia) have their occlusion
|
|||
|
// query return values larger when using AA.
|
|||
|
pBucket->m_flMinLuminance = 0.0f;
|
|||
|
pBucket->m_flMaxLuminance = 100000.0f;
|
|||
|
}
|
|||
|
|
|||
|
//Warning( "Bucket %d: min/max %f / %f ", nBucket, pBucket->m_flMinLuminance, pBucket->m_flMaxLuminance );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CTonemapSystem::SetOverrideTonemapScale( bool bEnableOverride, float flTonemapScale )
|
|||
|
{
|
|||
|
m_bOverrideTonemapScaleEnabled = bEnableOverride;
|
|||
|
m_flOverrideTonemapScale = flTonemapScale;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CTonemapSystem::DisplayHistogram()
|
|||
|
{
|
|||
|
if ( !mat_show_histogram.GetInt() || !mat_dynamic_tonemapping.GetInt() || ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) )
|
|||
|
return;
|
|||
|
|
|||
|
// Get render context
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->PushRenderTargetAndViewport();
|
|||
|
|
|||
|
// Prep variables for drawing histogram
|
|||
|
int nViewportX, nViewportY, nViewportWidth, nViewportHeight;
|
|||
|
pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
|
|||
|
|
|||
|
// Get num bins
|
|||
|
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS-1;
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 )
|
|||
|
nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW-1;
|
|||
|
|
|||
|
// Count total pixels in current bins
|
|||
|
int nMaxValidPixels = 0;
|
|||
|
int nTotalValidPixels = 0;
|
|||
|
int nTotalGraphPixelsWide = 0;
|
|||
|
for ( int nBucket = 0; nBucket < nNumHistogramBuckets; nBucket++ )
|
|||
|
{
|
|||
|
CHistogramBucket *pBucket = &( m_histogramBucketArray[ nBucket ] );
|
|||
|
if ( pBucket->ContainsValidData() )
|
|||
|
{
|
|||
|
nTotalValidPixels += pBucket->m_nPixelsInRange;
|
|||
|
if ( pBucket->m_nPixelsInRange > nMaxValidPixels )
|
|||
|
{
|
|||
|
nMaxValidPixels = pBucket->m_nPixelsInRange;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int nWidth = MAX( 1, 500 * ( pBucket->m_flMaxLuminance - pBucket->m_flMinLuminance ) );
|
|||
|
nTotalGraphPixelsWide += nWidth + 2;
|
|||
|
}
|
|||
|
|
|||
|
// Clear background to gray for screenshots
|
|||
|
//int nBoxWidth = ( nTotalGraphPixelsWide + 20 );
|
|||
|
//pRenderContext->ClearColor3ub( 150, 150, 150 );
|
|||
|
//pRenderContext->Viewport( nViewportWidth - nBoxWidth, 0, nBoxWidth, 245 );
|
|||
|
//pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// Output some text data
|
|||
|
if ( !IsX360() )
|
|||
|
{
|
|||
|
engine->Con_NPrintf( 23 + ( nViewportY / 10 ), "(Histogram luminance is in linear space)" );
|
|||
|
|
|||
|
engine->Con_NPrintf( 27 + ( nViewportY / 10 ), "AvgLum @ %4.2f%% mat_tonemap_min_avglum = %4.2f%% Using %d pixels Override(%s): %4.2f",
|
|||
|
MAX( 0.0f, FindLocationOfPercentBrightPixels( 50.0f ) ) * 100.0f, mat_tonemap_min_avglum.GetFloat(), nTotalValidPixels, m_bOverrideTonemapScaleEnabled ? "On" : "Off", m_flOverrideTonemapScale );
|
|||
|
engine->Con_NPrintf( 29 + ( nViewportY / 10 ), "BloomScale = %4.2f mat_hdr_manual_tonemap_rate = %4.2f mat_accelerate_adjust_exposure_down = %4.2f",
|
|||
|
GetCurrentBloomScale(), mat_hdr_manual_tonemap_rate.GetFloat(), mat_accelerate_adjust_exposure_down.GetFloat() );
|
|||
|
}
|
|||
|
|
|||
|
int xpStart = nViewportX + nViewportWidth - nTotalGraphPixelsWide - 10;
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
xpStart -= 50;
|
|||
|
}
|
|||
|
|
|||
|
int yOffset = 4 + nViewportY;
|
|||
|
|
|||
|
int xp = xpStart;
|
|||
|
for ( int nBucket = 0; nBucket < nNumHistogramBuckets; nBucket++ )
|
|||
|
{
|
|||
|
int np = 0;
|
|||
|
CHistogramBucket &e = m_histogramBucketArray[ nBucket ];
|
|||
|
if ( e.ContainsValidData() )
|
|||
|
np += e.m_nPixelsInRange;
|
|||
|
int width = MAX( 1, 500 * ( e.m_flMaxLuminance - e.m_flMinLuminance ) );
|
|||
|
|
|||
|
//Warning( "Bucket %d: min/max %f / %f. m_nPixelsInRange=%d m_nPixels=%d\n", nBucket, e.m_flMinLuminance, e.m_flMaxLuminance, e.m_nPixelsInRange, e.m_nPixels );
|
|||
|
|
|||
|
if ( np )
|
|||
|
{
|
|||
|
int height = MAX( 1, MIN( HISTOGRAM_BAR_SIZE, ( (float)np / (float)nMaxValidPixels ) * HISTOGRAM_BAR_SIZE ) );
|
|||
|
|
|||
|
pRenderContext->ClearColor3ub( 255, 0, 0 );
|
|||
|
pRenderContext->Viewport( xp, yOffset + HISTOGRAM_BAR_SIZE - height, width, height );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
int height = 1;
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->Viewport( xp, yOffset + HISTOGRAM_BAR_SIZE - height, width, height );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
}
|
|||
|
xp += width + 2;
|
|||
|
}
|
|||
|
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm only
|
|||
|
{
|
|||
|
float flYellowTargetPixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * mat_tonemap_percent_target.GetFloat() / 100.0f ) );
|
|||
|
float flYellowAveragePixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * mat_tonemap_min_avglum.GetFloat() / 100.0f ) );
|
|||
|
|
|||
|
float flTargetPixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetFloat() ) ) );
|
|||
|
float flAveragePixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * FindLocationOfPercentBrightPixels( 50.0f ) ) );
|
|||
|
|
|||
|
// Draw target yellow border bar
|
|||
|
int nHeight = HISTOGRAM_BAR_SIZE * 3 / 4;
|
|||
|
int nHeightOffset = -( HISTOGRAM_BAR_SIZE - nHeight ) / 2;
|
|||
|
|
|||
|
// Green is current percent target location
|
|||
|
pRenderContext->Viewport( flYellowTargetPixelStart-1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight - 2, 8, nHeight + 4 );
|
|||
|
pRenderContext->ClearColor3ub( 0, 127, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flYellowTargetPixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight );
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flTargetPixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight );
|
|||
|
pRenderContext->ClearColor3ub( 0, 255, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// Blue is average luminance location
|
|||
|
pRenderContext->Viewport( flYellowAveragePixelStart-1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight - 2, 8, nHeight + 4 );
|
|||
|
pRenderContext->ClearColor3ub( 0, 114, 188 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flYellowAveragePixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight );
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flAveragePixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight );
|
|||
|
pRenderContext->ClearColor3ub( 0, 191, 243 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
}
|
|||
|
|
|||
|
// Show actual tonemap value
|
|||
|
if ( 1 )
|
|||
|
{
|
|||
|
float flAutoExposureMin;
|
|||
|
float flAutoExposureMax;
|
|||
|
GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
|
|||
|
|
|||
|
float flBarWidth = nTotalGraphPixelsWide;
|
|||
|
float flBarStart = xpStart;
|
|||
|
|
|||
|
pRenderContext->Viewport( flBarStart, yOffset + HISTOGRAM_BAR_SIZE - 4 + 20, flBarWidth, 4 );
|
|||
|
pRenderContext->ClearColor3ub( 200, 200, 200 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flBarStart, yOffset + HISTOGRAM_BAR_SIZE - 4 + 20 + 1, flBarWidth, 2 );
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flBarStart + ( flBarWidth * ( ( m_flCurrentTonemapScale - flAutoExposureMin ) / ( flAutoExposureMax - flAutoExposureMin ) ) ) - 1,
|
|||
|
yOffset + HISTOGRAM_BAR_SIZE - 4 + 20 - 6 - 1, 4 + 2, 16 + 2 );
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->Viewport( flBarStart + ( flBarWidth * ( ( m_flCurrentTonemapScale - flAutoExposureMin ) / ( flAutoExposureMax - flAutoExposureMin ) ) ),
|
|||
|
yOffset + HISTOGRAM_BAR_SIZE - 4 + 20 - 6, 4, 16 );
|
|||
|
pRenderContext->ClearColor3ub( 255, 255, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
if ( !IsX360() )
|
|||
|
engine->Con_NPrintf( 21 + ( nViewportY / 10 ), "%.2f %.2f %.2f", flAutoExposureMin, ( flAutoExposureMax + flAutoExposureMin ) / 2.0f, flAutoExposureMax );
|
|||
|
}
|
|||
|
|
|||
|
// Last bar doesn't clear properly so draw an extra pixel
|
|||
|
pRenderContext->Viewport( 0, 0, 1, 1 );
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
pRenderContext->PopRenderTargetAndViewport();
|
|||
|
}
|
|||
|
|
|||
|
// Global postprocessing disable switch
|
|||
|
static bool s_bOverridePostProcessingDisable = false;
|
|||
|
|
|||
|
void UpdateMaterialSystemTonemapScalar()
|
|||
|
{
|
|||
|
GetCurrentTonemappingSystem()->UpdateMaterialSystemTonemapScalar();
|
|||
|
}
|
|||
|
|
|||
|
void CTonemapSystem::UpdateMaterialSystemTonemapScalar()
|
|||
|
{
|
|||
|
if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
|
|||
|
{
|
|||
|
// Deal with forced tone map scalar
|
|||
|
float flForcedTonemapScale = mat_force_tonemap_scale.GetFloat();
|
|||
|
|
|||
|
if ( mat_fullbright.GetInt() == 1 )
|
|||
|
{
|
|||
|
flForcedTonemapScale = 1.0f;
|
|||
|
}
|
|||
|
|
|||
|
if ( flForcedTonemapScale > 0.0f )
|
|||
|
{
|
|||
|
ResetTonemappingScale( flForcedTonemapScale );
|
|||
|
|
|||
|
// Send this value to the material system
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->SetToneMappingScaleLinear( Vector( m_flCurrentTonemapScale, m_flCurrentTonemapScale, m_flCurrentTonemapScale ) );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Override tone map scalar
|
|||
|
if ( m_bOverrideTonemapScaleEnabled )
|
|||
|
{
|
|||
|
float flAutoExposureMin;
|
|||
|
float flAutoExposureMax;
|
|||
|
GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
|
|||
|
|
|||
|
float fScale = clamp( m_flOverrideTonemapScale, flAutoExposureMin, flAutoExposureMax );
|
|||
|
ResetTonemappingScale( fScale );
|
|||
|
|
|||
|
// Send this value to the material system
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->SetToneMappingScaleLinear( Vector( m_flCurrentTonemapScale, m_flCurrentTonemapScale, m_flCurrentTonemapScale ) );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Send this value to the material system
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->SetToneMappingScaleLinear( Vector( m_flCurrentTonemapScale, m_flCurrentTonemapScale, m_flCurrentTonemapScale ) );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Send 1.0 to the material system since HDR is disabled
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->SetToneMappingScaleLinear( Vector( 1.0f, 1.0f, 1.0f ) );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CTonemapSystem::ResetTonemappingScale( float flTonemapScale )
|
|||
|
{
|
|||
|
if ( flTonemapScale <= 0.0f )
|
|||
|
{
|
|||
|
// L4D Hack to reset the tonemapping scale to the average of min and max since we have such dark lighting
|
|||
|
// compared to our other games. 1.0 is no longer a good value when changing spectator targets.
|
|||
|
float flAutoExposureMin = 0.0f;
|
|||
|
float flAutoExposureMax = 0.0f;
|
|||
|
GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
|
|||
|
flTonemapScale = ( flAutoExposureMin + flAutoExposureMax ) * 0.5f;
|
|||
|
flTonemapScale = clamp( flTonemapScale, 1.0f, 10.0f ); // Restrict this to the 1-10 range
|
|||
|
}
|
|||
|
|
|||
|
// Force current and target tonemap scalar
|
|||
|
m_flCurrentTonemapScale = flTonemapScale;
|
|||
|
m_flTargetTonemapScale = flTonemapScale;
|
|||
|
|
|||
|
// Clear averaging history
|
|||
|
m_nNumMovingAverageValid = 0;
|
|||
|
}
|
|||
|
|
|||
|
void CTonemapSystem::SetTargetTonemappingScale( float flTonemapScale )
|
|||
|
{
|
|||
|
Assert( IsFinite( flTonemapScale ) );
|
|||
|
if ( IsFinite( flTonemapScale ) )
|
|||
|
{
|
|||
|
m_flTargetTonemapScale = flTonemapScale;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Local contrast setting
|
|||
|
PostProcessParameters_t s_LocalPostProcessParameters[ MAX_SPLITSCREEN_PLAYERS ];
|
|||
|
|
|||
|
// view fade param settings
|
|||
|
static Vector4D s_viewFadeColor[ MAX_SPLITSCREEN_PLAYERS ];
|
|||
|
static bool s_bViewFadeModulate[ MAX_SPLITSCREEN_PLAYERS ];
|
|||
|
|
|||
|
class PPInit
|
|||
|
{
|
|||
|
public:
|
|||
|
PPInit()
|
|||
|
{
|
|||
|
for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
|
|||
|
{
|
|||
|
s_viewFadeColor[ i ].Init( 0.0f, 0.0f, 0.0f, 0.0f );
|
|||
|
s_bViewFadeModulate[ i ] = false;
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
static PPInit g_PPInit;
|
|||
|
|
|||
|
void ResetToneMapping( float flTonemappingScale )
|
|||
|
{
|
|||
|
GetCurrentTonemappingSystem()->ResetTonemappingScale( flTonemappingScale );
|
|||
|
|
|||
|
// Send this value to the material system
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
pRenderContext->SetToneMappingScaleLinear( Vector( flTonemappingScale, flTonemappingScale, flTonemappingScale ) );
|
|||
|
}
|
|||
|
|
|||
|
void CTonemapSystem::SetTonemapScale( IMatRenderContext *pRenderContext, float flTargetTonemapScalar, float flMinValue, float flMaxValue )
|
|||
|
{
|
|||
|
Assert( IsFinite( flTargetTonemapScalar ) );
|
|||
|
if ( !IsFinite( flTargetTonemapScalar ) )
|
|||
|
return;
|
|||
|
|
|||
|
//=========================================================================//
|
|||
|
// Save off new target tonemap scalar so we can compute a weighted average //
|
|||
|
//=========================================================================//
|
|||
|
if ( m_nNumMovingAverageValid < ARRAYSIZE( m_movingAverageTonemapScale ))
|
|||
|
{
|
|||
|
m_movingAverageTonemapScale[ m_nNumMovingAverageValid++ ] = flTargetTonemapScalar;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Scroll, losing oldest
|
|||
|
for ( int i = 0; i < ARRAYSIZE( m_movingAverageTonemapScale ) - 1; i++ )
|
|||
|
m_movingAverageTonemapScale[ i ] = m_movingAverageTonemapScale[ i + 1 ];
|
|||
|
m_movingAverageTonemapScale[ ARRAYSIZE( m_movingAverageTonemapScale ) - 1 ] = flTargetTonemapScalar;
|
|||
|
}
|
|||
|
|
|||
|
//==================================================================//
|
|||
|
// Compute a weighted average of the last 10 target tonemap scalars //
|
|||
|
//==================================================================//
|
|||
|
if ( m_nNumMovingAverageValid == ARRAYSIZE( m_movingAverageTonemapScale ) ) // If we have a full buffer
|
|||
|
{
|
|||
|
float flWeightedAverage = 0.0f;
|
|||
|
float flSumWeights = 0.0f;
|
|||
|
int iMidPoint = ARRAYSIZE( m_movingAverageTonemapScale ) / 2;
|
|||
|
for ( int i = 0; i < ARRAYSIZE( m_movingAverageTonemapScale ); i++ )
|
|||
|
{
|
|||
|
float flWeight = abs( i - iMidPoint ) * ( 1.0f / ( ARRAYSIZE( m_movingAverageTonemapScale ) / 2 ) );
|
|||
|
flSumWeights += flWeight;
|
|||
|
flWeightedAverage += flWeight * m_movingAverageTonemapScale[i];
|
|||
|
}
|
|||
|
flWeightedAverage *= ( 1.0f / flSumWeights );
|
|||
|
flWeightedAverage = clamp( flWeightedAverage, flMinValue, flMaxValue );
|
|||
|
|
|||
|
SetTargetTonemappingScale( flWeightedAverage );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SetTargetTonemappingScale( flTargetTonemapScalar );
|
|||
|
}
|
|||
|
|
|||
|
//=======================================//
|
|||
|
// Smoothly lerp to the target over time //
|
|||
|
//=======================================//
|
|||
|
float flElapsedTime = MAX( gpGlobals->frametime, 0.0f ); // Clamp to positive
|
|||
|
float flRate = mat_hdr_manual_tonemap_rate.GetFloat();
|
|||
|
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 )
|
|||
|
{
|
|||
|
flRate *= 2.0f; // Default 2x for the new tone mapping algorithm so it feels the same as the original
|
|||
|
}
|
|||
|
|
|||
|
if ( flRate == 0.0f ) // Zero indicates instantaneous tonemap scaling
|
|||
|
{
|
|||
|
m_flCurrentTonemapScale = m_flTargetTonemapScale;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if ( m_flTargetTonemapScale < m_flCurrentTonemapScale )
|
|||
|
{
|
|||
|
float acc_exposure_adjust = mat_accelerate_adjust_exposure_down.GetFloat();
|
|||
|
|
|||
|
// Adjust at up to 4x rate when over-exposed.
|
|||
|
flRate = MIN( ( acc_exposure_adjust * flRate ), FLerp( flRate, ( acc_exposure_adjust * flRate ), 0.0f, 1.5f, ( m_flCurrentTonemapScale - m_flTargetTonemapScale ) ) );
|
|||
|
}
|
|||
|
|
|||
|
float flRateTimesTime = flRate * flElapsedTime;
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 1 )
|
|||
|
{
|
|||
|
// For the new tone mapping algorithm, limit the rate based on the number of bins to
|
|||
|
// help reduce the tone map scalar "riding the wave" of the histogram re-building
|
|||
|
|
|||
|
//Warning( "flRateTimesTime = %.4f", flRateTimesTime );
|
|||
|
flRateTimesTime = MIN( flRateTimesTime, ( 1.0f / ( float )( NUM_HISTOGRAM_BUCKETS_NEW - 1 ) ) * 0.25f );
|
|||
|
//Warning( " --> %.4f\n", flRateTimesTime );
|
|||
|
}
|
|||
|
|
|||
|
float flAlpha = clamp( flRateTimesTime, 0.0f, 1.0f );
|
|||
|
m_flCurrentTonemapScale = ( m_flTargetTonemapScale * flAlpha ) + ( m_flCurrentTonemapScale * ( 1.0f - flAlpha ) );
|
|||
|
//m_flCurrentTonemapScale = FLerp( m_flCurrentTonemapScale, m_flTargetTonemapScale, flAlpha );
|
|||
|
|
|||
|
if ( !IsFinite( m_flCurrentTonemapScale ) )
|
|||
|
{
|
|||
|
Assert( 0 );
|
|||
|
m_flCurrentTonemapScale = m_flTargetTonemapScale;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//==========================================//
|
|||
|
// Step on values if we're forcing a scalar //
|
|||
|
//==========================================//
|
|||
|
float flForcedTonemapScale = mat_force_tonemap_scale.GetFloat();
|
|||
|
if ( flForcedTonemapScale > 0.0f )
|
|||
|
{
|
|||
|
ResetTonemappingScale( flForcedTonemapScale );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//=====================================================================================================================
|
|||
|
// Public functions for messing with tone mapping
|
|||
|
//=====================================================================================================================
|
|||
|
|
|||
|
float GetCurrentTonemapScale()
|
|||
|
{
|
|||
|
return GetCurrentTonemappingSystem()->GetCurrentTonemappingScale();
|
|||
|
}
|
|||
|
|
|||
|
void SetOverrideTonemapScale( bool bEnableOverride, float flTonemapScale )
|
|||
|
{
|
|||
|
GetCurrentTonemappingSystem()->SetOverrideTonemapScale( bEnableOverride, flTonemapScale );
|
|||
|
}
|
|||
|
|
|||
|
void SetOverridePostProcessingDisable( bool bForceOff )
|
|||
|
{
|
|||
|
s_bOverridePostProcessingDisable = bForceOff;
|
|||
|
}
|
|||
|
|
|||
|
void SetPostProcessParams( const PostProcessParameters_t *pPostProcessParameters )
|
|||
|
{
|
|||
|
int nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
|
|||
|
|
|||
|
s_LocalPostProcessParameters[ nSplitScreenSlot ] = *pPostProcessParameters;
|
|||
|
}
|
|||
|
|
|||
|
void SetViewFadeParams( byte r, byte g, byte b, byte a, bool bModulate )
|
|||
|
{
|
|||
|
int nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
|
|||
|
|
|||
|
s_viewFadeColor[ nSplitScreenSlot ].Init( float(r)/255.0f, float(g)/255.0f, float(b)/255.0f, float(a)/255.0f );
|
|||
|
s_bViewFadeModulate[ nSplitScreenSlot ] = bModulate;
|
|||
|
}
|
|||
|
|
|||
|
//=====================================================================================================================
|
|||
|
// BloomAdd material proxy ============================================================================================
|
|||
|
//=====================================================================================================================
|
|||
|
|
|||
|
class CBloomAddMaterialProxy : public CEntityMaterialProxy
|
|||
|
{
|
|||
|
public:
|
|||
|
CBloomAddMaterialProxy();
|
|||
|
virtual ~CBloomAddMaterialProxy() {}
|
|||
|
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
|
|||
|
virtual void OnBind( C_BaseEntity *pEntity );
|
|||
|
virtual IMaterial *GetMaterial();
|
|||
|
|
|||
|
private:
|
|||
|
IMaterialVar *m_pMaterialParam_BloomAmount;
|
|||
|
|
|||
|
public:
|
|||
|
static void SetBloomAmount( float flBloomAmount ) { s_flBloomAmount = flBloomAmount; }
|
|||
|
|
|||
|
private:
|
|||
|
static float s_flBloomAmount;
|
|||
|
};
|
|||
|
|
|||
|
float CBloomAddMaterialProxy::s_flBloomAmount = 1.0f;
|
|||
|
|
|||
|
CBloomAddMaterialProxy::CBloomAddMaterialProxy()
|
|||
|
: m_pMaterialParam_BloomAmount( NULL )
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
bool CBloomAddMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
|
|||
|
{
|
|||
|
bool bFoundVar = false;
|
|||
|
|
|||
|
m_pMaterialParam_BloomAmount = pMaterial->FindVar( "$c0_x", &bFoundVar, false );
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
void CBloomAddMaterialProxy::OnBind( C_BaseEntity *pEnt )
|
|||
|
{
|
|||
|
if ( m_pMaterialParam_BloomAmount )
|
|||
|
m_pMaterialParam_BloomAmount->SetFloatValue( s_flBloomAmount );
|
|||
|
}
|
|||
|
|
|||
|
IMaterial *CBloomAddMaterialProxy::GetMaterial()
|
|||
|
{
|
|||
|
if ( m_pMaterialParam_BloomAmount == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
return m_pMaterialParam_BloomAmount->GetOwningMaterial();
|
|||
|
}
|
|||
|
|
|||
|
EXPOSE_MATERIAL_PROXY( CBloomAddMaterialProxy, BloomAdd );
|
|||
|
|
|||
|
//=====================================================================================================================
|
|||
|
// Engine_Post material proxy ============================================================================================
|
|||
|
//=====================================================================================================================
|
|||
|
|
|||
|
static ConVar mat_software_aa_strength( "mat_software_aa_strength", "-1.0", 0, "Software AA - perform a software anti-aliasing post-process (an alternative/supplement to MSAA). This value sets the strength of the effect: (0.0 - off), (1.0 - full)" );
|
|||
|
static ConVar mat_software_aa_quality( "mat_software_aa_quality", "0", 0, "Software AA quality mode: (0 - 5-tap filter), (1 - 9-tap filter)" );
|
|||
|
static ConVar mat_software_aa_edge_threshold( "mat_software_aa_edge_threshold", "1.0", 0, "Software AA - adjusts the sensitivity of the software AA shader's edge detection (default 1.0 - a lower value will soften more edges, a higher value will soften fewer)" );
|
|||
|
static ConVar mat_software_aa_blur_one_pixel_lines( "mat_software_aa_blur_one_pixel_lines", "0.5", 0, "How much software AA should blur one-pixel thick lines: (0.0 - none), (1.0 - lots)" );
|
|||
|
static ConVar mat_software_aa_tap_offset( "mat_software_aa_tap_offset", "1.0", 0, "Software AA - adjusts the displacement of the taps used by the software AA shader (default 1.0 - a lower value will make the image sharper, higher will make it blurrier)" );
|
|||
|
static ConVar mat_software_aa_debug( "mat_software_aa_debug", "0", 0, "Software AA debug mode: (0 - off), (1 - show number of 'unlike' samples: 0->black, 1->red, 2->green, 3->blue), (2 - show anti-alias blend strength), (3 - show averaged 'unlike' colour)" );
|
|||
|
static ConVar mat_software_aa_strength_vgui( "mat_software_aa_strength_vgui", "-1.0", 0, "Same as mat_software_aa_strength, but forced to this value when called by the post vgui AA pass." );
|
|||
|
|
|||
|
class CEnginePostMaterialProxy : public CEntityMaterialProxy
|
|||
|
{
|
|||
|
public:
|
|||
|
CEnginePostMaterialProxy();
|
|||
|
virtual ~CEnginePostMaterialProxy();
|
|||
|
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
|
|||
|
virtual void OnBind( C_BaseEntity *pEntity );
|
|||
|
virtual IMaterial *GetMaterial();
|
|||
|
|
|||
|
private:
|
|||
|
IMaterialVar *m_pMaterialParam_AAValues;
|
|||
|
IMaterialVar *m_pMaterialParam_AAValues2;
|
|||
|
IMaterialVar *m_pMaterialParam_BloomEnable;
|
|||
|
IMaterialVar *m_pMaterialParam_BloomAmount;
|
|||
|
IMaterialVar *m_pMaterialParam_BloomUVTransform;
|
|||
|
IMaterialVar *m_pMaterialParam_ColCorrectEnable;
|
|||
|
IMaterialVar *m_pMaterialParam_ColCorrectNumLookups;
|
|||
|
IMaterialVar *m_pMaterialParam_ColCorrectDefaultWeight;
|
|||
|
IMaterialVar *m_pMaterialParam_ColCorrectLookupWeights;
|
|||
|
IMaterialVar *m_pMaterialParam_LocalContrastStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_LocalContrastEdgeStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_VignetteStart;
|
|||
|
IMaterialVar *m_pMaterialParam_VignetteEnd;
|
|||
|
IMaterialVar *m_pMaterialParam_VignetteBlurEnable;
|
|||
|
IMaterialVar *m_pMaterialParam_VignetteBlurStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_FadeToBlackStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_DepthBlurFocalDistance;
|
|||
|
IMaterialVar *m_pMaterialParam_DepthBlurStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_ScreenBlurStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_FilmGrainStrength;
|
|||
|
IMaterialVar *m_pMaterialParam_VomitEnable;
|
|||
|
IMaterialVar *m_pMaterialParam_VomitColor1;
|
|||
|
IMaterialVar *m_pMaterialParam_VomitColor2;
|
|||
|
IMaterialVar *m_pMaterialParam_FadeColor;
|
|||
|
IMaterialVar *m_pMaterialParam_FadeType;
|
|||
|
|
|||
|
public:
|
|||
|
static void SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize,
|
|||
|
bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength, float flBloomAmount );
|
|||
|
static void SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength );
|
|||
|
static void SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D destTexSize );
|
|||
|
|
|||
|
private:
|
|||
|
static float s_vBloomAAValues[4];
|
|||
|
static float s_vBloomAAValues2[4];
|
|||
|
static float s_vBloomUVTransform[4];
|
|||
|
static int s_PostBloomEnable;
|
|||
|
static float s_PostBloomAmount;
|
|||
|
};
|
|||
|
|
|||
|
float CEnginePostMaterialProxy::s_vBloomAAValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|||
|
float CEnginePostMaterialProxy::s_vBloomAAValues2[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|||
|
float CEnginePostMaterialProxy::s_vBloomUVTransform[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|||
|
int CEnginePostMaterialProxy::s_PostBloomEnable = 1;
|
|||
|
float CEnginePostMaterialProxy::s_PostBloomAmount = 1.0f;
|
|||
|
|
|||
|
CEnginePostMaterialProxy::CEnginePostMaterialProxy()
|
|||
|
{
|
|||
|
m_pMaterialParam_AAValues = NULL;
|
|||
|
m_pMaterialParam_AAValues2 = NULL;
|
|||
|
m_pMaterialParam_BloomUVTransform = NULL;
|
|||
|
m_pMaterialParam_BloomEnable = NULL;
|
|||
|
m_pMaterialParam_BloomAmount = NULL;
|
|||
|
m_pMaterialParam_ColCorrectEnable = NULL;
|
|||
|
m_pMaterialParam_ColCorrectNumLookups = NULL;
|
|||
|
m_pMaterialParam_ColCorrectDefaultWeight = NULL;
|
|||
|
m_pMaterialParam_ColCorrectLookupWeights = NULL;
|
|||
|
m_pMaterialParam_LocalContrastStrength = NULL;
|
|||
|
m_pMaterialParam_LocalContrastEdgeStrength = NULL;
|
|||
|
m_pMaterialParam_VignetteStart = NULL;
|
|||
|
m_pMaterialParam_VignetteEnd = NULL;
|
|||
|
m_pMaterialParam_VignetteBlurEnable = NULL;
|
|||
|
m_pMaterialParam_VignetteBlurStrength = NULL;
|
|||
|
m_pMaterialParam_FadeToBlackStrength = NULL;
|
|||
|
m_pMaterialParam_DepthBlurFocalDistance = NULL;
|
|||
|
m_pMaterialParam_DepthBlurStrength = NULL;
|
|||
|
m_pMaterialParam_ScreenBlurStrength = NULL;
|
|||
|
m_pMaterialParam_FilmGrainStrength = NULL;
|
|||
|
}
|
|||
|
|
|||
|
CEnginePostMaterialProxy::~CEnginePostMaterialProxy()
|
|||
|
{
|
|||
|
// Do nothing
|
|||
|
}
|
|||
|
|
|||
|
bool CEnginePostMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
|
|||
|
{
|
|||
|
bool bFoundVar = false;
|
|||
|
|
|||
|
m_pMaterialParam_AAValues = pMaterial->FindVar( "$AAInternal1", &bFoundVar, false );
|
|||
|
m_pMaterialParam_AAValues2 = pMaterial->FindVar( "$AAInternal3", &bFoundVar, false );
|
|||
|
m_pMaterialParam_BloomUVTransform = pMaterial->FindVar( "$AAInternal2", &bFoundVar, false );
|
|||
|
m_pMaterialParam_BloomEnable = pMaterial->FindVar( "$bloomEnable", &bFoundVar, false );
|
|||
|
m_pMaterialParam_BloomAmount = pMaterial->FindVar( "$bloomAmount", &bFoundVar, false );
|
|||
|
m_pMaterialParam_ColCorrectEnable = pMaterial->FindVar( "$colCorrectEnable", &bFoundVar, false );
|
|||
|
m_pMaterialParam_ColCorrectNumLookups = pMaterial->FindVar( "$colCorrect_NumLookups", &bFoundVar, false );
|
|||
|
m_pMaterialParam_ColCorrectDefaultWeight = pMaterial->FindVar( "$colCorrect_DefaultWeight", &bFoundVar, false );
|
|||
|
m_pMaterialParam_ColCorrectLookupWeights = pMaterial->FindVar( "$colCorrect_LookupWeights", &bFoundVar, false );
|
|||
|
m_pMaterialParam_LocalContrastStrength = pMaterial->FindVar( "$localContrastScale", &bFoundVar, false );
|
|||
|
m_pMaterialParam_LocalContrastEdgeStrength = pMaterial->FindVar( "$localContrastEdgeScale", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VignetteStart = pMaterial->FindVar( "$localContrastVignetteStart", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VignetteEnd = pMaterial->FindVar( "$localContrastVignetteEnd", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VignetteBlurEnable = pMaterial->FindVar( "$blurredVignetteEnable", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VignetteBlurStrength = pMaterial->FindVar( "$blurredVignetteScale", &bFoundVar, false );
|
|||
|
m_pMaterialParam_FadeToBlackStrength = pMaterial->FindVar( "$fadeToBlackScale", &bFoundVar, false );
|
|||
|
m_pMaterialParam_DepthBlurFocalDistance = pMaterial->FindVar( "$depthBlurFocalDistance", &bFoundVar, false );
|
|||
|
m_pMaterialParam_DepthBlurStrength = pMaterial->FindVar( "$depthBlurStrength", &bFoundVar, false );
|
|||
|
m_pMaterialParam_ScreenBlurStrength = pMaterial->FindVar( "$screenBlurStrength", &bFoundVar, false );
|
|||
|
m_pMaterialParam_FilmGrainStrength = pMaterial->FindVar( "$noiseScale", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VomitEnable = pMaterial->FindVar( "$vomitEnable", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VomitColor1 = pMaterial->FindVar( "$vomitColor1", &bFoundVar, false );
|
|||
|
m_pMaterialParam_VomitColor2 = pMaterial->FindVar( "$vomitColor2", &bFoundVar, false );
|
|||
|
m_pMaterialParam_FadeColor = pMaterial->FindVar( "$fadeColor", &bFoundVar, false );
|
|||
|
m_pMaterialParam_FadeType = pMaterial->FindVar( "$fade", &bFoundVar, false );
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
void CEnginePostMaterialProxy::OnBind( C_BaseEntity *pEnt )
|
|||
|
{
|
|||
|
int nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
|
|||
|
|
|||
|
if ( m_pMaterialParam_AAValues )
|
|||
|
m_pMaterialParam_AAValues->SetVecValue( s_vBloomAAValues, 4 );
|
|||
|
|
|||
|
if ( m_pMaterialParam_AAValues2 )
|
|||
|
m_pMaterialParam_AAValues2->SetVecValue( s_vBloomAAValues2, 4 );
|
|||
|
|
|||
|
if ( m_pMaterialParam_BloomUVTransform )
|
|||
|
m_pMaterialParam_BloomUVTransform->SetVecValue( s_vBloomUVTransform, 4 );
|
|||
|
|
|||
|
if ( m_pMaterialParam_BloomEnable )
|
|||
|
m_pMaterialParam_BloomEnable->SetIntValue( s_PostBloomEnable );
|
|||
|
|
|||
|
if ( m_pMaterialParam_BloomAmount )
|
|||
|
m_pMaterialParam_BloomAmount->SetFloatValue( s_PostBloomAmount );
|
|||
|
|
|||
|
if ( m_pMaterialParam_LocalContrastStrength )
|
|||
|
m_pMaterialParam_LocalContrastStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_LOCAL_CONTRAST_STRENGTH ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_LocalContrastEdgeStrength )
|
|||
|
m_pMaterialParam_LocalContrastEdgeStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_LOCAL_CONTRAST_EDGE_STRENGTH ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_VignetteStart )
|
|||
|
m_pMaterialParam_VignetteStart->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_START ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_VignetteEnd )
|
|||
|
m_pMaterialParam_VignetteEnd->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_END ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_VignetteBlurEnable )
|
|||
|
m_pMaterialParam_VignetteBlurEnable->SetIntValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ] > 0.0f ? 1 : 0 );
|
|||
|
|
|||
|
if ( m_pMaterialParam_VignetteBlurStrength )
|
|||
|
m_pMaterialParam_VignetteBlurStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_FadeToBlackStrength )
|
|||
|
m_pMaterialParam_FadeToBlackStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_FADE_TO_BLACK_STRENGTH ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_DepthBlurFocalDistance )
|
|||
|
m_pMaterialParam_DepthBlurFocalDistance->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_DEPTH_BLUR_FOCAL_DISTANCE ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_DepthBlurStrength )
|
|||
|
m_pMaterialParam_DepthBlurStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_DEPTH_BLUR_STRENGTH ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_ScreenBlurStrength )
|
|||
|
m_pMaterialParam_ScreenBlurStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_SCREEN_BLUR_STRENGTH ] );
|
|||
|
|
|||
|
if ( m_pMaterialParam_FilmGrainStrength )
|
|||
|
m_pMaterialParam_FilmGrainStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_FILM_GRAIN_STRENGTH ] );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if ( m_pMaterialParam_FadeType )
|
|||
|
{
|
|||
|
int nFadeType = ( s_bViewFadeModulate[nSplitScreenSlot] ) ? 2 : 1;
|
|||
|
nFadeType = ( s_viewFadeColor[nSplitScreenSlot][3] > 0.0f ) ? nFadeType : 0;
|
|||
|
m_pMaterialParam_FadeType->SetIntValue( nFadeType );
|
|||
|
}
|
|||
|
|
|||
|
if ( m_pMaterialParam_FadeColor )
|
|||
|
{
|
|||
|
m_pMaterialParam_FadeColor->SetVecValue( s_viewFadeColor[nSplitScreenSlot].Base(), 4 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IMaterial *CEnginePostMaterialProxy::GetMaterial()
|
|||
|
{
|
|||
|
if ( m_pMaterialParam_AAValues == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
return m_pMaterialParam_AAValues->GetOwningMaterial();
|
|||
|
}
|
|||
|
|
|||
|
void CEnginePostMaterialProxy::SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength )
|
|||
|
{
|
|||
|
if ( bPerformSoftwareAA )
|
|||
|
{
|
|||
|
// Pass ConVars to the material by proxy
|
|||
|
// - the strength of the AA effect (from 0 to 1)
|
|||
|
// - how much to allow 1-pixel lines to be blurred (from 0 to 1)
|
|||
|
// - pick one of the two quality modes (5-tap or 9-tap filter)
|
|||
|
// - optionally enable one of several debug modes (via dynamic combos)
|
|||
|
// NOTE: this order matches pixel shader constants in Engine_Post_ps2x.fxc
|
|||
|
s_vBloomAAValues[0] = flAAStrength;
|
|||
|
s_vBloomAAValues[1] = 1.0f - mat_software_aa_blur_one_pixel_lines.GetFloat();
|
|||
|
s_vBloomAAValues[2] = mat_software_aa_quality.GetInt();
|
|||
|
s_vBloomAAValues[3] = mat_software_aa_debug.GetInt();
|
|||
|
s_vBloomAAValues2[0] = mat_software_aa_edge_threshold.GetFloat();
|
|||
|
s_vBloomAAValues2[1] = mat_software_aa_tap_offset.GetFloat();
|
|||
|
//s_vBloomAAValues2[2] = unused;
|
|||
|
//s_vBloomAAValues2[3] = unused;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Zero-strength AA is interpreted as "AA disabled"
|
|||
|
s_vBloomAAValues[0] = 0.0f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CEnginePostMaterialProxy::SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D fbSize )
|
|||
|
{
|
|||
|
// Engine_Post uses a UV transform (from (quarter-res) bloom texture coords ('1')
|
|||
|
// to (full-res) framebuffer texture coords ('2')).
|
|||
|
//
|
|||
|
// We compute the UV transform as an offset and a scale, using the texture coordinates
|
|||
|
// of the top-left corner of the screen to compute the offset and the coordinate
|
|||
|
// change from the top-left to the bottom-right of the quad to compute the scale.
|
|||
|
|
|||
|
// Take texel coordinates (start = top-left, end = bottom-right):
|
|||
|
Vector2D texelStart1 = Vector2D( fullViewportBloomUVs.x, fullViewportBloomUVs.y );
|
|||
|
Vector2D texelStart2 = Vector2D( fullViewportFBUVs.x, fullViewportFBUVs.y );
|
|||
|
Vector2D texelEnd1 = Vector2D( fullViewportBloomUVs.z, fullViewportBloomUVs.w );
|
|||
|
Vector2D texelEnd2 = Vector2D( fullViewportFBUVs.z, fullViewportFBUVs.w );
|
|||
|
// ...and transform to UV coordinates:
|
|||
|
Vector2D texRes1 = fbSize / 4;
|
|||
|
Vector2D texRes2 = fbSize;
|
|||
|
Vector2D uvStart1 = ( texelStart1 + Vector2D(0.5,0.5) ) / texRes1;
|
|||
|
Vector2D uvStart2 = ( texelStart2 + Vector2D(0.5,0.5) ) / texRes2;
|
|||
|
Vector2D dUV1 = ( texelEnd1 - texelStart1 ) / texRes1;
|
|||
|
Vector2D dUV2 = ( texelEnd2 - texelStart2 ) / texRes2;
|
|||
|
|
|||
|
// We scale about the rect's top-left pixel centre (not the origin) in UV-space:
|
|||
|
// uv' = ((uv - uvStart1)*uvScale + uvStart1) + uvOffset
|
|||
|
// = uvScale*uv + uvOffset + uvStart1*(1 - uvScale)
|
|||
|
Vector2D uvOffset = uvStart2 - uvStart1;
|
|||
|
Vector2D uvScale = dUV2 / dUV1;
|
|||
|
uvOffset = uvOffset + uvStart1*(Vector2D(1,1) - uvScale);
|
|||
|
|
|||
|
s_vBloomUVTransform[0] = uvOffset.x;
|
|||
|
s_vBloomUVTransform[1] = uvOffset.y;
|
|||
|
s_vBloomUVTransform[2] = uvScale.x;
|
|||
|
s_vBloomUVTransform[3] = uvScale.y;
|
|||
|
}
|
|||
|
|
|||
|
void CEnginePostMaterialProxy::SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize,
|
|||
|
bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength, float flBloomAmount )
|
|||
|
{
|
|||
|
s_PostBloomEnable = bPerformBloom ? 1 : 0;
|
|||
|
s_PostBloomAmount = flBloomAmount;
|
|||
|
|
|||
|
SetupEnginePostMaterialAA( bPerformSoftwareAA, flAAStrength );
|
|||
|
|
|||
|
SetupEnginePostMaterialTextureTransform( fullViewportBloomUVs, fullViewportFBUVs, destTexSize );
|
|||
|
}
|
|||
|
|
|||
|
EXPOSE_MATERIAL_PROXY( CEnginePostMaterialProxy, engine_post );
|
|||
|
|
|||
|
static void DrawBloomDebugBoxes( IMatRenderContext *pRenderContext, int nX, int nY, int nWidth, int nHeight )
|
|||
|
{
|
|||
|
// draw inset rects which should have a centered bloom
|
|||
|
pRenderContext->PushRenderTargetAndViewport();
|
|||
|
pRenderContext->SetRenderTarget(NULL);
|
|||
|
|
|||
|
// full screen clear
|
|||
|
pRenderContext->Viewport( nX, nY, nWidth, nHeight );
|
|||
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// inset for screensafe
|
|||
|
int inset = 64;
|
|||
|
int size = 32;
|
|||
|
|
|||
|
// centerish, translating
|
|||
|
static int wx = 0;
|
|||
|
wx = ( wx + 1 ) & 63;
|
|||
|
|
|||
|
pRenderContext->Viewport( nWidth / 2 + nX + wx, nY + nHeight / 2, size, size );
|
|||
|
pRenderContext->ClearColor3ub( 255, 255, 255 );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// upper left
|
|||
|
pRenderContext->Viewport( nX + inset, nY + inset, size, size );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// upper right
|
|||
|
pRenderContext->Viewport( nX + nWidth - inset - size, nY + inset, size, size );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// lower right
|
|||
|
pRenderContext->Viewport( nX + nWidth - inset - size, nY + nHeight - inset - size, size, size );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// lower left
|
|||
|
pRenderContext->Viewport( nX + inset, nX + nHeight - inset - size, size, size );
|
|||
|
pRenderContext->ClearBuffers( true, true );
|
|||
|
|
|||
|
// restore
|
|||
|
pRenderContext->PopRenderTargetAndViewport();
|
|||
|
}
|
|||
|
|
|||
|
static float GetBloomAmount( void )
|
|||
|
{
|
|||
|
HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType();
|
|||
|
|
|||
|
bool bBloomEnabled = (mat_hdr_level.GetInt() >= 1);
|
|||
|
|
|||
|
if ( !engine->MapHasHDRLighting() )
|
|||
|
bBloomEnabled = false;
|
|||
|
if ( mat_force_bloom.GetInt() )
|
|||
|
bBloomEnabled = true;
|
|||
|
if ( mat_disable_bloom.GetInt() )
|
|||
|
bBloomEnabled = false;
|
|||
|
if ( building_cubemaps.GetBool() )
|
|||
|
bBloomEnabled = false;
|
|||
|
if ( mat_fullbright.GetInt() == 1 )
|
|||
|
{
|
|||
|
bBloomEnabled = false;
|
|||
|
}
|
|||
|
|
|||
|
float flBloomAmount=0.0;
|
|||
|
|
|||
|
if (bBloomEnabled)
|
|||
|
{
|
|||
|
static float currentBloomAmount = 1.0f;
|
|||
|
float rate = mat_bloomamount_rate.GetFloat();
|
|||
|
|
|||
|
// Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings.
|
|||
|
currentBloomAmount = GetCurrentBloomScale() * rate + ( 1.0f - rate ) * currentBloomAmount;
|
|||
|
flBloomAmount = currentBloomAmount;
|
|||
|
|
|||
|
if (IsX360())
|
|||
|
{
|
|||
|
//we want to scale the bloom effect down because the effect textures are lower reolution on the 360.
|
|||
|
//target match 1280x1024
|
|||
|
if ( (g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height == 720) )
|
|||
|
{
|
|||
|
flBloomAmount *= (720.0f/1024.0f);
|
|||
|
}
|
|||
|
else //640x480
|
|||
|
{
|
|||
|
flBloomAmount *= (480.0f/1024.0f);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( hdrType == HDR_TYPE_NONE )
|
|||
|
{
|
|||
|
flBloomAmount *= mat_non_hdr_bloom_scalefactor.GetFloat();
|
|||
|
}
|
|||
|
|
|||
|
flBloomAmount *= mat_bloom_scalefactor_scalar.GetFloat();
|
|||
|
|
|||
|
return flBloomAmount;
|
|||
|
}
|
|||
|
|
|||
|
static bool s_bScreenEffectTextureIsUpdated = false;
|
|||
|
|
|||
|
// WARNING: This function sets rendertarget and viewport. Save and restore is left to the caller.
|
|||
|
static void DownsampleFBQuarterSize( IMatRenderContext *pRenderContext, int nSrcWidth, int nSrcHeight, ITexture* pDest,
|
|||
|
bool bFloatHDR = false )
|
|||
|
{
|
|||
|
Assert( pRenderContext );
|
|||
|
Assert( pDest );
|
|||
|
|
|||
|
IMaterial *downsample_mat = materials->FindMaterial( bFloatHDR ? "dev/downsample" : "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true );
|
|||
|
|
|||
|
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
|
|||
|
Assert( pDest->GetActualWidth() == nSrcWidth / 4 );
|
|||
|
Assert( pDest->GetActualHeight() == nSrcHeight / 4 );
|
|||
|
|
|||
|
/*
|
|||
|
bool bFound;
|
|||
|
IMaterialVar *pbloomexpvar = downsample_mat->FindVar( "$bloomexp", &bFound, false );
|
|||
|
if ( bFound )
|
|||
|
{
|
|||
|
pbloomexpvar->SetFloatValue( g_flBloomExponent );
|
|||
|
}
|
|||
|
|
|||
|
IMaterialVar *pbloomsaturationvar = downsample_mat->FindVar( "$bloomsaturation", &bFound, false );
|
|||
|
if ( bFound )
|
|||
|
{
|
|||
|
pbloomsaturationvar->SetFloatValue( g_flBloomSaturation );
|
|||
|
}
|
|||
|
*/
|
|||
|
|
|||
|
// downsample fb to rt0
|
|||
|
SetRenderTargetAndViewPort( pDest );
|
|||
|
// note the -2's below. Thats because we are downsampling on each axis and the shader
|
|||
|
// accesses pixels on both sides of the source coord
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( downsample_mat, 0, 0, nSrcWidth/4, nSrcHeight/4,
|
|||
|
0, 0, nSrcWidth-2, nSrcHeight-2,
|
|||
|
nSrcWidth, nSrcHeight );
|
|||
|
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
pRenderContext->CopyRenderTargetToTextureEx( pDest, 0, NULL, NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void Generate8BitBloomTexture( IMatRenderContext *pRenderContext,
|
|||
|
int x, int y, int w, int h, bool bExtractBloomRange, bool bClearRGB = true )
|
|||
|
{
|
|||
|
pRenderContext->PushRenderTargetAndViewport();
|
|||
|
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
int nSrcWidth = pSrc->GetActualWidth();
|
|||
|
int nSrcHeight = pSrc->GetActualHeight(); //,nViewportHeight;
|
|||
|
|
|||
|
IMaterial *xblur_mat = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true );
|
|||
|
IMaterial *yblur_mat = NULL;
|
|||
|
if ( bClearRGB )
|
|||
|
{
|
|||
|
yblur_mat = materials->FindMaterial( "dev/blurfiltery_nohdr_clear", TEXTURE_GROUP_OTHER, true );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
yblur_mat = materials->FindMaterial( "dev/blurfiltery_nohdr", TEXTURE_GROUP_OTHER, true );
|
|||
|
}
|
|||
|
ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
|
|||
|
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
|
|||
|
Assert( dest_rt0->GetActualWidth() == pSrc->GetActualWidth() / 4 );
|
|||
|
Assert( dest_rt0->GetActualHeight() == pSrc->GetActualHeight() / 4 );
|
|||
|
Assert( dest_rt1->GetActualWidth() == pSrc->GetActualWidth() / 4 );
|
|||
|
Assert( dest_rt1->GetActualHeight() == pSrc->GetActualHeight() / 4 );
|
|||
|
|
|||
|
// downsample fb to rt0
|
|||
|
if ( bExtractBloomRange )
|
|||
|
{
|
|||
|
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// just downsample, don't apply bloom extraction math
|
|||
|
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0, true );
|
|||
|
}
|
|||
|
|
|||
|
// guassian blur x rt0 to rt1
|
|||
|
SetRenderTargetAndViewPort( dest_rt1 );
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( xblur_mat, 0, 0, nSrcWidth/4, nSrcHeight/4,
|
|||
|
0, 0, nSrcWidth/4-1, nSrcHeight/4-1,
|
|||
|
nSrcWidth/4, nSrcHeight/4 );
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
pRenderContext->CopyRenderTargetToTextureEx( dest_rt1, 0, NULL, NULL );
|
|||
|
}
|
|||
|
|
|||
|
// GR - gaussian blur y rt1 to rt0
|
|||
|
SetRenderTargetAndViewPort( dest_rt0 );
|
|||
|
IMaterialVar *pBloomAmountVar = yblur_mat->FindVar( "$bloomamount", NULL );
|
|||
|
pBloomAmountVar->SetFloatValue( 1.0f ); // the bloom amount is now applied in engine_post or bloomadd materials
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( yblur_mat, 0, 0, nSrcWidth / 4, nSrcHeight / 4,
|
|||
|
0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1,
|
|||
|
nSrcWidth / 4, nSrcHeight / 4 );
|
|||
|
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
pRenderContext->CopyRenderTargetToTextureEx( dest_rt0, 0, NULL, NULL );
|
|||
|
}
|
|||
|
|
|||
|
pRenderContext->PopRenderTargetAndViewport();
|
|||
|
}
|
|||
|
|
|||
|
static void DoTonemapping( IMatRenderContext *pRenderContext, int nX, int nY, int nWidth, int nHeight, float flAutoExposureMin, float flAutoExposureMax )
|
|||
|
{
|
|||
|
// Skip if HDR disabled
|
|||
|
if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE )
|
|||
|
return;
|
|||
|
|
|||
|
// Update HDR histogram
|
|||
|
if ( mat_dynamic_tonemapping.GetInt() )
|
|||
|
{
|
|||
|
if ( s_bScreenEffectTextureIsUpdated == false )
|
|||
|
{
|
|||
|
// FIXME: nX/nY/nWidth/nHeight are used here, but the equivalent parameters are ignored in Generate8BitBloomTexture
|
|||
|
UpdateScreenEffectTexture( 0, nX, nY, nWidth, nHeight, false );
|
|||
|
s_bScreenEffectTextureIsUpdated = true;
|
|||
|
}
|
|||
|
|
|||
|
GetCurrentTonemappingSystem()->IssueAndReceiveBucketQueries();
|
|||
|
|
|||
|
float flTargetScalar = GetCurrentTonemappingSystem()->ComputeTargetTonemapScalar();
|
|||
|
float flTargetScalarClamped = MAX( flAutoExposureMin, MIN( flAutoExposureMax, flTargetScalar ) );
|
|||
|
flTargetScalarClamped = MAX( 0.001f, flTargetScalarClamped ); // Don't let this go to 0!
|
|||
|
GetCurrentTonemappingSystem()->SetTonemapScale( pRenderContext, flTargetScalarClamped, flAutoExposureMin, flAutoExposureMax );
|
|||
|
|
|||
|
if ( mat_show_histogram.GetInt() )
|
|||
|
{
|
|||
|
bool bDrawTextThisFrame = true;
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
static float s_flLastTimeUpdate = 0.0f;
|
|||
|
if ( int( gpGlobals->curtime ) - int( s_flLastTimeUpdate ) >= 2 )
|
|||
|
{
|
|||
|
s_flLastTimeUpdate = gpGlobals->curtime;
|
|||
|
bDrawTextThisFrame = true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bDrawTextThisFrame = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( bDrawTextThisFrame == true )
|
|||
|
{
|
|||
|
if ( mat_tonemap_algorithm.GetInt() == 0 )
|
|||
|
{
|
|||
|
engine->Con_NPrintf( 25 + ( nY / 10 ), "(Original algorithm) Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Current Scalar: %4.2f",
|
|||
|
flTargetScalar, flAutoExposureMin, flAutoExposureMax, GetCurrentTonemappingSystem()->GetCurrentTonemappingScale() );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
engine->Con_NPrintf( 25 + ( nY / 10 ), "[mat_show_histogram] Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Final Scalar: %4.2f\n",
|
|||
|
GetCurrentTonemappingSystem()->ComputeTargetTonemapScalar( true ), flAutoExposureMin, flAutoExposureMax, GetCurrentTonemappingSystem()->GetCurrentTonemappingScale() );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
engine->Con_NPrintf( 25 + ( nY / 10 ), "%.2f%% of pixels above %d%% target @ %4.2f%% Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Final Scalar: %4.2f",
|
|||
|
mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetInt(),
|
|||
|
( GetCurrentTonemappingSystem()->FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetFloat() ) * 100.0f ),
|
|||
|
GetCurrentTonemappingSystem()->ComputeTargetTonemapScalar( true ), flAutoExposureMin, flAutoExposureMax, GetCurrentTonemappingSystem()->GetCurrentTonemappingScale() );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void CenterScaleQuadUVs( Vector4D & quadUVs, const Vector2D & uvScale )
|
|||
|
{
|
|||
|
Vector2D uvMid = 0.5f*Vector2D( ( quadUVs.z + quadUVs.x ), ( quadUVs.w + quadUVs.y ) );
|
|||
|
Vector2D uvRange= 0.5f*Vector2D( ( quadUVs.z - quadUVs.x ), ( quadUVs.w - quadUVs.y ) );
|
|||
|
quadUVs.x = uvMid.x - uvScale.x*uvRange.x;
|
|||
|
quadUVs.y = uvMid.y - uvScale.y*uvRange.y;
|
|||
|
quadUVs.z = uvMid.x + uvScale.x*uvRange.x;
|
|||
|
quadUVs.w = uvMid.y + uvScale.y*uvRange.y;
|
|||
|
}
|
|||
|
|
|||
|
static ConVar r_queued_post_processing( "r_queued_post_processing", "0" );
|
|||
|
|
|||
|
// How much to dice up the screen during post-processing on 360
|
|||
|
// This has really marginal effects, but 4x1 does seem vaguely better for post-processing
|
|||
|
static ConVar mat_postprocess_x( "mat_postprocess_x", "4" );
|
|||
|
static ConVar mat_postprocess_y( "mat_postprocess_y", "1" );
|
|||
|
static ConVar mat_postprocess_enable( "mat_postprocess_enable", "1", FCVAR_CHEAT );
|
|||
|
|
|||
|
void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, bool bPostVGui )
|
|||
|
{
|
|||
|
// don't do this if disabled or in alt-tab
|
|||
|
if ( s_bOverridePostProcessingDisable || w <=0 || h <= 0 )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
|
|||
|
if ( r_queued_post_processing.GetInt() )
|
|||
|
{
|
|||
|
ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
|
|||
|
if ( pCallQueue )
|
|||
|
{
|
|||
|
pCallQueue->QueueCall( DoEnginePostProcessing, x, y, w, h, bFlashlightIsOn, bPostVGui );
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if defined( _X360 )
|
|||
|
pRenderContext->PushVertexShaderGPRAllocation( 16 ); //max out pixel shader threads
|
|||
|
#endif
|
|||
|
|
|||
|
GetTonemapSettingsFromEnvTonemapController();
|
|||
|
|
|||
|
g_bFlashlightIsOn = bFlashlightIsOn;
|
|||
|
|
|||
|
// Use the appropriate autoexposure min / max settings.
|
|||
|
// Mapmaker's overrides the convar settings.
|
|||
|
float flAutoExposureMin;
|
|||
|
float flAutoExposureMax;
|
|||
|
GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
|
|||
|
|
|||
|
if ( mat_debug_bloom.GetInt() == 1 )
|
|||
|
{
|
|||
|
DrawBloomDebugBoxes( pRenderContext, x, y, w, h );
|
|||
|
}
|
|||
|
|
|||
|
s_bScreenEffectTextureIsUpdated = false; // Force an update in tone mapping code
|
|||
|
DoTonemapping( pRenderContext, x, y, w, h, flAutoExposureMin, flAutoExposureMax );
|
|||
|
|
|||
|
if ( mat_postprocess_enable.GetInt() == 0 )
|
|||
|
{
|
|||
|
GetCurrentTonemappingSystem()->DisplayHistogram();
|
|||
|
|
|||
|
#if defined( _X360 )
|
|||
|
pRenderContext->PopVertexShaderGPRAllocation();
|
|||
|
#endif
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Set software-AA on by default for 360
|
|||
|
if ( mat_software_aa_strength.GetFloat() == -1.0f )
|
|||
|
{
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
mat_software_aa_strength.SetValue( 1.0f );
|
|||
|
if ( g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height > 480 )
|
|||
|
{
|
|||
|
mat_software_aa_quality.SetValue( 0 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// For standard-def, we have fewer pixels so we can afford 'high quality' mode (5->9 taps/pixel)
|
|||
|
mat_software_aa_quality.SetValue( 1 );
|
|||
|
|
|||
|
// Disable in 480p for now
|
|||
|
mat_software_aa_strength.SetValue( 0.0f );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mat_software_aa_strength.SetValue( 0.0f );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Same trick for setting up the vgui aa strength
|
|||
|
if ( mat_software_aa_strength_vgui.GetFloat() == -1.0f )
|
|||
|
{
|
|||
|
if ( IsX360() && (g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height == 720) )
|
|||
|
{
|
|||
|
mat_software_aa_strength_vgui.SetValue( 2.0f );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mat_software_aa_strength_vgui.SetValue( 1.0f );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
float flAAStrength;
|
|||
|
|
|||
|
// We do a second AA blur pass over the TF intro menus. use mat_software_aa_strength_vgui there instead
|
|||
|
if ( IsX360() && bPostVGui )
|
|||
|
{
|
|||
|
flAAStrength = mat_software_aa_strength_vgui.GetFloat();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
flAAStrength = mat_software_aa_strength.GetFloat();
|
|||
|
}
|
|||
|
|
|||
|
// Bloom, software-AA and color-correction (applied in 1 pass, after generation of the bloom texture)
|
|||
|
float flBloomScale = GetBloomAmount();
|
|||
|
bool bPerformSoftwareAA = ( flAAStrength != 0.0f );
|
|||
|
bool bPerformBloom = !bPostVGui && ( flBloomScale > 0.0f );
|
|||
|
bool bPerformColCorrect = !bPostVGui &&
|
|||
|
g_pColorCorrectionMgr->HasNonZeroColorCorrectionWeights() &&
|
|||
|
mat_colorcorrection.GetInt();
|
|||
|
|
|||
|
pRenderContext->EnableColorCorrection( bPerformColCorrect );
|
|||
|
|
|||
|
bool bPerformLocalContrastEnhancement = false;
|
|||
|
IMaterial* pPostMat;
|
|||
|
if ( engine->IsSplitScreenActive() )
|
|||
|
pPostMat = materials->FindMaterial( "dev/engine_post_splitscreen", TEXTURE_GROUP_OTHER, true );
|
|||
|
else
|
|||
|
pPostMat = materials->FindMaterial( "dev/engine_post", TEXTURE_GROUP_OTHER, true );
|
|||
|
if ( pPostMat )
|
|||
|
{
|
|||
|
IMaterialVar* pMatVar = pPostMat->FindVar( "$localcontrastenable", NULL, false );
|
|||
|
if ( pMatVar )
|
|||
|
{
|
|||
|
bPerformLocalContrastEnhancement = pMatVar->GetIntValue() && mat_local_contrast_enable.GetBool();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( true )
|
|||
|
{
|
|||
|
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
int nSrcWidth = pSrc->GetActualWidth();
|
|||
|
int nSrcHeight = pSrc->GetActualHeight();
|
|||
|
|
|||
|
ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
|
|||
|
if ( !s_bScreenEffectTextureIsUpdated )
|
|||
|
{
|
|||
|
UpdateScreenEffectTexture( 0, x, y, w, h, false );
|
|||
|
s_bScreenEffectTextureIsUpdated = true;
|
|||
|
}
|
|||
|
|
|||
|
if ( bPerformBloom || bPerformLocalContrastEnhancement )
|
|||
|
{
|
|||
|
Generate8BitBloomTexture( pRenderContext, x, y, w, h, true, false );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Now add bloom (dest_rt0) to the framebuffer and perform software anti-aliasing and
|
|||
|
// colour correction, all in one pass (improves performance, reduces quantization errors)
|
|||
|
//
|
|||
|
// First, set up texel coords (in the bloom and fb textures) at the centres of the outer pixel of the viewport:
|
|||
|
float flFbWidth = ( float )pSrc->GetActualWidth();
|
|||
|
float flFbHeight = ( float )pSrc->GetActualHeight();
|
|||
|
|
|||
|
Vector4D fullViewportPostSrcCorners( 0.0f, -0.5f, nSrcWidth/4-1, nSrcHeight/4-1 );
|
|||
|
Vector4D fullViewportPostSrcRect( nSrcWidth * ( ( x + 0 ) / flFbWidth ) / 4.0f + 0.0f, nSrcHeight * ( ( y + 0 ) / flFbHeight ) / 4.0f - 0.5f,
|
|||
|
nSrcWidth * ( ( x + w ) / flFbWidth ) / 4.0f - 1.0f, nSrcHeight * ( ( y + h ) / flFbHeight ) / 4.0f - 1.0f );
|
|||
|
Vector4D fullViewportPostDestCorners( 0.0f, 0.0f, nSrcWidth - 1, nSrcHeight - 1 );
|
|||
|
Rect_t fullViewportPostDestRect = { x, y, w, h };
|
|||
|
Vector2D destTexSize( nSrcWidth, nSrcHeight );
|
|||
|
|
|||
|
// When the viewport is not fullscreen, the UV-space size of a pixel changes
|
|||
|
// (due to a stretchrect blit being used in UpdateScreenEffectTexture()), so
|
|||
|
// we need to adjust the corner-pixel UVs sent to our drawrect call:
|
|||
|
Vector2D uvScale( ( nSrcWidth - ( nSrcWidth / (float)w ) ) / ( nSrcWidth - 1 ),
|
|||
|
( nSrcHeight - ( nSrcHeight / (float)h ) ) / ( nSrcHeight - 1 ) );
|
|||
|
CenterScaleQuadUVs( fullViewportPostSrcCorners, uvScale );
|
|||
|
CenterScaleQuadUVs( fullViewportPostDestCorners, uvScale );
|
|||
|
|
|||
|
Rect_t partialViewportPostDestRect = fullViewportPostDestRect;
|
|||
|
Vector4D partialViewportPostSrcCorners = fullViewportPostSrcCorners;
|
|||
|
if ( debug_postproc.GetInt() == 2 )
|
|||
|
{
|
|||
|
// Restrict the post effects to the centre quarter of the screen
|
|||
|
// (we only use a portion of the bloom texture, so this *does* affect bloom texture UVs)
|
|||
|
partialViewportPostDestRect.x += 0.25f*fullViewportPostDestRect.width;
|
|||
|
partialViewportPostDestRect.y += 0.25f*fullViewportPostDestRect.height;
|
|||
|
partialViewportPostDestRect.width -= 0.50f*fullViewportPostDestRect.width;
|
|||
|
partialViewportPostDestRect.height -= 0.50f*fullViewportPostDestRect.height;
|
|||
|
|
|||
|
// This math interprets texel coords as being at corner pixel centers (*not* at corner vertices):
|
|||
|
Vector2D uvScale( 1.0f - ( (w / 2) / (float)(w - 1) ),
|
|||
|
1.0f - ( (h / 2) / (float)(h - 1) ) );
|
|||
|
CenterScaleQuadUVs( partialViewportPostSrcCorners, uvScale );
|
|||
|
}
|
|||
|
|
|||
|
// Temporary hack... Color correction was crashing on the first frame
|
|||
|
// when run outside the debugger for some mods (DoD). This forces it to skip
|
|||
|
// a frame, ensuring we don't get the weird texture crash we otherwise would.
|
|||
|
// FIXME: This will be removed when the true cause is found [added: Main CL 144694]
|
|||
|
static bool bFirstFrame = !IsX360();
|
|||
|
if ( !bFirstFrame || !bPerformColCorrect )
|
|||
|
{
|
|||
|
HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType();
|
|||
|
if ( hdrType == HDR_TYPE_FLOAT )
|
|||
|
{
|
|||
|
// reset to render the final combine passes to the "real" display backbuffer
|
|||
|
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_BACK_BUFFER_INDEX, BACK_BUFFER_INDEX_DEFAULT );
|
|||
|
pRenderContext->SetRenderTarget( NULL );
|
|||
|
}
|
|||
|
|
|||
|
Vector4D v4dFullViewportPostDestRect( fullViewportPostDestRect.x, fullViewportPostDestRect.y,
|
|||
|
fullViewportPostDestRect.x + fullViewportPostDestRect.width - 1,
|
|||
|
fullViewportPostDestRect.y + fullViewportPostDestRect.height - 1 );
|
|||
|
|
|||
|
CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcRect, v4dFullViewportPostDestRect, destTexSize, bPerformSoftwareAA, bPerformBloom, bPerformColCorrect, flAAStrength, flBloomScale );
|
|||
|
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( pPostMat,
|
|||
|
0, 0,
|
|||
|
partialViewportPostDestRect.width, partialViewportPostDestRect.height,
|
|||
|
fullViewportPostSrcRect.x, fullViewportPostSrcRect.y,
|
|||
|
fullViewportPostSrcRect.z, fullViewportPostSrcRect.w,
|
|||
|
|
|||
|
dest_rt1->GetActualWidth(), dest_rt1->GetActualHeight(),
|
|||
|
GetClientWorldEntity()->GetClientRenderable(),
|
|||
|
mat_postprocess_x.GetInt(), mat_postprocess_y.GetInt() );
|
|||
|
}
|
|||
|
bFirstFrame = false;
|
|||
|
}
|
|||
|
|
|||
|
GetCurrentTonemappingSystem()->DisplayHistogram();
|
|||
|
|
|||
|
#if defined( _X360 )
|
|||
|
pRenderContext->PopVertexShaderGPRAllocation();
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
void DoBlurFade( float flStrength, float flDesaturate, int x, int y, int w, int h )
|
|||
|
{
|
|||
|
if ( flStrength < 0.0001f )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
UpdateScreenEffectTexture();
|
|||
|
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
Generate8BitBloomTexture( pRenderContext, x, y, w, h, false, false );
|
|||
|
|
|||
|
int nViewportX, nViewportY, nViewportWidth, nViewportHeight;
|
|||
|
pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
|
|||
|
|
|||
|
int nRtWidth, nRtHeight;
|
|||
|
pRenderContext->GetRenderTargetDimensions( nRtWidth, nRtHeight );
|
|||
|
|
|||
|
IMaterial* pMat = materials->FindMaterial( "dev/fade_blur", TEXTURE_GROUP_OTHER, true );
|
|||
|
bool bFound = false;
|
|||
|
IMaterialVar* pVar = pMat->FindVar( "$c0_x", &bFound );
|
|||
|
if ( pVar )
|
|||
|
{
|
|||
|
pVar->SetFloatValue( flStrength );
|
|||
|
}
|
|||
|
|
|||
|
// Desaturate strength
|
|||
|
pVar = pMat->FindVar( "$c1_x", &bFound );
|
|||
|
if ( pVar )
|
|||
|
{
|
|||
|
pVar->SetFloatValue( flDesaturate );
|
|||
|
}
|
|||
|
|
|||
|
pRenderContext->DrawScreenSpaceRectangle( pMat, 0, 0, nViewportWidth, nViewportHeight,
|
|||
|
nViewportX, nViewportY,
|
|||
|
nViewportX + nViewportWidth - 1, nViewportY + nViewportHeight - 1,
|
|||
|
nRtWidth, nRtHeight );
|
|||
|
}
|
|||
|
|
|||
|
// Motion Blur Material Proxy =========================================================================================
|
|||
|
static float g_vMotionBlurValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|||
|
static float g_vMotionBlurViewportValues[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
|
|||
|
class CMotionBlurMaterialProxy : public CEntityMaterialProxy
|
|||
|
{
|
|||
|
public:
|
|||
|
CMotionBlurMaterialProxy();
|
|||
|
virtual ~CMotionBlurMaterialProxy();
|
|||
|
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
|
|||
|
virtual void OnBind( C_BaseEntity *pEntity );
|
|||
|
virtual IMaterial *GetMaterial();
|
|||
|
|
|||
|
private:
|
|||
|
IMaterialVar *m_pMaterialParam;
|
|||
|
IMaterialVar *m_pMaterialParamViewport;
|
|||
|
};
|
|||
|
|
|||
|
CMotionBlurMaterialProxy::CMotionBlurMaterialProxy()
|
|||
|
{
|
|||
|
m_pMaterialParam = NULL;
|
|||
|
}
|
|||
|
|
|||
|
CMotionBlurMaterialProxy::~CMotionBlurMaterialProxy()
|
|||
|
{
|
|||
|
// Do nothing
|
|||
|
}
|
|||
|
|
|||
|
bool CMotionBlurMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
|
|||
|
{
|
|||
|
bool bFoundVar = false;
|
|||
|
|
|||
|
m_pMaterialParam = pMaterial->FindVar( "$MotionBlurInternal", &bFoundVar, false );
|
|||
|
if ( bFoundVar == false)
|
|||
|
return false;
|
|||
|
|
|||
|
m_pMaterialParamViewport = pMaterial->FindVar( "$MotionBlurViewportInternal", &bFoundVar, false );
|
|||
|
if ( bFoundVar == false)
|
|||
|
return false;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
void CMotionBlurMaterialProxy::OnBind( C_BaseEntity *pEnt )
|
|||
|
{
|
|||
|
if ( m_pMaterialParam != NULL )
|
|||
|
{
|
|||
|
m_pMaterialParam->SetVecValue( g_vMotionBlurValues, 4 );
|
|||
|
}
|
|||
|
|
|||
|
if ( m_pMaterialParamViewport != NULL )
|
|||
|
{
|
|||
|
m_pMaterialParamViewport->SetVecValue( g_vMotionBlurViewportValues, 4 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IMaterial *CMotionBlurMaterialProxy::GetMaterial()
|
|||
|
{
|
|||
|
if ( m_pMaterialParam == NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
return m_pMaterialParam->GetOwningMaterial();
|
|||
|
}
|
|||
|
|
|||
|
EXPOSE_MATERIAL_PROXY( CMotionBlurMaterialProxy, MotionBlur );
|
|||
|
|
|||
|
//=====================================================================================================================
|
|||
|
// Image-space Motion Blur ============================================================================================
|
|||
|
//=====================================================================================================================
|
|||
|
ConVar mat_motion_blur_enabled( "mat_motion_blur_enabled", "1" );
|
|||
|
|
|||
|
ConVar mat_motion_blur_forward_enabled( "mat_motion_blur_forward_enabled", "0" );
|
|||
|
ConVar mat_motion_blur_falling_min( "mat_motion_blur_falling_min", "10.0" );
|
|||
|
|
|||
|
ConVar mat_motion_blur_falling_max( "mat_motion_blur_falling_max", "20.0" );
|
|||
|
ConVar mat_motion_blur_falling_intensity( "mat_motion_blur_falling_intensity", "1.0" );
|
|||
|
//ConVar mat_motion_blur_roll_intensity( "mat_motion_blur_roll_intensity", "1.0" );
|
|||
|
ConVar mat_motion_blur_rotation_intensity( "mat_motion_blur_rotation_intensity", "1.0" );
|
|||
|
ConVar mat_motion_blur_strength( "mat_motion_blur_strength", "1.0" );
|
|||
|
|
|||
|
struct MotionBlurHistory_t
|
|||
|
{
|
|||
|
MotionBlurHistory_t()
|
|||
|
{
|
|||
|
m_flLastTimeUpdate = 0.0f;
|
|||
|
m_flPreviousPitch = 0.0f;
|
|||
|
m_flPreviousYaw = 0.0f;
|
|||
|
m_vPreviousPositon.Init( 0.0f, 0.0f, 0.0f );
|
|||
|
m_mPreviousFrameBasisVectors;
|
|||
|
m_flNoRotationalMotionBlurUntil = 0.0f;
|
|||
|
SetIdentityMatrix( m_mPreviousFrameBasisVectors );
|
|||
|
}
|
|||
|
|
|||
|
float m_flLastTimeUpdate;
|
|||
|
float m_flPreviousPitch;
|
|||
|
float m_flPreviousYaw;
|
|||
|
Vector m_vPreviousPositon;
|
|||
|
matrix3x4_t m_mPreviousFrameBasisVectors;
|
|||
|
float m_flNoRotationalMotionBlurUntil;
|
|||
|
};
|
|||
|
|
|||
|
void DoImageSpaceMotionBlur( const CViewSetup &view )
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
if ( ( !mat_motion_blur_enabled.GetInt() ) || ( view.m_nMotionBlurMode == MOTION_BLUR_DISABLE ) )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
int x = view.x;
|
|||
|
int y = view.y;
|
|||
|
int w = view.width;
|
|||
|
int h = view.height;
|
|||
|
|
|||
|
bool bSFMBlur = ( view.m_nMotionBlurMode == MOTION_BLUR_SFM );
|
|||
|
|
|||
|
//======================================================================================================//
|
|||
|
// Get these convars here to make it easier to remove them later and to default each client differently //
|
|||
|
//======================================================================================================//
|
|||
|
float flMotionBlurRotationIntensity = mat_motion_blur_rotation_intensity.GetFloat() * 0.15f; // The default is to not blur past 15% of the range
|
|||
|
float flMotionBlurRollIntensity = 0.3f; // * mat_motion_blur_roll_intensity.GetFloat(); // The default is to not blur past 30% of the range
|
|||
|
float flMotionBlurFallingIntensity = mat_motion_blur_falling_intensity.GetFloat();
|
|||
|
float flMotionBlurFallingMin = mat_motion_blur_falling_min.GetFloat();
|
|||
|
float flMotionBlurFallingMax = mat_motion_blur_falling_max.GetFloat();
|
|||
|
float flMotionBlurGlobalStrength = mat_motion_blur_strength.GetFloat();
|
|||
|
|
|||
|
//===============================================================================//
|
|||
|
// Set global g_vMotionBlurValues[4] values so material proxy can get the values //
|
|||
|
//===============================================================================//
|
|||
|
if ( true )
|
|||
|
{
|
|||
|
//=====================//
|
|||
|
// Previous frame data //
|
|||
|
//=====================//
|
|||
|
static MotionBlurHistory_t s_History[ MAX_SPLITSCREEN_PLAYERS ];
|
|||
|
|
|||
|
ASSERT_LOCAL_PLAYER_RESOLVABLE();
|
|||
|
MotionBlurHistory_t &history = s_History[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
|
|||
|
|
|||
|
//float vPreviousSideVec[3] = { s_mPreviousFrameBasisVectors[0][1], s_mPreviousFrameBasisVectors[1][1], s_mPreviousFrameBasisVectors[2][1] };
|
|||
|
//float vPreviousForwardVec[3] = { s_mPreviousFrameBasisVectors[0][0], s_mPreviousFrameBasisVectors[1][0], s_mPreviousFrameBasisVectors[2][0] };
|
|||
|
//float vPreviousUpVec[3] = { s_mPreviousFrameBasisVectors[0][2], s_mPreviousFrameBasisVectors[1][2], s_mPreviousFrameBasisVectors[2][2] };
|
|||
|
|
|||
|
float flTimeElapsed;
|
|||
|
|
|||
|
// Motion blur driven by CViewSetup, not engine time (currently only driven by SFM)
|
|||
|
if ( bSFMBlur )
|
|||
|
{
|
|||
|
history.m_flLastTimeUpdate = 0.0f; // Don't care about these, but zero them out
|
|||
|
history.m_flNoRotationalMotionBlurUntil = 0.0f; //
|
|||
|
|
|||
|
flTimeElapsed = view.m_flShutterTime;
|
|||
|
|
|||
|
history.m_vPreviousPositon[0] = view.m_vShutterOpenPosition.x; //
|
|||
|
history.m_vPreviousPositon[1] = view.m_vShutterOpenPosition.y; // Slam "previous" values to shutter open values
|
|||
|
history.m_vPreviousPositon[2] = view.m_vShutterOpenPosition.z; //
|
|||
|
AngleMatrix( view.m_shutterOpenAngles, history.m_mPreviousFrameBasisVectors );//
|
|||
|
|
|||
|
history.m_flPreviousPitch = view.m_shutterOpenAngles[PITCH]; // Get "previous" pitch & wrap to +-180
|
|||
|
while ( history.m_flPreviousPitch > 180.0f )
|
|||
|
history.m_flPreviousPitch -= 360.0f;
|
|||
|
while ( history.m_flPreviousPitch < -180.0f )
|
|||
|
history.m_flPreviousPitch += 360.0f;
|
|||
|
|
|||
|
history.m_flPreviousYaw = view.m_shutterOpenAngles[YAW]; // Get "previous" yaw & wrap to +-180
|
|||
|
while ( history.m_flPreviousYaw > 180.0f )
|
|||
|
history.m_flPreviousYaw -= 360.0f;
|
|||
|
while ( history.m_flPreviousYaw < -180.0f )
|
|||
|
history.m_flPreviousYaw += 360.0f;
|
|||
|
}
|
|||
|
else // view.m_nDoMotionBlurMode == MOTION_BLUR_GAME
|
|||
|
{
|
|||
|
flTimeElapsed = gpGlobals->realtime - history.m_flLastTimeUpdate;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//===================================//
|
|||
|
// Get current pitch & wrap to +-180 //
|
|||
|
//===================================//
|
|||
|
float flCurrentPitch = view.angles[PITCH];
|
|||
|
if ( bSFMBlur )
|
|||
|
flCurrentPitch = view.m_shutterCloseAngles[PITCH];
|
|||
|
while ( flCurrentPitch > 180.0f )
|
|||
|
flCurrentPitch -= 360.0f;
|
|||
|
while ( flCurrentPitch < -180.0f )
|
|||
|
flCurrentPitch += 360.0f;
|
|||
|
|
|||
|
//=================================//
|
|||
|
// Get current yaw & wrap to +-180 //
|
|||
|
//=================================//
|
|||
|
float flCurrentYaw = view.angles[YAW];
|
|||
|
if ( bSFMBlur )
|
|||
|
flCurrentYaw = view.m_shutterCloseAngles[YAW];
|
|||
|
while ( flCurrentYaw > 180.0f )
|
|||
|
flCurrentYaw -= 360.0f;
|
|||
|
while ( flCurrentYaw < -180.0f )
|
|||
|
flCurrentYaw += 360.0f;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*engine->Con_NPrintf( 0, "Blur Pitch: %6.2f Yaw: %6.2f", flCurrentPitch, flCurrentYaw );
|
|||
|
engine->Con_NPrintf( 1, "Blur FOV: %6.2f Aspect: %6.2f Ortho: %s", view.fov, view.m_flAspectRatio, view.m_bOrtho ? "Yes" : "No" );
|
|||
|
engine->Con_NPrintf( 2, "View Angles: %6.2f %6.2f %6.2f", XYZ(view.angles) );*/
|
|||
|
|
|||
|
//===========================//
|
|||
|
// Get current basis vectors //
|
|||
|
//===========================//
|
|||
|
matrix3x4_t mCurrentBasisVectors;
|
|||
|
|
|||
|
if ( bSFMBlur )
|
|||
|
{
|
|||
|
AngleMatrix( view.m_shutterCloseAngles, mCurrentBasisVectors );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
AngleMatrix( view.angles, mCurrentBasisVectors );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Vector vCurrentSideVec( mCurrentBasisVectors[0][1], mCurrentBasisVectors[1][1], mCurrentBasisVectors[2][1] );
|
|||
|
Vector vCurrentForwardVec( mCurrentBasisVectors[0][0], mCurrentBasisVectors[1][0], mCurrentBasisVectors[2][0] );
|
|||
|
//Vector vCurrentUpVec( mCurrentBasisVectors[0][2], mCurrentBasisVectors[1][2], mCurrentBasisVectors[2][2] );
|
|||
|
|
|||
|
//===========================================================================//
|
|||
|
// Get current position (shutter close time when SFM is driving motion blur) //
|
|||
|
//===========================================================================//
|
|||
|
Vector vCurrentPosition = view.origin;
|
|||
|
|
|||
|
if ( bSFMBlur )
|
|||
|
{
|
|||
|
vCurrentPosition[0] = view.m_vShutterClosePosition.x;
|
|||
|
vCurrentPosition[1] = view.m_vShutterClosePosition.y;
|
|||
|
vCurrentPosition[2] = view.m_vShutterClosePosition.z;
|
|||
|
}
|
|||
|
|
|||
|
//===============================================================//
|
|||
|
// Evaluate change in position to determine if we need to update //
|
|||
|
//===============================================================//
|
|||
|
Vector vPositionChange( 0.0f, 0.0f, 0.0f );
|
|||
|
VectorSubtract( history.m_vPreviousPositon, vCurrentPosition, vPositionChange );
|
|||
|
if ( ( VectorLength( vPositionChange ) > 30.0f ) && ( flTimeElapsed >= 0.5f ) && !bSFMBlur )
|
|||
|
{
|
|||
|
//=======================================================//
|
|||
|
// If we moved a far distance in one frame and more than //
|
|||
|
// half a second elapsed, disable motion blur this frame //
|
|||
|
//=======================================================//
|
|||
|
//engine->Con_NPrintf( 8, " Pos change && time > 0.5 seconds %f ", gpGlobals->realtime );
|
|||
|
|
|||
|
g_vMotionBlurValues[0] = 0.0f;
|
|||
|
g_vMotionBlurValues[1] = 0.0f;
|
|||
|
g_vMotionBlurValues[2] = 0.0f;
|
|||
|
g_vMotionBlurValues[3] = 0.0f;
|
|||
|
}
|
|||
|
else if ( ( flTimeElapsed > ( 1.0f / 15.0f ) ) && !bSFMBlur )
|
|||
|
{
|
|||
|
//==========================================//
|
|||
|
// If slower than 15 fps, don't motion blur //
|
|||
|
//==========================================//
|
|||
|
g_vMotionBlurValues[0] = 0.0f;
|
|||
|
g_vMotionBlurValues[1] = 0.0f;
|
|||
|
g_vMotionBlurValues[2] = 0.0f;
|
|||
|
g_vMotionBlurValues[3] = 0.0f;
|
|||
|
}
|
|||
|
else if ( ( VectorLength( vPositionChange ) > 50.0f ) && !bSFMBlur )
|
|||
|
{
|
|||
|
//================================================================================//
|
|||
|
// We moved a far distance in a frame, use the same motion blur as last frame //
|
|||
|
// because I think we just went through a portal (should we ifdef this behavior?) //
|
|||
|
//================================================================================//
|
|||
|
//engine->Con_NPrintf( 8, " Position changed %f units @ %.2f time ", VectorLength( vPositionChange ), gpGlobals->realtime );
|
|||
|
|
|||
|
history.m_flNoRotationalMotionBlurUntil = gpGlobals->realtime + 1.0f; // Wait a second until the portal craziness calms down
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//====================//
|
|||
|
// Normal update path //
|
|||
|
//====================//
|
|||
|
// Compute horizontal and vertical fov
|
|||
|
float flHorizontalFov = view.fov;
|
|||
|
float flVerticalFov = ( view.m_flAspectRatio <= 0.0f ) ? ( view.fov ) : ( view.fov / view.m_flAspectRatio );
|
|||
|
//engine->Con_NPrintf( 2, "Horizontal Fov: %6.2f Vertical Fov: %6.2f", flHorizontalFov, flVerticalFov );
|
|||
|
|
|||
|
//=====================//
|
|||
|
// Forward motion blur //
|
|||
|
//=====================//
|
|||
|
float flViewDotMotion = DotProduct( vCurrentForwardVec, vPositionChange );
|
|||
|
if ( mat_motion_blur_forward_enabled.GetBool() ) // Want forward and falling
|
|||
|
g_vMotionBlurValues[2] = flViewDotMotion;
|
|||
|
else // Falling only
|
|||
|
g_vMotionBlurValues[2] = flViewDotMotion * fabs( vCurrentForwardVec[2] ); // Only want this if we're looking up or down;
|
|||
|
|
|||
|
//====================================//
|
|||
|
// Yaw (Compensate for circle strafe) //
|
|||
|
//====================================//
|
|||
|
float flSideDotMotion = DotProduct( vCurrentSideVec, vPositionChange );
|
|||
|
float flYawDiffOriginal = history.m_flPreviousYaw - flCurrentYaw;
|
|||
|
if ( ( ( history.m_flPreviousYaw - flCurrentYaw > 180.0f ) || ( history.m_flPreviousYaw - flCurrentYaw < -180.0f ) ) &&
|
|||
|
( ( history.m_flPreviousYaw + flCurrentYaw > -180.0f ) && ( history.m_flPreviousYaw + flCurrentYaw < 180.0f ) ) )
|
|||
|
flYawDiffOriginal = history.m_flPreviousYaw + flCurrentYaw;
|
|||
|
|
|||
|
float flYawDiffAdjusted = flYawDiffOriginal + ( flSideDotMotion / 3.0f ); // Yes, 3.0 is a magic number, sue me
|
|||
|
|
|||
|
// Make sure the adjustment only lessens the effect, not magnify it or reverse it
|
|||
|
if ( flYawDiffOriginal < 0.0f )
|
|||
|
flYawDiffAdjusted = clamp ( flYawDiffAdjusted, flYawDiffOriginal, 0.0f );
|
|||
|
else
|
|||
|
flYawDiffAdjusted = clamp ( flYawDiffAdjusted, 0.0f, flYawDiffOriginal );
|
|||
|
|
|||
|
// Use pitch to dampen yaw
|
|||
|
float flUndampenedYaw = flYawDiffAdjusted / flHorizontalFov;
|
|||
|
g_vMotionBlurValues[0] = flUndampenedYaw * ( 1.0f - ( fabs( flCurrentPitch ) / 90.0f ) ); // Dampen horizontal yaw blur based on pitch
|
|||
|
|
|||
|
//engine->Con_NPrintf( 4, "flSideDotMotion: %6.2f yaw diff: %6.2f ( %6.2f, %6.2f )", flSideDotMotion, ( s_flPreviousYaw - flCurrentYaw ), flYawDiffOriginal, flYawDiffAdjusted );
|
|||
|
|
|||
|
//=======================================//
|
|||
|
// Pitch (Compensate for forward motion) //
|
|||
|
//=======================================//
|
|||
|
float flPitchCompensateMask = 1.0f - ( ( 1.0f - fabs( vCurrentForwardVec[2] ) ) * ( 1.0f - fabs( vCurrentForwardVec[2] ) ) );
|
|||
|
float flPitchDiffOriginal = history.m_flPreviousPitch - flCurrentPitch;
|
|||
|
float flPitchDiffAdjusted = flPitchDiffOriginal;
|
|||
|
|
|||
|
if ( flCurrentPitch > 0.0f )
|
|||
|
flPitchDiffAdjusted = flPitchDiffOriginal - ( ( flViewDotMotion / 2.0f ) * flPitchCompensateMask ); // Yes, 2.0 is a magic number, sue me
|
|||
|
else
|
|||
|
flPitchDiffAdjusted = flPitchDiffOriginal + ( ( flViewDotMotion / 2.0f ) * flPitchCompensateMask ); // Yes, 2.0 is a magic number, sue me
|
|||
|
|
|||
|
// Make sure the adjustment only lessens the effect, not magnify it or reverse it
|
|||
|
if ( flPitchDiffOriginal < 0.0f )
|
|||
|
flPitchDiffAdjusted = clamp ( flPitchDiffAdjusted, flPitchDiffOriginal, 0.0f );
|
|||
|
else
|
|||
|
flPitchDiffAdjusted = clamp ( flPitchDiffAdjusted, 0.0f, flPitchDiffOriginal );
|
|||
|
|
|||
|
g_vMotionBlurValues[1] = flPitchDiffAdjusted / flVerticalFov;
|
|||
|
|
|||
|
//engine->Con_NPrintf( 5, "flViewDotMotion %6.2f, flPitchCompensateMask %6.2f, flPitchDiffOriginal %6.2f, flPitchDiffAdjusted %6.2f, g_vMotionBlurValues[1] %6.2f", flViewDotMotion, flPitchCompensateMask, flPitchDiffOriginal, flPitchDiffAdjusted, g_vMotionBlurValues[1]);
|
|||
|
|
|||
|
//========================================================//
|
|||
|
// Roll (Enabled when we're looking down and yaw changes) //
|
|||
|
//========================================================//
|
|||
|
g_vMotionBlurValues[3] = flUndampenedYaw; // Roll starts out as undampened yaw intensity and is then scaled by pitch
|
|||
|
g_vMotionBlurValues[3] *= ( fabs( flCurrentPitch ) / 90.0f ) * ( fabs( flCurrentPitch ) / 90.0f ) * ( fabs( flCurrentPitch ) / 90.0f ); // Dampen roll based on pitch^3
|
|||
|
|
|||
|
//engine->Con_NPrintf( 4, "[2] before scale and bias: %6.2f", g_vMotionBlurValues[2] );
|
|||
|
//engine->Con_NPrintf( 5, "[3] before scale and bias: %6.2f", g_vMotionBlurValues[3] );
|
|||
|
|
|||
|
//==============================================================//
|
|||
|
// Time-adjust falling effect until we can do something smarter //
|
|||
|
//==============================================================//
|
|||
|
if ( flTimeElapsed > 0.0f )
|
|||
|
g_vMotionBlurValues[2] /= flTimeElapsed * 30.0f; // 1/30th of a second?
|
|||
|
else
|
|||
|
g_vMotionBlurValues[2] = 0.0f;
|
|||
|
|
|||
|
// Scale and bias values after time adjustment
|
|||
|
g_vMotionBlurValues[2] = clamp( ( fabs( g_vMotionBlurValues[2] ) - flMotionBlurFallingMin ) / ( flMotionBlurFallingMax - flMotionBlurFallingMin ), 0.0f, 1.0f ) * ( g_vMotionBlurValues[2] >= 0.0f ? 1.0f : -1.0f );
|
|||
|
g_vMotionBlurValues[2] /= 30.0f; // To counter-adjust for time adjustment above
|
|||
|
|
|||
|
//=================//
|
|||
|
// Apply intensity //
|
|||
|
//=================//
|
|||
|
g_vMotionBlurValues[0] *= flMotionBlurRotationIntensity * flMotionBlurGlobalStrength;
|
|||
|
g_vMotionBlurValues[1] *= flMotionBlurRotationIntensity * flMotionBlurGlobalStrength;
|
|||
|
g_vMotionBlurValues[2] *= flMotionBlurFallingIntensity * flMotionBlurGlobalStrength;
|
|||
|
g_vMotionBlurValues[3] *= flMotionBlurRollIntensity * flMotionBlurGlobalStrength;
|
|||
|
|
|||
|
//===============================================================//
|
|||
|
// Dampen motion blur from 100%-0% as fps drops from 50fps-30fps //
|
|||
|
//===============================================================//
|
|||
|
if ( !IsX360() && !bSFMBlur ) // I'm not doing this on the 360 yet since I can't test it. SFM doesn't need it either
|
|||
|
{
|
|||
|
float flSlowFps = 30.0f;
|
|||
|
float flFastFps = 50.0f;
|
|||
|
float flCurrentFps = ( flTimeElapsed > 0.0f ) ? ( 1.0f / flTimeElapsed ) : 0.0f;
|
|||
|
float flDampenFactor = clamp( ( ( flCurrentFps - flSlowFps ) / ( flFastFps - flSlowFps ) ), 0.0f, 1.0f );
|
|||
|
|
|||
|
//engine->Con_NPrintf( 4, "gpGlobals->realtime %.2f gpGlobals->curtime %.2f", gpGlobals->realtime, gpGlobals->curtime );
|
|||
|
//engine->Con_NPrintf( 5, "flCurrentFps %.2f", flCurrentFps );
|
|||
|
//engine->Con_NPrintf( 7, "flTimeElapsed %.2f", flTimeElapsed );
|
|||
|
|
|||
|
g_vMotionBlurValues[0] *= flDampenFactor;
|
|||
|
g_vMotionBlurValues[1] *= flDampenFactor;
|
|||
|
g_vMotionBlurValues[2] *= flDampenFactor;
|
|||
|
g_vMotionBlurValues[3] *= flDampenFactor;
|
|||
|
|
|||
|
//engine->Con_NPrintf( 6, "Dampen: %.2f", flDampenFactor );
|
|||
|
}
|
|||
|
|
|||
|
//engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f );
|
|||
|
}
|
|||
|
|
|||
|
//============================================//
|
|||
|
// Zero out blur if still in that time window //
|
|||
|
//============================================//
|
|||
|
if ( !bSFMBlur && ( gpGlobals->realtime < history.m_flNoRotationalMotionBlurUntil ) )
|
|||
|
{
|
|||
|
//engine->Con_NPrintf( 9, " No Rotation @ %f ", gpGlobals->realtime );
|
|||
|
|
|||
|
// Zero out rotational blur but leave forward/falling blur alone
|
|||
|
g_vMotionBlurValues[0] = 0.0f; // X
|
|||
|
g_vMotionBlurValues[1] = 0.0f; // Y
|
|||
|
g_vMotionBlurValues[3] = 0.0f; // Roll
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
history.m_flNoRotationalMotionBlurUntil = 0.0f;
|
|||
|
}
|
|||
|
|
|||
|
//================================================================================//
|
|||
|
// Disable roll and forward blur if in split screen and reduce the blur intensity //
|
|||
|
//================================================================================//
|
|||
|
if ( engine->IsSplitScreenActive() )
|
|||
|
{
|
|||
|
g_vMotionBlurValues[0] *= 0.25f; // X
|
|||
|
g_vMotionBlurValues[1] *= 0.25f; // Y
|
|||
|
g_vMotionBlurValues[2] = 0.0f;
|
|||
|
g_vMotionBlurValues[3] = 0.0f;
|
|||
|
}
|
|||
|
|
|||
|
//====================================//
|
|||
|
// Store current frame for next frame //
|
|||
|
//====================================//
|
|||
|
VectorCopy( vCurrentPosition, history.m_vPreviousPositon );
|
|||
|
history.m_mPreviousFrameBasisVectors = mCurrentBasisVectors;
|
|||
|
history.m_flPreviousPitch = flCurrentPitch;
|
|||
|
history.m_flPreviousYaw = flCurrentYaw;
|
|||
|
history.m_flLastTimeUpdate = gpGlobals->realtime;
|
|||
|
}
|
|||
|
|
|||
|
//engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f );
|
|||
|
|
|||
|
//==========================================//
|
|||
|
// Set global g_vMotionBlurViewportValues[] //
|
|||
|
//==========================================//
|
|||
|
if ( true )
|
|||
|
{
|
|||
|
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
float flSrcWidth = ( float )pSrc->GetActualWidth();
|
|||
|
float flSrcHeight = ( float )pSrc->GetActualHeight();
|
|||
|
|
|||
|
// NOTE #1: float4 stored as ( minx, miny, maxy, maxx )...z&w have been swapped to save pixel shader instructions
|
|||
|
// NOTE #2: This code should definitely work for 2 players (horizontal or vertical), or 4 players (4 corners), but
|
|||
|
// it might have to be modified if we ever want to support other split screen configurations
|
|||
|
|
|||
|
int nOffset; // Offset by one pixel to land in the correct half
|
|||
|
|
|||
|
// Left
|
|||
|
nOffset = ( x > 0 ) ? 1 : 0;
|
|||
|
g_vMotionBlurViewportValues[0] = ( float )( x + nOffset ) / ( flSrcWidth - 1 );
|
|||
|
|
|||
|
// Right
|
|||
|
nOffset = ( x < ( flSrcWidth - 1 ) ) ? -1 : 0;
|
|||
|
g_vMotionBlurViewportValues[3] = ( float )( x + w + nOffset ) / ( flSrcWidth - 1 );
|
|||
|
|
|||
|
// Top
|
|||
|
nOffset = ( y > 0 ) ? 1 : 0; // Offset by one pixel to land in the correct half
|
|||
|
g_vMotionBlurViewportValues[1] = ( float )( y + nOffset ) / ( flSrcHeight - 1 );
|
|||
|
|
|||
|
// Bottom
|
|||
|
nOffset = ( y < ( flSrcHeight - 1 ) ) ? -1 : 0;
|
|||
|
g_vMotionBlurViewportValues[2] = ( float )( y + h + nOffset ) / ( flSrcHeight - 1 );
|
|||
|
|
|||
|
// Only allow clamping to happen in the middle of the screen, so nudge the clamp values out if they're on the border of the screen
|
|||
|
for ( int i = 0; i < 4; i++ )
|
|||
|
{
|
|||
|
if ( g_vMotionBlurViewportValues[i] <= 0.0f )
|
|||
|
g_vMotionBlurViewportValues[i] = -1.0f;
|
|||
|
else if ( g_vMotionBlurViewportValues[i] >= 1.0f )
|
|||
|
g_vMotionBlurViewportValues[i] = 2.0f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//=============================================================================================//
|
|||
|
// Render quad and let material proxy pick up the g_vMotionBlurValues[4] values just set above //
|
|||
|
//=============================================================================================//
|
|||
|
if ( true )
|
|||
|
{
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
//pRenderContext->PushRenderTargetAndViewport();
|
|||
|
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
int nSrcWidth = pSrc->GetActualWidth();
|
|||
|
int nSrcHeight = pSrc->GetActualHeight();
|
|||
|
int nViewportWidth, nViewportHeight, nDummy;
|
|||
|
pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight );
|
|||
|
|
|||
|
UpdateScreenEffectTexture( 0, x, y, w, h, false );
|
|||
|
|
|||
|
// Get material pointer
|
|||
|
IMaterial *pMatMotionBlur = materials->FindMaterial( "dev/motion_blur", TEXTURE_GROUP_OTHER, true );
|
|||
|
|
|||
|
//SetRenderTargetAndViewPort( dest_rt0 );
|
|||
|
//pRenderContext->PopRenderTargetAndViewport();
|
|||
|
|
|||
|
if ( pMatMotionBlur != NULL )
|
|||
|
{
|
|||
|
pRenderContext->DrawScreenSpaceRectangle(
|
|||
|
pMatMotionBlur,
|
|||
|
0, 0, nViewportWidth, nViewportHeight,
|
|||
|
x, y, x + w-1, y + h-1,
|
|||
|
nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//=====================================================================================================================
|
|||
|
// Depth of field =====================================================================================================
|
|||
|
//=====================================================================================================================
|
|||
|
ConVar mat_dof_enabled( "mat_dof_enabled", "1" );
|
|||
|
ConVar mat_dof_override( "mat_dof_override", "0" );
|
|||
|
ConVar mat_dof_near_blur_depth( "mat_dof_near_blur_depth", "20.0" );
|
|||
|
ConVar mat_dof_near_focus_depth( "mat_dof_near_focus_depth", "100.0" );
|
|||
|
ConVar mat_dof_far_focus_depth( "mat_dof_far_focus_depth", "250.0" );
|
|||
|
ConVar mat_dof_far_blur_depth( "mat_dof_far_blur_depth", "1000.0" );
|
|||
|
ConVar mat_dof_near_blur_radius( "mat_dof_near_blur_radius", "10.0" );
|
|||
|
ConVar mat_dof_far_blur_radius( "mat_dof_far_blur_radius", "5.0" );
|
|||
|
ConVar mat_dof_quality( "mat_dof_quality", "0" );
|
|||
|
|
|||
|
static float GetNearBlurDepth()
|
|||
|
{
|
|||
|
return mat_dof_override.GetBool() ? mat_dof_near_blur_depth.GetFloat() : g_flDOFNearBlurDepth;
|
|||
|
}
|
|||
|
|
|||
|
static float GetNearFocusDepth()
|
|||
|
{
|
|||
|
return mat_dof_override.GetBool() ? mat_dof_near_focus_depth.GetFloat() : g_flDOFNearFocusDepth;
|
|||
|
}
|
|||
|
|
|||
|
static float GetFarFocusDepth()
|
|||
|
{
|
|||
|
return mat_dof_override.GetBool() ? mat_dof_far_focus_depth.GetFloat() : g_flDOFFarFocusDepth;
|
|||
|
}
|
|||
|
|
|||
|
static float GetFarBlurDepth()
|
|||
|
{
|
|||
|
return mat_dof_override.GetBool() ? mat_dof_far_blur_depth.GetFloat() : g_flDOFFarBlurDepth;
|
|||
|
}
|
|||
|
|
|||
|
static float GetNearBlurRadius()
|
|||
|
{
|
|||
|
return mat_dof_override.GetBool() ? mat_dof_near_blur_radius.GetFloat() : g_flDOFNearBlurRadius;
|
|||
|
}
|
|||
|
|
|||
|
static float GetFarBlurRadius()
|
|||
|
{
|
|||
|
return mat_dof_override.GetBool() ? mat_dof_far_blur_radius.GetFloat() : g_flDOFFarBlurRadius;
|
|||
|
}
|
|||
|
|
|||
|
bool IsDepthOfFieldEnabled()
|
|||
|
{
|
|||
|
const CViewSetup *pViewSetup = view->GetViewSetup();
|
|||
|
if ( !pViewSetup )
|
|||
|
return false;
|
|||
|
|
|||
|
// We need high-precision depth, which we currently only get in float HDR mode
|
|||
|
if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_FLOAT )
|
|||
|
return false;
|
|||
|
|
|||
|
if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 92 )
|
|||
|
return false;
|
|||
|
|
|||
|
// Only SFM sets this at the moment...it supersedes mat_dof_ convars if true
|
|||
|
if ( pViewSetup->m_bDoDepthOfField )
|
|||
|
return true;
|
|||
|
|
|||
|
if ( !mat_dof_enabled.GetBool() )
|
|||
|
return false;
|
|||
|
|
|||
|
if ( mat_dof_override.GetBool() == true )
|
|||
|
{
|
|||
|
return mat_dof_enabled.GetBool();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return g_bDOFEnabled;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static inline bool SetMaterialVarFloat( IMaterial* pMat, const char* pVarName, float flValue )
|
|||
|
{
|
|||
|
Assert( pMat != NULL );
|
|||
|
Assert( pVarName != NULL );
|
|||
|
if ( pMat == NULL || pVarName == NULL )
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
bool bFound = false;
|
|||
|
IMaterialVar* pVar = pMat->FindVar( pVarName, &bFound );
|
|||
|
if ( bFound )
|
|||
|
{
|
|||
|
pVar->SetFloatValue( flValue );
|
|||
|
}
|
|||
|
|
|||
|
return bFound;
|
|||
|
}
|
|||
|
|
|||
|
static inline bool SetMaterialVarInt( IMaterial* pMat, const char* pVarName, int nValue )
|
|||
|
{
|
|||
|
Assert( pMat != NULL );
|
|||
|
Assert( pVarName != NULL );
|
|||
|
if ( pMat == NULL || pVarName == NULL )
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
bool bFound = false;
|
|||
|
IMaterialVar* pVar = pMat->FindVar( pVarName, &bFound );
|
|||
|
if ( bFound )
|
|||
|
{
|
|||
|
pVar->SetIntValue( nValue );
|
|||
|
}
|
|||
|
|
|||
|
return bFound;
|
|||
|
}
|
|||
|
|
|||
|
void DoDepthOfField( const CViewSetup &view )
|
|||
|
{
|
|||
|
if ( !IsDepthOfFieldEnabled() )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Copy from backbuffer to _rt_FullFrameFB
|
|||
|
UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height, false ); // Do we need to check if we already did this?
|
|||
|
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
|
|||
|
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
int nSrcWidth = pSrc->GetActualWidth();
|
|||
|
int nSrcHeight = pSrc->GetActualHeight();
|
|||
|
|
|||
|
if ( mat_dof_quality.GetInt() < 2 )
|
|||
|
{
|
|||
|
/////////////////////////////////////
|
|||
|
// Downsample backbuffer to 1/4 size
|
|||
|
/////////////////////////////////////
|
|||
|
|
|||
|
// Update downsampled framebuffer. TODO: Don't do this again for the bloom if we already did it here...
|
|||
|
pRenderContext->PushRenderTargetAndViewport();
|
|||
|
ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
|
|||
|
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
|
|||
|
Assert( dest_rt0->GetActualWidth() == pSrc->GetActualWidth() / 4 );
|
|||
|
Assert( dest_rt0->GetActualHeight() == pSrc->GetActualHeight() / 4 );
|
|||
|
|
|||
|
// Downsample fb to rt0
|
|||
|
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0, true );
|
|||
|
|
|||
|
//////////////////////////////////////
|
|||
|
// Additional blur using 3x3 gaussian
|
|||
|
//////////////////////////////////////
|
|||
|
|
|||
|
IMaterial *pMat = materials->FindMaterial( "dev/blurgaussian_3x3", TEXTURE_GROUP_OTHER, true );
|
|||
|
|
|||
|
if ( pMat == NULL )
|
|||
|
return;
|
|||
|
|
|||
|
SetMaterialVarFloat( pMat, "$c0_x", 0.5f / (float)dest_rt0->GetActualWidth() );
|
|||
|
SetMaterialVarFloat( pMat, "$c0_y", 0.5f / (float)dest_rt0->GetActualHeight() );
|
|||
|
SetMaterialVarFloat( pMat, "$c1_x", -0.5f / (float)dest_rt0->GetActualWidth() );
|
|||
|
SetMaterialVarFloat( pMat, "$c1_y", 0.5f / (float)dest_rt0->GetActualHeight() );
|
|||
|
|
|||
|
ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
SetRenderTargetAndViewPort( dest_rt1 );
|
|||
|
|
|||
|
pRenderContext->DrawScreenSpaceRectangle(
|
|||
|
pMat, 0, 0, nSrcWidth/4, nSrcHeight/4,
|
|||
|
0, 0, dest_rt0->GetActualWidth()-1, dest_rt0->GetActualHeight()-1,
|
|||
|
dest_rt0->GetActualWidth(), dest_rt0->GetActualHeight() );
|
|||
|
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
pRenderContext->CopyRenderTargetToTextureEx( dest_rt1, 0, NULL, NULL );
|
|||
|
}
|
|||
|
|
|||
|
pRenderContext->PopRenderTargetAndViewport();
|
|||
|
}
|
|||
|
|
|||
|
// Render depth-of-field quad
|
|||
|
|
|||
|
int nViewportWidth = 0;
|
|||
|
int nViewportHeight = 0;
|
|||
|
int nDummy = 0;
|
|||
|
pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight );
|
|||
|
|
|||
|
IMaterial *pMatDOF = materials->FindMaterial( "dev/depth_of_field", TEXTURE_GROUP_OTHER, true );
|
|||
|
|
|||
|
if ( pMatDOF == NULL )
|
|||
|
return;
|
|||
|
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearPlane", view.zNear );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farPlane", view.zFar );
|
|||
|
|
|||
|
// Only SFM drives this bool at the moment...
|
|||
|
if ( view.m_bDoDepthOfField )
|
|||
|
{
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearBlurDepth", view.m_flNearBlurDepth );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearFocusDepth", view.m_flNearFocusDepth );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farFocusDepth", view.m_flFarFocusDepth );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farBlurDepth", view.m_flFarBlurDepth );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearBlurRadius", view.m_flNearBlurRadius );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farBlurRadius", view.m_flFarBlurRadius );
|
|||
|
SetMaterialVarInt( pMatDOF, "$quality", view.m_nDoFQuality );
|
|||
|
}
|
|||
|
else // pull from convars/globals
|
|||
|
{
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearBlurDepth", GetNearBlurDepth() );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearFocusDepth", GetNearFocusDepth() );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farFocusDepth", GetFarFocusDepth() );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farBlurDepth", GetFarBlurDepth() );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$nearBlurRadius", GetNearBlurRadius() );
|
|||
|
SetMaterialVarFloat( pMatDOF, "$farBlurRadius", GetFarBlurRadius() );
|
|||
|
SetMaterialVarInt( pMatDOF, "$quality", mat_dof_quality.GetInt() );
|
|||
|
}
|
|||
|
|
|||
|
pRenderContext->DrawScreenSpaceRectangle(
|
|||
|
pMatDOF,
|
|||
|
0, 0, nViewportWidth, nViewportHeight,
|
|||
|
0, 0, nSrcWidth-1, nSrcHeight-1,
|
|||
|
nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void DrawModulationQuad( IMaterial *pMaterial, IMatRenderContext *pRenderContext, uint8 r, uint8 g, uint8 b, uint8 a, float fDepth )
|
|||
|
{
|
|||
|
pRenderContext->EnableClipping( false );
|
|||
|
pRenderContext->Bind( pMaterial );
|
|||
|
|
|||
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
|
|||
|
|
|||
|
pRenderContext->MatrixMode( MATERIAL_MODEL );
|
|||
|
pRenderContext->PushMatrix();
|
|||
|
pRenderContext->LoadIdentity();
|
|||
|
|
|||
|
pRenderContext->MatrixMode( MATERIAL_VIEW );
|
|||
|
pRenderContext->PushMatrix();
|
|||
|
pRenderContext->LoadIdentity();
|
|||
|
|
|||
|
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
|
|||
|
pRenderContext->PushMatrix();
|
|||
|
pRenderContext->LoadIdentity();
|
|||
|
|
|||
|
int w, h;
|
|||
|
|
|||
|
pRenderContext->GetRenderTargetDimensions( w, h );
|
|||
|
if ( ( w == 0 ) || ( h == 0 ) )
|
|||
|
return;
|
|||
|
|
|||
|
// This is the size of the back-buffer we're reading from.
|
|||
|
int bw, bh;
|
|||
|
bw = w; bh = h;
|
|||
|
|
|||
|
float s0, t0;
|
|||
|
float s1, t1;
|
|||
|
|
|||
|
float flOffsetS = (bw != 0.0f) ? 1.0f / bw : 0.0f;
|
|||
|
float flOffsetT = (bh != 0.0f) ? 1.0f / bh : 0.0f;
|
|||
|
s0 = 0.5f * flOffsetS;
|
|||
|
t0 = 0.5f * flOffsetT;
|
|||
|
s1 = (w-0.5f) * flOffsetS;
|
|||
|
t1 = (h-0.5f) * flOffsetT;
|
|||
|
|
|||
|
CMeshBuilder meshBuilder;
|
|||
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
|
|||
|
|
|||
|
meshBuilder.Position3f( -1.0f, -1.0f, fDepth );
|
|||
|
//meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
|
|||
|
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
|
|||
|
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
|
|||
|
meshBuilder.TexCoord2f( 0, s0, t1 );
|
|||
|
meshBuilder.Color4ub( r, g, b, a );
|
|||
|
meshBuilder.AdvanceVertex();
|
|||
|
|
|||
|
meshBuilder.Position3f( -1.0f, 1, fDepth );
|
|||
|
//meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
|
|||
|
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
|
|||
|
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
|
|||
|
meshBuilder.TexCoord2f( 0, s0, t0 );
|
|||
|
meshBuilder.Color4ub( r, g, b, a );
|
|||
|
meshBuilder.AdvanceVertex();
|
|||
|
|
|||
|
meshBuilder.Position3f( 1, 1, fDepth );
|
|||
|
//meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
|
|||
|
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
|
|||
|
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
|
|||
|
meshBuilder.TexCoord2f( 0, s1, t0 );
|
|||
|
meshBuilder.Color4ub( r, g, b, a );
|
|||
|
meshBuilder.AdvanceVertex();
|
|||
|
|
|||
|
meshBuilder.Position3f( 1, -1.0f, fDepth );
|
|||
|
//meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
|
|||
|
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
|
|||
|
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
|
|||
|
meshBuilder.TexCoord2f( 0, s1, t1 );
|
|||
|
meshBuilder.Color4ub( r, g, b, a );
|
|||
|
meshBuilder.AdvanceVertex();
|
|||
|
|
|||
|
meshBuilder.End();
|
|||
|
pMesh->Draw();
|
|||
|
|
|||
|
pRenderContext->MatrixMode( MATERIAL_MODEL );
|
|||
|
pRenderContext->PopMatrix();
|
|||
|
|
|||
|
pRenderContext->MatrixMode( MATERIAL_VIEW );
|
|||
|
pRenderContext->PopMatrix();
|
|||
|
|
|||
|
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
|
|||
|
pRenderContext->PopMatrix();
|
|||
|
pRenderContext->EnableClipping( true );
|
|||
|
}
|
|||
|
|
|||
|
ConVar cl_blurClearAlpha( "cl_blurClearAlpha", "0", 0, "0-255, but 0 has errors at the moment" );
|
|||
|
ConVar cl_blurDebug( "cl_blurDebug", "0" );
|
|||
|
ConVar cl_blurTapSize( "cl_blurTapSize", "0.5" );
|
|||
|
ConVar cl_blurPasses( "cl_blurPasses", "1" );
|
|||
|
|
|||
|
void BlurEntity( IClientRenderable *pRenderable, bool bPreDraw, int drawFlags, const RenderableInstance_t &instance, const CViewSetup &view, int x, int y, int w, int h )
|
|||
|
{
|
|||
|
ITexture *pFullFrameFB = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
ITexture *dest_rt[2];
|
|||
|
dest_rt[0] = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
dest_rt[1] = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
|
|||
|
|
|||
|
IMaterial *pBlurPass[2];
|
|||
|
pBlurPass[0] = materials->FindMaterial( "dev/blurentity_blurpass0", TEXTURE_GROUP_OTHER );
|
|||
|
pBlurPass[1] = materials->FindMaterial( "dev/blurentity_blurpass1", TEXTURE_GROUP_OTHER );
|
|||
|
IMaterial *pEntBlurCopyBack[2];
|
|||
|
pEntBlurCopyBack[0] = materials->FindMaterial( "dev/blurentity_copyback0", TEXTURE_GROUP_OTHER );
|
|||
|
pEntBlurCopyBack[1] = materials->FindMaterial( "dev/blurentity_copyback1", TEXTURE_GROUP_OTHER );
|
|||
|
IMaterial *pEntBlurAlphaSilhoutte = materials->FindMaterial( "dev/blurentity_alphasilhoutte", TEXTURE_GROUP_OTHER );
|
|||
|
|
|||
|
if( !pFullFrameFB ||
|
|||
|
!dest_rt[0] || !dest_rt[1] ||
|
|||
|
!pBlurPass[0] || !pBlurPass[1] ||
|
|||
|
!pEntBlurCopyBack[0] || !pEntBlurCopyBack[1] ||
|
|||
|
!pEntBlurAlphaSilhoutte )
|
|||
|
{
|
|||
|
return; //missing a vital texture/material
|
|||
|
}
|
|||
|
|
|||
|
// Copy from backbuffer to _rt_FullFrameFB
|
|||
|
UpdateScreenEffectTexture( 0, x, y, w, h, true ); // Do we need to check if we already did this?
|
|||
|
|
|||
|
CMatRenderContextPtr pRenderContext( materials );
|
|||
|
|
|||
|
pRenderContext->PushRenderTargetAndViewport();
|
|||
|
|
|||
|
|
|||
|
int nSrcWidth = pFullFrameFB->GetActualWidth();
|
|||
|
int nSrcHeight = pFullFrameFB->GetActualHeight();
|
|||
|
|
|||
|
pRenderContext->OverrideAlphaWriteEnable( true, true ); //ensure we're always copying alpha values in every shader since we're using alpha as a mask when drawing to the back buffer
|
|||
|
|
|||
|
//replace the alpha channel with a silhoutte of the desired entity. We'll use the blurred alpha when rendering back to the back buffer
|
|||
|
{
|
|||
|
SetRenderTargetAndViewPort( pFullFrameFB );
|
|||
|
pRenderContext->ClearColor4ub( 255, 255, 255, cl_blurClearAlpha.GetInt() );
|
|||
|
pRenderContext->ClearBuffersObeyStencilEx( cl_blurDebug.GetBool(), true, true ); //clear out the existing alpha and depth
|
|||
|
|
|||
|
if( bPreDraw ) //in pre-draw mode, this renderable hasn't drawn it's colors anywhere yet, add them to _rt_FullFrameFB
|
|||
|
pRenderable->DrawModel( drawFlags, instance );
|
|||
|
|
|||
|
//just write 1.0 to alpha, don't alter color information
|
|||
|
if( !cl_blurDebug.GetBool() )
|
|||
|
pRenderContext->OverrideColorWriteEnable( true, false );
|
|||
|
modelrender->ForcedMaterialOverride( pEntBlurAlphaSilhoutte );
|
|||
|
|
|||
|
pRenderable->DrawModel( drawFlags, instance );
|
|||
|
modelrender->ForcedMaterialOverride( NULL );
|
|||
|
if( !cl_blurDebug.GetBool() )
|
|||
|
pRenderContext->OverrideColorWriteEnable( false, false );
|
|||
|
}
|
|||
|
|
|||
|
IMaterial *pEntBlurCopyBackFinal = NULL; //the material to use when copying the blur back to the backbuffer
|
|||
|
//generate blur texture
|
|||
|
{
|
|||
|
/////////////////////////////////////
|
|||
|
// Downsample backbuffer to 1/4 size
|
|||
|
/////////////////////////////////////
|
|||
|
|
|||
|
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
|
|||
|
Assert( dest_rt[0]->GetActualWidth() == pFullFrameFB->GetActualWidth() / 4 );
|
|||
|
Assert( dest_rt[0]->GetActualHeight() == pFullFrameFB->GetActualHeight() / 4 );
|
|||
|
|
|||
|
// Downsample fb to rt0
|
|||
|
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt[0], true );
|
|||
|
|
|||
|
//////////////////////////////////////
|
|||
|
// Additional blur
|
|||
|
//////////////////////////////////////
|
|||
|
float flBlurTapSize = cl_blurTapSize.GetFloat();
|
|||
|
for( int i = 0; i != 2; ++i )
|
|||
|
{
|
|||
|
SetMaterialVarFloat( pBlurPass[i], "$c0_x", flBlurTapSize / (float)dest_rt[i]->GetActualWidth() );
|
|||
|
SetMaterialVarFloat( pBlurPass[i], "$c0_y", flBlurTapSize / (float)dest_rt[i]->GetActualHeight() );
|
|||
|
}
|
|||
|
|
|||
|
int iBlurPasses = cl_blurPasses.GetInt();
|
|||
|
|
|||
|
for( int i = 0; i < iBlurPasses; ++i )
|
|||
|
{
|
|||
|
int iSrc = i & 1;
|
|||
|
int iDest = 1 - iSrc;
|
|||
|
SetRenderTargetAndViewPort( dest_rt[iDest] );
|
|||
|
|
|||
|
pRenderContext->DrawScreenSpaceRectangle(
|
|||
|
pBlurPass[iSrc], 0, 0, nSrcWidth/4, nSrcHeight/4,
|
|||
|
0, 0, dest_rt[iSrc]->GetActualWidth()-1, dest_rt[iSrc]->GetActualHeight()-1,
|
|||
|
dest_rt[iSrc]->GetActualWidth(), dest_rt[iSrc]->GetActualHeight() );
|
|||
|
|
|||
|
if ( IsX360() )
|
|||
|
{
|
|||
|
pRenderContext->CopyRenderTargetToTextureEx( dest_rt[iDest], 0, NULL, NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
pEntBlurCopyBackFinal = pEntBlurCopyBack[iBlurPasses & 1];
|
|||
|
}
|
|||
|
pRenderContext->OverrideAlphaWriteEnable( false, true );
|
|||
|
|
|||
|
pRenderContext->PopRenderTargetAndViewport();
|
|||
|
|
|||
|
//render back to the screen. We use the depth of the closest bbox point as our quad depth
|
|||
|
{
|
|||
|
const Vector &vRenderOrigin = pRenderable->GetRenderOrigin();
|
|||
|
const QAngle &qRenderAngles = pRenderable->GetRenderAngles();
|
|||
|
Vector vMins, vMaxs;
|
|||
|
pRenderable->GetRenderBounds( vMins, vMaxs );
|
|||
|
|
|||
|
VMatrix matWorld, matView, matProj, matWorldView, matWorldViewProj;
|
|||
|
//since the model matrix isn't necessarily set for this renderable, construct it manually
|
|||
|
matWorld.SetupMatrixOrgAngles( vRenderOrigin, qRenderAngles );
|
|||
|
pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
|
|||
|
pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
|
|||
|
MatrixMultiply( matView, matWorld, matWorldView );
|
|||
|
MatrixMultiply( matProj, matWorldView, matWorldViewProj );
|
|||
|
|
|||
|
float fClosestBBoxDepth = 1.0f;
|
|||
|
Vector4D vTest;
|
|||
|
vTest.w = 1.0f;
|
|||
|
for( int i = 0; i != 8; ++i )
|
|||
|
{
|
|||
|
vTest.x = (i & (1 << 0)) ? vMaxs.x : vMins.x;
|
|||
|
vTest.y = (i & (1 << 1)) ? vMaxs.y : vMins.y;
|
|||
|
vTest.z = (i & (1 << 2)) ? vMaxs.z : vMins.z;
|
|||
|
Vector4D vOut;
|
|||
|
matWorldViewProj.V4Mul( vTest, vOut );
|
|||
|
float fDepth = vOut.z/vOut.w;
|
|||
|
if( fDepth < fClosestBBoxDepth )
|
|||
|
fClosestBBoxDepth = fDepth;
|
|||
|
}
|
|||
|
|
|||
|
if( fClosestBBoxDepth < 0.0f )
|
|||
|
fClosestBBoxDepth = 0.0f;
|
|||
|
|
|||
|
DrawModulationQuad( pEntBlurCopyBackFinal, pRenderContext, 255, 255, 255, 255, fClosestBBoxDepth );
|
|||
|
}
|
|||
|
|
|||
|
if( bPreDraw && ( instance.m_nAlpha == 255 ) && ( ( drawFlags & STUDIO_TRANSPARENCY ) == 0 ) ) //write depth out to the depth buffer
|
|||
|
{
|
|||
|
modelrender->ForcedMaterialOverride( NULL, OVERRIDE_DEPTH_WRITE );
|
|||
|
pRenderable->DrawModel( drawFlags, instance );
|
|||
|
modelrender->ForcedMaterialOverride( NULL );
|
|||
|
}
|
|||
|
}
|