From c56a4a8bf77510d57e8c7654ffcb5a3671726fe5 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Tue, 11 Feb 2025 20:36:25 +0300 Subject: [PATCH 1/5] Implement Lottes HDR->LDR tonemapper --- src/engine/renderer/gl_shader.cpp | 3 ++ src/engine/renderer/gl_shader.h | 39 +++++++++++++++++++ .../glsl_source/cameraEffects_fp.glsl | 22 ++++++++++- src/engine/renderer/tr_backend.cpp | 38 ++++++++++++++++++ src/engine/renderer/tr_init.cpp | 17 ++++++++ src/engine/renderer/tr_local.h | 8 ++++ 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 9cfefe3d54..12c360f6c6 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -2910,6 +2910,9 @@ GLShader_cameraEffects::GLShader_cameraEffects( GLShaderManager *manager ) : u_ColorModulate( this ), u_TextureMatrix( this ), u_ModelViewProjectionMatrix( this ), + u_Tonemap( this ), + u_TonemapParms( this ), + u_TonemapExposure( this ), u_InverseGamma( this ) { } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 2e357c651f..4894f19ea1 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -3795,6 +3795,42 @@ class u_InverseGamma : } }; +class u_Tonemap : + GLUniform1Bool { + public: + u_Tonemap( GLShader* shader ) : + GLUniform1Bool( shader, "u_Tonemap", true ) { + } + + void SetUniform_Tonemap( bool tonemap ) { + this->SetValue( tonemap ); + } +}; + +class u_TonemapParms : + GLUniform4f { + public: + u_TonemapParms( GLShader* shader ) : + GLUniform4f( shader, "u_TonemapParms" ) { + } + + void SetUniform_TonemapParms( vec4_t tonemapParms ) { + this->SetValue( tonemapParms ); + } +}; + +class u_TonemapExposure : + GLUniform1f { + public: + u_TonemapExposure( GLShader* shader ) : + GLUniform1f( shader, "u_TonemapExposure", true ) { + } + + void SetUniform_TonemapExposure( float tonemapExposure ) { + this->SetValue( tonemapExposure ); + } +}; + class u_LightGridOrigin : GLUniform3f { @@ -4427,6 +4463,9 @@ class GLShader_cameraEffects : public u_ColorModulate, public u_TextureMatrix, public u_ModelViewProjectionMatrix, + public u_Tonemap, + public u_TonemapParms, + public u_TonemapExposure, public u_InverseGamma { public: diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index e58b37bbd9..bb913e3ae9 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -35,14 +35,32 @@ IN(smooth) vec2 var_TexCoords; DECLARE_OUTPUT(vec4) -void main() +/* x: contrast +y: highlightsCompressionSpeed +z: shoulderClip +w: highlightsCompression */ +uniform bool u_Tonemap; +uniform vec4 u_TonemapParms; +uniform float u_TonemapExposure; + +vec3 TonemapLottes( vec3 color ) { + // Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines" + return pow( color, vec3( u_TonemapParms[0] ) ) + / ( pow( color, vec3( u_TonemapParms[0] * u_TonemapParms[1] ) ) * u_TonemapParms[2] + u_TonemapParms[3] ); +} + +void main() { // calculate the screen texcoord in the 0.0 to 1.0 range vec2 st = gl_FragCoord.st / r_FBufSize; vec4 color = texture2D(u_CurrentMap, st); - color = clamp(color, 0.0, 1.0); + if( u_Tonemap ) { + color.rgb = TonemapLottes( color.rgb * u_TonemapExposure ); + } else { + color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) ); + } #if defined(r_colorGrading) // apply color grading diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 3305c5208c..c88402bafe 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -3302,6 +3302,35 @@ void RB_FXAA() GL_CheckErrors(); } +static void ComputeTonemapParams( const float contrast, const float highlightsCompressionSpeed, + const float HDRMax, + const float darkAreaPointHDR, const float darkAreaPointLDR, + float& shoulderClip, float& highlightsCompression ) { + // Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines" + /* a: contrast + d: highlightsCompressionSpeed + b: shoulderClip + c: highlightsCompression + hdrMax: HDRMax + midIn: darkAreaPointHDR + midOut: darkAreaPointLDR */ + + shoulderClip = + ( -pow( darkAreaPointHDR, contrast ) + pow( HDRMax, contrast ) * darkAreaPointLDR ) + / + ( ( pow( HDRMax, contrast * highlightsCompressionSpeed ) + - pow( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) + ) * darkAreaPointLDR ); + highlightsCompression = + ( pow( HDRMax, contrast * highlightsCompressionSpeed ) * pow( darkAreaPointHDR, contrast ) + - pow( HDRMax, contrast ) * pow( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) * darkAreaPointLDR + ) + / + ( ( pow( HDRMax, contrast * highlightsCompressionSpeed ) + - pow( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) + ) * darkAreaPointLDR ); +} + void RB_CameraPostFX() { matrix_t ortho; @@ -3332,6 +3361,15 @@ void RB_CameraPostFX() gl_cameraEffectsShader->SetUniform_InverseGamma( 1.0 / r_gamma->value ); + if ( r_highPrecisionRendering.Get() ) { + vec4_t tonemapParms { r_tonemapContrast.Get(), r_tonemapHighlightsCompressionSpeed.Get() }; + ComputeTonemapParams( tonemapParms[0], tonemapParms[1], r_tonemapHDRMax.Get(), + r_tonemapDarkAreaPointHDR.Get(), r_tonemapDarkAreaPointLDR.Get(), tonemapParms[2], tonemapParms[3] ); + gl_cameraEffectsShader->SetUniform_TonemapParms( tonemapParms ); + gl_cameraEffectsShader->SetUniform_TonemapExposure( r_tonemapExposure.Get() ); + } + gl_cameraEffectsShader->SetUniform_Tonemap( r_highPrecisionRendering.Get() && r_tonemap.Get() ); + // This shader is run last, so let it render to screen instead of // tr.mainFBO R_BindNullFBO(); diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 2ece318382..1a20288d99 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -204,6 +204,23 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Cvar::Cvar r_highPrecisionRendering("r_highPrecisionRendering", "use high precision frame buffers for rendering and blending", Cvar::NONE, true); cvar_t *r_gamma; + + Cvar::Cvar r_tonemap( "r_tonemap", "Use HDR->LDR tonemapping", Cvar::NONE, true ); + Cvar::Cvar r_tonemapExposure( "r_tonemapExposure", "Tonemap exposure", Cvar::NONE, 1.0f ); + Cvar::Range> r_tonemapContrast( "r_tonemapContrast", "Makes dark areas light up faster", + Cvar::NONE, 1.6f, 1.0f, 10.0f ); + Cvar::Range> r_tonemapHighlightsCompressionSpeed( "r_tonemapHighlightsCompressionSpeed", + "Highlights saturation", + Cvar::NONE, 0.977f, 0.0f, 10.0f ); + Cvar::Range> r_tonemapHDRMax( "r_tonemapHDRMax", "HDR white point", + Cvar::NONE, 8.0f, 1.0f, 128.0f ); + Cvar::Range> r_tonemapDarkAreaPointHDR( "r_tonemapDarkAreaPointHDR", + "Cut-off for dark area light-up", + Cvar::NONE, 0.18f, 0.0f, 1.0f ); + Cvar::Range> r_tonemapDarkAreaPointLDR( "r_tonemapDarkAreaPointLDR", + "Convert to this brightness at dark area cut-off", + Cvar::NONE, 0.268f, 0.0f, 1.0f ); + cvar_t *r_lockpvs; cvar_t *r_noportals; diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index b581166672..5292e92725 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2985,6 +2985,14 @@ enum class shaderProfilerRenderSubGroupsMode { extern cvar_t *r_mode; // video mode extern cvar_t *r_gamma; + extern Cvar::Cvar r_tonemap; + extern Cvar::Cvar r_tonemapExposure; + extern Cvar::Range> r_tonemapContrast; + extern Cvar::Range> r_tonemapHighlightsCompressionSpeed; + extern Cvar::Range> r_tonemapHDRMax; + extern Cvar::Range> r_tonemapDarkAreaPointHDR; + extern Cvar::Range> r_tonemapDarkAreaPointLDR; + extern cvar_t *r_nobind; // turns off binding to appropriate textures extern cvar_t *r_singleShader; // make most world faces use default shader extern cvar_t *r_picMip; // controls picmip values From d05c2ef6b47cf1d5c8fd3309dc8b16f63732e513 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Wed, 12 Feb 2025 22:41:02 +0300 Subject: [PATCH 2/5] pow->powf --- src/engine/renderer/tr_backend.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index c88402bafe..66750ec4a0 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -3316,18 +3316,18 @@ static void ComputeTonemapParams( const float contrast, const float highlightsCo midOut: darkAreaPointLDR */ shoulderClip = - ( -pow( darkAreaPointHDR, contrast ) + pow( HDRMax, contrast ) * darkAreaPointLDR ) + ( -powf( darkAreaPointHDR, contrast ) + powf( HDRMax, contrast ) * darkAreaPointLDR ) / - ( ( pow( HDRMax, contrast * highlightsCompressionSpeed ) - - pow( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) + ( ( powf( HDRMax, contrast * highlightsCompressionSpeed ) + - powf( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) ) * darkAreaPointLDR ); highlightsCompression = - ( pow( HDRMax, contrast * highlightsCompressionSpeed ) * pow( darkAreaPointHDR, contrast ) - - pow( HDRMax, contrast ) * pow( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) * darkAreaPointLDR + ( powf( HDRMax, contrast * highlightsCompressionSpeed ) * powf( darkAreaPointHDR, contrast ) + - powf( HDRMax, contrast ) * powf( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) * darkAreaPointLDR ) / - ( ( pow( HDRMax, contrast * highlightsCompressionSpeed ) - - pow( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) + ( ( powf( HDRMax, contrast * highlightsCompressionSpeed ) + - powf( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) ) * darkAreaPointLDR ); } From 68c75a7fb3593e86593bc78028ce3873ba9928f9 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Wed, 12 Feb 2025 22:43:38 +0300 Subject: [PATCH 3/5] Fix tonemapping availability check --- src/engine/renderer/tr_backend.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 66750ec4a0..cdb1c5c899 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -3361,14 +3361,15 @@ void RB_CameraPostFX() gl_cameraEffectsShader->SetUniform_InverseGamma( 1.0 / r_gamma->value ); - if ( r_highPrecisionRendering.Get() ) { + const bool tonemap = r_tonemap.Get() && r_highPrecisionRendering.Get() && glConfig2.textureFloatAvailable; + if ( tonemap ) { vec4_t tonemapParms { r_tonemapContrast.Get(), r_tonemapHighlightsCompressionSpeed.Get() }; ComputeTonemapParams( tonemapParms[0], tonemapParms[1], r_tonemapHDRMax.Get(), r_tonemapDarkAreaPointHDR.Get(), r_tonemapDarkAreaPointLDR.Get(), tonemapParms[2], tonemapParms[3] ); gl_cameraEffectsShader->SetUniform_TonemapParms( tonemapParms ); gl_cameraEffectsShader->SetUniform_TonemapExposure( r_tonemapExposure.Get() ); } - gl_cameraEffectsShader->SetUniform_Tonemap( r_highPrecisionRendering.Get() && r_tonemap.Get() ); + gl_cameraEffectsShader->SetUniform_Tonemap( tonemap ); // This shader is run last, so let it render to screen instead of // tr.mainFBO From 8dd3c387179f157ce061d73e1e54b1d6927b254f Mon Sep 17 00:00:00 2001 From: VReaperV Date: Wed, 12 Feb 2025 22:44:48 +0300 Subject: [PATCH 4/5] Make tonemap uniforms more consistent --- src/engine/renderer/gl_shader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 4894f19ea1..fe1cecea65 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -3811,7 +3811,7 @@ class u_TonemapParms : GLUniform4f { public: u_TonemapParms( GLShader* shader ) : - GLUniform4f( shader, "u_TonemapParms" ) { + GLUniform4f( shader, "u_TonemapParms", true ) { } void SetUniform_TonemapParms( vec4_t tonemapParms ) { From 1ce722c0e702f2b13d59226e03965beeef96be1c Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sat, 22 Feb 2025 10:59:28 +0300 Subject: [PATCH 5/5] Always clamp LDR colour Avoid weird-looking over-saturation. --- src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index bb913e3ae9..f4b4fbcfd6 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -58,9 +58,8 @@ void main() if( u_Tonemap ) { color.rgb = TonemapLottes( color.rgb * u_TonemapExposure ); - } else { - color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) ); } + color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) ); #if defined(r_colorGrading) // apply color grading