Skip to content

Commit cc75fac

Browse files
committed
Implement adaptive exposure
Add a compute shader that will compute the geometric mean of scene luminance, then map it to an exposure curve in the `cameraEffects` shader. This is controlled by `r_tonemapAdaptiveExposure`.
1 parent d8b3fe8 commit cc75fac

18 files changed

+344
-20
lines changed

libs/crunch

src.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ set(GLSLSOURCELIST
173173
${ENGINE_DIR}/renderer/glsl_source/lighttile_fp.glsl
174174
${ENGINE_DIR}/renderer/glsl_source/computeLight_fp.glsl
175175
${ENGINE_DIR}/renderer/glsl_source/reliefMapping_fp.glsl
176+
${ENGINE_DIR}/renderer/glsl_source/luminanceReduction_cp.glsl
177+
${ENGINE_DIR}/renderer/glsl_source/clearFrameData_cp.glsl
176178

177179
# Common vertex shader libraries
178180
${ENGINE_DIR}/renderer/glsl_source/deformVertexes_vp.glsl

src/engine/renderer/Material.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ enum class BufferBind {
291291
PORTAL_SURFACES = 5,
292292
GEOMETRY_CACHE_INPUT_VBO = 6,
293293
GEOMETRY_CACHE_VBO = 7,
294+
LUMINANCE = 3,
295+
LUMINANCE_STORAGE = 8,
294296
DEBUG = 10,
295297
UNUSED = INT32_MAX
296298
};

src/engine/renderer/gl_shader.cpp

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ GLShader_fxaa *gl_fxaaShader = nullptr;
5656
GLShader_motionblur *gl_motionblurShader = nullptr;
5757
GLShader_ssao *gl_ssaoShader = nullptr;
5858

59-
GLShader_depthtile1 *gl_depthtile1Shader = nullptr;
60-
GLShader_depthtile2 *gl_depthtile2Shader = nullptr;
61-
GLShader_lighttile *gl_lighttileShader = nullptr;
59+
GLShader_depthtile1* gl_depthtile1Shader = nullptr;
60+
GLShader_depthtile2* gl_depthtile2Shader = nullptr;
61+
GLShader_lighttile* gl_lighttileShader = nullptr;
62+
GLShader_luminanceReduction* gl_luminanceReductionShader = nullptr;
63+
GLShader_clearFrameData* gl_clearFrameDataShader = nullptr;
6264

6365
GLShader_generic *gl_genericShader = nullptr;
6466
GLShader_genericMaterial *gl_genericShaderMaterial = nullptr;
@@ -83,6 +85,7 @@ GLShader_skybox *gl_skyboxShader = nullptr;
8385
GLShader_skyboxMaterial *gl_skyboxShaderMaterial = nullptr;
8486
GLShader_debugShadowMap *gl_debugShadowMapShader = nullptr;
8587
GLShaderManager gl_shaderManager;
88+
GLBuffer luminanceBuffer( "luminance", Util::ordinal( BufferBind::LUMINANCE ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
8689

8790
namespace // Implementation details
8891
{
@@ -432,6 +435,9 @@ static const std::vector<addedExtension_t> fragmentVertexAddedExtensions = {
432435
where the core variables have different names. */
433436
{ glConfig2.shaderDrawParametersAvailable, -1, "ARB_shader_draw_parameters" },
434437
{ glConfig2.SSBOAvailable, 430, "ARB_shader_storage_buffer_object" },
438+
{ glConfig2.shadingLanguage420PackAvailable, 420, "ARB_shading_language_420pack" },
439+
{ glConfig2.explicitUniformLocationAvailable, 430, "ARB_explicit_uniform_location" },
440+
{ glConfig2.shaderAtomicCountersAvailable, 420, "ARB_shader_atomic_counters" },
435441
/* Even though these are part of the GL_KHR_shader_subgroup extension, we need to enable
436442
the individual extensions for each feature.
437443
GL_KHR_shader_subgroup itself can't be used in the shader. */
@@ -579,6 +585,10 @@ static std::string GenVertexHeader() {
579585
AddDefine( str, "BIND_LIGHTMAP_DATA", Util::ordinal( BufferBind::LIGHTMAP_DATA ) );
580586
}
581587

588+
if ( glConfig2.adaptiveExposureAvailable ) {
589+
AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) );
590+
}
591+
582592
return str;
583593
}
584594

@@ -619,6 +629,10 @@ static std::string GenFragmentHeader() {
619629
AddDefine( str, "BIND_LIGHTMAP_DATA", Util::ordinal( BufferBind::LIGHTMAP_DATA ) );
620630
}
621631

632+
if ( glConfig2.adaptiveExposureAvailable ) {
633+
AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) );
634+
}
635+
622636
return str;
623637
}
624638

