You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2950 lines
113 KiB
2950 lines
113 KiB
//========== Copyright © 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 ); |
|
} |
|
}
|
|
|