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..fe1cecea65 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", true ) { + } + + 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..f4b4fbcfd6 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -35,14 +35,31 @@ 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 ); + } + 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..cdb1c5c899 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 = + ( -powf( darkAreaPointHDR, contrast ) + powf( HDRMax, contrast ) * darkAreaPointLDR ) + / + ( ( powf( HDRMax, contrast * highlightsCompressionSpeed ) + - powf( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) + ) * darkAreaPointLDR ); + highlightsCompression = + ( powf( HDRMax, contrast * highlightsCompressionSpeed ) * powf( darkAreaPointHDR, contrast ) + - powf( HDRMax, contrast ) * powf( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) * darkAreaPointLDR + ) + / + ( ( powf( HDRMax, contrast * highlightsCompressionSpeed ) + - powf( darkAreaPointHDR, contrast * highlightsCompressionSpeed ) + ) * darkAreaPointLDR ); +} + void RB_CameraPostFX() { matrix_t ortho; @@ -3332,6 +3361,16 @@ void RB_CameraPostFX() gl_cameraEffectsShader->SetUniform_InverseGamma( 1.0 / r_gamma->value ); + 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( tonemap ); + // 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