@@ -644,6 +658,11 @@ static std::string GenComputeHeader() {
644658
AddDefine( str, "BIND_DEBUG", Util::ordinal( BufferBind::DEBUG ) );
645659
}
646660

661+
if ( glConfig2.adaptiveExposureAvailable ) {
662+
AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) );
663+
AddDefine( str, "BIND_LUMINANCE_STORAGE", Util::ordinal( BufferBind::LUMINANCE_STORAGE ) );
664+
}
665+
647666
if ( glConfig2.usingBindlessTextures ) {
648667
str += "layout(bindless_image) uniform;\n";
649668
}
@@ -2805,6 +2824,23 @@ void GLShader_shadowFill::SetShaderProgramUniforms( ShaderProgramDescriptor *sha
28052824
glUniform1i( glGetUniformLocation( shaderProgram->id, "u_ColorMap" ), 0 );
28062825
}
28072826

2827+
GLShader_luminanceReduction::GLShader_luminanceReduction( GLShaderManager* manager ) :
2828+
GLShader( "luminanceReduction",
2829+
false, "luminanceReduction" ),
2830+
u_ViewWidth( this ),
2831+
u_ViewHeight( this ),
2832+
u_TonemapParms2( this ) {
2833+
}
2834+
2835+
void GLShader_luminanceReduction::SetShaderProgramUniforms( ShaderProgramDescriptor* shaderProgram ) {
2836+
glUniform1i( glGetUniformLocation( shaderProgram->id, "initialRenderImage" ), 0 );
2837+
}
2838+
2839+
GLShader_clearFrameData::GLShader_clearFrameData( GLShaderManager* manager ) :
2840+
GLShader( "clearFrameData",
2841+
false, "clearFrameData" ) {
2842+
}
2843+
28082844
GLShader_reflection::GLShader_reflection():
28092845
GLShader( "reflection", ATTR_POSITION | ATTR_TEXCOORD | ATTR_QTANGENT,
28102846
false, "reflection_CB", "reflection_CB" ),
@@ -3061,11 +3097,13 @@ GLShader_cameraEffects::GLShader_cameraEffects() :
30613097
u_CurrentMap( this ),
30623098
u_GlobalLightFactor( this ),
30633099
u_ColorModulate( this ),
3100+
u_ViewWidth( this ),
3101+
u_ViewHeight( this ),
30643102
u_Tonemap( this ),
3103+
u_TonemapAdaptiveExposure( this ),
30653104
u_TonemapParms( this ),
30663105
u_TonemapExposure( this ),
3067-
u_InverseGamma( this )
3068-
{
3106+
u_InverseGamma( this ) {
30693107
}
30703108

30713109
void GLShader_cameraEffects::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram )

src/engine/renderer/gl_shader.h

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3894,6 +3894,18 @@ class u_Tonemap :
38943894
}
38953895
};
38963896

3897+
class u_TonemapAdaptiveExposure :
3898+
GLUniform1Bool {
3899+
public:
3900+
u_TonemapAdaptiveExposure( GLShader* shader ) :
3901+
GLUniform1Bool( shader, "u_TonemapAdaptiveExposure", true ) {
3902+
}
3903+
3904+
void SetUniform_TonemapAdaptiveExposure( bool tonemapAdaptiveExposure ) {
3905+
this->SetValue( tonemapAdaptiveExposure );
3906+
}
3907+
};
3908+
38973909
class u_TonemapParms :
38983910
GLUniform4f {
38993911
public:
@@ -3906,6 +3918,18 @@ class u_TonemapParms :
39063918
}
39073919
};
39083920

