Skip to content

Implement Lottes HDR->LDR tonemapper #1550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/engine/renderer/gl_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
{
}
Expand Down
39 changes: 39 additions & 0 deletions src/engine/renderer/gl_shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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:
Expand Down
21 changes: 19 additions & 2 deletions src/engine/renderer/glsl_source/cameraEffects_fp.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions src/engine/renderer/tr_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
17 changes: 17 additions & 0 deletions src/engine/renderer/tr_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,23 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Cvar::Cvar<bool> r_highPrecisionRendering("r_highPrecisionRendering", "use high precision frame buffers for rendering and blending", Cvar::NONE, true);

cvar_t *r_gamma;

Cvar::Cvar<bool> r_tonemap( "r_tonemap", "Use HDR->LDR tonemapping", Cvar::NONE, true );
Cvar::Cvar<float> r_tonemapExposure( "r_tonemapExposure", "Tonemap exposure", Cvar::NONE, 1.0f );
Cvar::Range<Cvar::Cvar<float>> r_tonemapContrast( "r_tonemapContrast", "Makes dark areas light up faster",
Cvar::NONE, 1.6f, 1.0f, 10.0f );
Cvar::Range<Cvar::Cvar<float>> r_tonemapHighlightsCompressionSpeed( "r_tonemapHighlightsCompressionSpeed",
"Highlights saturation",
Cvar::NONE, 0.977f, 0.0f, 10.0f );
Cvar::Range<Cvar::Cvar<float>> r_tonemapHDRMax( "r_tonemapHDRMax", "HDR white point",
Cvar::NONE, 8.0f, 1.0f, 128.0f );
Cvar::Range<Cvar::Cvar<float>> r_tonemapDarkAreaPointHDR( "r_tonemapDarkAreaPointHDR",
"Cut-off for dark area light-up",
Cvar::NONE, 0.18f, 0.0f, 1.0f );
Cvar::Range<Cvar::Cvar<float>> 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;

Expand Down
8 changes: 8 additions & 0 deletions src/engine/renderer/tr_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -2985,6 +2985,14 @@ enum class shaderProfilerRenderSubGroupsMode {
extern cvar_t *r_mode; // video mode
extern cvar_t *r_gamma;

extern Cvar::Cvar<bool> r_tonemap;
extern Cvar::Cvar<float> r_tonemapExposure;
extern Cvar::Range<Cvar::Cvar<float>> r_tonemapContrast;
extern Cvar::Range<Cvar::Cvar<float>> r_tonemapHighlightsCompressionSpeed;
extern Cvar::Range<Cvar::Cvar<float>> r_tonemapHDRMax;
extern Cvar::Range<Cvar::Cvar<float>> r_tonemapDarkAreaPointHDR;
extern Cvar::Range<Cvar::Cvar<float>> 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
Expand Down