3921+
class u_TonemapParms2 :
3922+
GLUniform4f {
3923+
public:
3924+
u_TonemapParms2( GLShader* shader ) :
3925+
GLUniform4f( shader, "u_TonemapParms2", true ) {
3926+
}
3927+
3928+
void SetUniform_TonemapParms2( vec4_t tonemapParms2 ) {
3929+
this->SetValue( tonemapParms2 );
3930+
}
3931+
};
3932+
39093933
class u_TonemapExposure :
39103934
GLUniform1f {
39113935
public:
@@ -4336,6 +4360,22 @@ class GLShader_shadowFill :
43364360
void SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) override;
43374361
};
43384362

4363+
class GLShader_luminanceReduction :
4364+
public GLShader,
4365+
public u_ViewWidth,
4366+
public u_ViewHeight,
4367+
public u_TonemapParms2 {
4368+
public:
4369+
GLShader_luminanceReduction( GLShaderManager* manager );
4370+
void SetShaderProgramUniforms( ShaderProgramDescriptor* shaderProgram ) override;
4371+
};
4372+
4373+
class GLShader_clearFrameData :
4374+
public GLShader {
4375+
public:
4376+
GLShader_clearFrameData( GLShaderManager* manager );
4377+
};
4378+
43394379
class GLShader_reflection :
43404380
public GLShader,
43414381
public u_ColorMapCube,
@@ -4546,7 +4586,10 @@ class GLShader_cameraEffects :
45464586
public u_CurrentMap,
45474587
public u_GlobalLightFactor,
45484588
public u_ColorModulate,
4589+
public u_ViewWidth,
4590+
public u_ViewHeight,
45494591
public u_Tonemap,
4592+
public u_TonemapAdaptiveExposure,
45504593
public u_TonemapParms,
45514594
public u_TonemapExposure,
45524595
public u_InverseGamma
@@ -4778,9 +4821,11 @@ extern GLShader_fxaa *gl_fxaaShader;
47784821
extern GLShader_motionblur *gl_motionblurShader;
47794822
extern GLShader_ssao *gl_ssaoShader;
47804823

4781-
extern GLShader_depthtile1 *gl_depthtile1Shader;
4782-
extern GLShader_depthtile2 *gl_depthtile2Shader;
4783-
extern GLShader_lighttile *gl_lighttileShader;
4824+
extern GLShader_depthtile1* gl_depthtile1Shader;
4825+
extern GLShader_depthtile2* gl_depthtile2Shader;
4826+
extern GLShader_lighttile* gl_lighttileShader;
4827+
extern GLShader_luminanceReduction* gl_luminanceReductionShader;
4828+
extern GLShader_clearFrameData* gl_clearFrameDataShader;
47844829

47854830
extern GLShader_generic *gl_genericShader;
47864831
extern GLShader_genericMaterial *gl_genericShaderMaterial;
@@ -4805,5 +4850,6 @@ extern GLShader_skybox *gl_skyboxShader;
48054850
extern GLShader_skyboxMaterial *gl_skyboxShaderMaterial;
48064851
extern GLShader_debugShadowMap *gl_debugShadowMapShader;
48074852
extern GLShaderManager gl_shaderManager;
4853+
extern GLBuffer luminanceBuffer;
48084854

48094855
#endif // GL_SHADER_H

src/engine/renderer/glsl_source/cameraEffects_fp.glsl

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,24 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2525
uniform sampler2D u_CurrentMap;
2626

2727
#if defined(r_colorGrading)
28-
uniform sampler3D u_ColorMap3D;
28+
uniform sampler3D u_ColorMap3D;
2929
#endif
3030

31-
uniform vec4 u_ColorModulate;
32-
uniform float u_GlobalLightFactor; // 1 / tr.identityLight
33-
uniform float u_InverseGamma;
31+
uniform vec4 u_ColorModulate;
32+
uniform float u_GlobalLightFactor; // 1 / tr.identityLight
33+
uniform float u_InverseGamma;
3434

3535
// Tone mapping is not available when high-precision float framebuffer isn't enabled or supported.
3636
#if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float)
37+
uniform uint u_ViewWidth;
38+
uniform uint u_ViewHeight;
39+
40+
uniform bool u_Tonemap;
41+
uniform bool u_TonemapAdaptiveExposure;
3742
/* x: contrast
3843
y: highlightsCompressionSpeed
3944
z: shoulderClip
4045
w: highlightsCompression */
41-
uniform bool u_Tonemap;
4246
uniform vec4 u_TonemapParms;
4347
uniform float u_TonemapExposure;
4448

@@ -47,12 +51,21 @@ vec3 TonemapLottes( vec3 color ) {
4751
return pow( color, vec3( u_TonemapParms[0] ) )
4852
/ ( pow( color, vec3( u_TonemapParms[0] * u_TonemapParms[1] ) ) * u_TonemapParms[2] + u_TonemapParms[3] );
4953
}
54+
55+
#if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters)
56+
layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO {
57+
uint luminanceU;
58+
};
59+
#endif
60+
61+
float GetAverageLuminance( const in uint luminance ) {
62+
return float( luminanceU ) / ( 256.0f * u_ViewWidth * u_ViewHeight );
63+
}
5064
#endif
5165

5266
DECLARE_OUTPUT(vec4)
5367

54-
void main()
55-
{
68+
void main() {
5669
// calculate the screen texcoord in the 0.0 to 1.0 range
5770
vec2 st = gl_FragCoord.st / r_FBufSize;
5871

@@ -61,6 +74,13 @@ void main()
6174

6275
#if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float)
6376
if( u_Tonemap ) {
77+
#if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters)
78+
if( u_TonemapAdaptiveExposure ) {
79+
const float l = GetAverageLuminance( luminanceU ) - 8;
80+
color.rgb *= clamp( 0.18f / exp2( l * 0.8f + 0.1f ), 0.0f, 2.0f );
81+
}
82+
#endif
83+
6484
color.rgb = TonemapLottes( color.rgb * u_TonemapExposure );
6585
}
6686
#endif
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
===========================================================================
3+
4+
Daemon BSD Source Code
5+
Copyright (c) 2025 Daemon Developers
6+
All rights reserved.
7+
8+
This file is part of the Daemon BSD Source Code (Daemon Source Code).
9+
10+
Redistribution and use in source and binary forms, with or without
11+
modification, are permitted provided that the following conditions are met:
12+
* Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
* Redistributions in binary form must reproduce the above copyright
15+
notice, this list of conditions and the following disclaimer in the
16+
documentation and/or other materials provided with the distribution.
17+
* Neither the name of the Daemon developers nor the
18+
names of its contributors may be used to endorse or promote products
19+
derived from this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
25+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
===========================================================================
33+
*/
34+
35+
/* clearFrameData_cp.glsl */
36+
37+
#insert common_cp
38+
39+
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
40+
41+
layout(std430, binding = BIND_LUMINANCE_STORAGE) writeonly buffer luminanceBuffer {
42+
uint luminance;
43+
};
44+
45+
uniform uint u_Frame;
46+
47+
void main() {
48+
const uint globalInvocationID = GLOBAL_INVOCATION_ID;
49+
if( globalInvocationID >= 1 ) {
50+
return;
51+
}
52+
luminance = 0;
53+
}

src/engine/renderer/glsl_source/common_cp.glsl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ array must be in the form of uvec4 array[] */
5656

5757
/* Macro combinations for subgroup ops */
5858

59+
#if defined(HAVE_KHR_shader_subgroup_basic) && defined(HAVE_KHR_shader_subgroup_arithmetic)\
60+
&& defined(HAVE_ARB_shader_atomic_counter_ops)
61+
#define SUBGROUP_ATOMIC
62+
#endif
63+
5964
#if defined(HAVE_KHR_shader_subgroup_basic) && defined(HAVE_KHR_shader_subgroup_arithmetic)\
6065
&& defined(HAVE_KHR_shader_subgroup_ballot) && defined(HAVE_ARB_shader_atomic_counter_ops)
6166
#define SUBGROUP_STREAM_COMPACTION

0 commit comments

Comments
 (0)