Skip to content

Commit b7277fe

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`. Also improves some of the shader #insert's a bit and untangle some of the related code.
1 parent 1ce722c commit b7277fe

19 files changed

+324
-56
lines changed

src.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ set(GLSLSOURCELIST
148148
${ENGINE_DIR}/renderer/glsl_source/cull_cp.glsl
149149
${ENGINE_DIR}/renderer/glsl_source/depthReduction_cp.glsl
150150
${ENGINE_DIR}/renderer/glsl_source/processSurfaces_cp.glsl
151+
${ENGINE_DIR}/renderer/glsl_source/material_cp.glsl
151152
${ENGINE_DIR}/renderer/glsl_source/material_vp.glsl
152153
${ENGINE_DIR}/renderer/glsl_source/material_fp.glsl
153154
${ENGINE_DIR}/renderer/glsl_source/skybox_vp.glsl
@@ -161,6 +162,7 @@ set(GLSLSOURCELIST
161162
${ENGINE_DIR}/renderer/glsl_source/cameraEffects_fp.glsl
162163
${ENGINE_DIR}/renderer/glsl_source/cameraEffects_vp.glsl
163164
${ENGINE_DIR}/renderer/glsl_source/computeLight_fp.glsl
165+
${ENGINE_DIR}/renderer/glsl_source/luminanceReduction_cp.glsl
164166
${ENGINE_DIR}/renderer/glsl_source/contrast_fp.glsl
165167
${ENGINE_DIR}/renderer/glsl_source/contrast_vp.glsl
166168
${ENGINE_DIR}/renderer/glsl_source/debugShadowMap_fp.glsl

src/engine/renderer/Material.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ enum class BufferBind {
291291
PORTAL_SURFACES = 5,
292292
GEOMETRY_CACHE_INPUT_VBO = 6,
293293
GEOMETRY_CACHE_VBO = 7,
294+
LUMINANCE = 3,
294295
DEBUG = 10,
295296
UNUSED = INT32_MAX
296297
};

src/engine/renderer/gl_shader.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ GLShader_lightMappingMaterial *gl_lightMappingShaderMaterial = nullpt
5454
GLShader_forwardLighting_omniXYZ *gl_forwardLightingShader_omniXYZ = nullptr;
5555
GLShader_forwardLighting_projXYZ *gl_forwardLightingShader_projXYZ = nullptr;
5656
GLShader_forwardLighting_directionalSun *gl_forwardLightingShader_directionalSun = nullptr;
57+
GLShader_luminanceReduction *gl_luminanceReductionShader = nullptr;
5758
GLShader_shadowFill *gl_shadowFillShader = nullptr;
5859
GLShader_reflection *gl_reflectionShader = nullptr;
5960
GLShader_reflectionMaterial *gl_reflectionShaderMaterial = nullptr;
@@ -80,6 +81,7 @@ GLShader_depthtile2 *gl_depthtile2Shader = nullptr;
8081
GLShader_lighttile *gl_lighttileShader = nullptr;
8182
GLShader_fxaa *gl_fxaaShader = nullptr;
8283
GLShaderManager gl_shaderManager;
84+
GLBuffer luminanceBuffer( "luminance", Util::ordinal( BufferBind::LUMINANCE ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
8385

8486
namespace // Implementation details
8587
{
@@ -405,6 +407,9 @@ static const std::vector<addedExtension_t> fragmentVertexAddedExtensions = {
405407
where the core variables have different names. */
406408
{ glConfig2.shaderDrawParametersAvailable, -1, "ARB_shader_draw_parameters" },
407409
{ glConfig2.SSBOAvailable, 430, "ARB_shader_storage_buffer_object" },
410+
{ glConfig2.shadingLanguage420PackAvailable, 420, "ARB_shading_language_420pack" },
411+
{ glConfig2.explicitUniformLocationAvailable, 430, "ARB_explicit_uniform_location" },
412+
{ glConfig2.shaderAtomicCountersAvailable, 420, "ARB_shader_atomic_counters" },
408413
/* Even though these are part of the GL_KHR_shader_subgroup extension, we need to enable
409414
the individual extensions for each feature.
410415
GL_KHR_shader_subgroup itself can't be used in the shader. */
@@ -552,6 +557,10 @@ static std::string GenVertexHeader() {
552557
AddDefine( str, "BIND_LIGHTMAP_DATA", Util::ordinal( BufferBind::LIGHTMAP_DATA ) );
553558
}
554559

560+
if ( glConfig2.adaptiveExposureAvailable ) {
561+
AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) );
562+
}
563+
555564
return str;
556565
}
557566

@@ -592,6 +601,10 @@ static std::string GenFragmentHeader() {
592601
AddDefine( str, "BIND_LIGHTMAP_DATA", Util::ordinal( BufferBind::LIGHTMAP_DATA ) );
593602
}
594603

604+
if ( glConfig2.adaptiveExposureAvailable ) {
605+
AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) );
606+
}
607+
595608
return str;
596609
}
597610

@@ -617,6 +630,10 @@ static std::string GenComputeHeader() {
617630
AddDefine( str, "BIND_DEBUG", Util::ordinal( BufferBind::DEBUG ) );
618631
}
619632

633+
if ( glConfig2.adaptiveExposureAvailable ) {
634+
AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) );
635+
}
636+
620637
if ( glConfig2.usingBindlessTextures ) {
621638
str += "layout(bindless_image) uniform;\n";
622639
}
@@ -2640,6 +2657,17 @@ void GLShader_forwardLighting_directionalSun::SetShaderProgramUniforms( shaderPr
26402657
glUniform1i( glGetUniformLocation( shaderProgram->program, "u_HeightMap" ), 15 );
26412658
}
26422659

2660+
GLShader_luminanceReduction::GLShader_luminanceReduction( GLShaderManager* manager ) :
2661+
GLShader( "luminanceReduction", 0, manager, false, false, true ),
2662+
u_ViewWidth( this ),
2663+
u_ViewHeight( this ),
2664+
u_TonemapParms2( this ) {
2665+
}
2666+
2667+
void GLShader_luminanceReduction::SetShaderProgramUniforms( shaderProgram_t* shaderProgram ) {
2668+
glUniform1i( glGetUniformLocation( shaderProgram->program, "initialRenderImage" ), 0 );
2669+
}
2670+
26432671
GLShader_shadowFill::GLShader_shadowFill( GLShaderManager *manager ) :
26442672
GLShader( "shadowFill", ATTR_POSITION | ATTR_TEXCOORD | ATTR_QTANGENT, manager ),
26452673
u_ColorMap( this ),
@@ -2910,7 +2938,10 @@ GLShader_cameraEffects::GLShader_cameraEffects( GLShaderManager *manager ) :
29102938
u_ColorModulate( this ),
29112939
u_TextureMatrix( this ),
29122940
u_ModelViewProjectionMatrix( this ),
2941+
u_ViewWidth( this ),
2942+
u_ViewHeight( this ),
29132943
u_Tonemap( this ),
2944+
u_TonemapAdaptiveExposure( this ),
29142945
u_TonemapParms( this ),
29152946
u_TonemapExposure( this ),
29162947
u_InverseGamma( this )

src/engine/renderer/gl_shader.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3807,6 +3807,18 @@ class u_Tonemap :
38073807
}
38083808
};
38093809

3810+
class u_TonemapAdaptiveExposure :
3811+
GLUniform1Bool {
3812+
public:
3813+
u_TonemapAdaptiveExposure( GLShader* shader ) :
3814+
GLUniform1Bool( shader, "u_TonemapAdaptiveExposure", true ) {
3815+
}
3816+
3817+
void SetUniform_TonemapAdaptiveExposure( bool tonemapAdaptiveExposure ) {
3818+
this->SetValue( tonemapAdaptiveExposure );
3819+
}
3820+
};
3821+
38103822
class u_TonemapParms :
38113823
GLUniform4f {
38123824
public:
@@ -3819,6 +3831,18 @@ class u_TonemapParms :
38193831
}
38203832
};
38213833

3834+
class u_TonemapParms2 :
3835+
GLUniform4f {
3836+
public:
3837+
u_TonemapParms2( GLShader* shader ) :
3838+
GLUniform4f( shader, "u_TonemapParms2", true ) {
3839+
}
3840+
3841+
void SetUniform_TonemapParms2( vec4_t tonemapParms2 ) {
3842+
this->SetValue( tonemapParms2 );
3843+
}
3844+
};
3845+
38223846
class u_TonemapExposure :
38233847
GLUniform1f {
38243848
public:
@@ -4227,6 +4251,16 @@ class GLShader_forwardLighting_directionalSun :
42274251
void SetShaderProgramUniforms( shaderProgram_t *shaderProgram ) override;
42284252
};
42294253

4254+
class GLShader_luminanceReduction :
4255+
public GLShader,
4256+
public u_ViewWidth,
4257+
public u_ViewHeight,
4258+
public u_TonemapParms2 {
4259+
public:
4260+
GLShader_luminanceReduction( GLShaderManager* manager );
4261+
void SetShaderProgramUniforms( shaderProgram_t* shaderProgram ) override;
4262+
};
4263+
42304264
class GLShader_shadowFill :
42314265
public GLShader,
42324266
public u_ColorMap,
@@ -4463,7 +4497,10 @@ class GLShader_cameraEffects :
44634497
public u_ColorModulate,
44644498
public u_TextureMatrix,
44654499
public u_ModelViewProjectionMatrix,
4500+
public u_ViewWidth,
4501+
public u_ViewHeight,
44664502
public u_Tonemap,
4503+
public u_TonemapAdaptiveExposure,
44674504
public u_TonemapParms,
44684505
public u_TonemapExposure,
44694506
public u_InverseGamma
@@ -4700,6 +4737,7 @@ extern GLShader_lightMappingMaterial *gl_lightMappingShaderMaterial;
47004737
extern GLShader_forwardLighting_omniXYZ *gl_forwardLightingShader_omniXYZ;
47014738
extern GLShader_forwardLighting_projXYZ *gl_forwardLightingShader_projXYZ;
47024739
extern GLShader_forwardLighting_directionalSun *gl_forwardLightingShader_directionalSun;
4740+
extern GLShader_luminanceReduction *gl_luminanceReductionShader;
47034741
extern GLShader_shadowFill *gl_shadowFillShader;
47044742
extern GLShader_reflection *gl_reflectionShader;
47054743
extern GLShader_reflectionMaterial *gl_reflectionShaderMaterial;
@@ -4726,5 +4764,6 @@ extern GLShader_depthtile2 *gl_depthtile2Shader;
47264764
extern GLShader_lighttile *gl_lighttileShader;
47274765
extern GLShader_fxaa *gl_fxaaShader;
47284766
extern GLShaderManager gl_shaderManager;
4767+
extern GLBuffer luminanceBuffer;
47294768

47304769
#endif // GL_SHADER_H

src/engine/renderer/glsl_source/cameraEffects_fp.glsl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,26 @@ uniform sampler2D u_CurrentMap;
2828
uniform sampler3D u_ColorMap3D;
2929
#endif
3030

31+
layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO {
32+
uint luminanceU;
33+
};
34+
3135
uniform vec4 u_ColorModulate;
3236
uniform float u_InverseGamma;
3337

3438
IN(smooth) vec2 var_TexCoords;
3539

3640
DECLARE_OUTPUT(vec4)
3741

42+
uniform uint u_ViewWidth;
43+
uniform uint u_ViewHeight;
44+
45+
uniform bool u_Tonemap;
46+
uniform bool u_TonemapAdaptiveExposure;
3847
/* x: contrast
3948
y: highlightsCompressionSpeed
4049
z: shoulderClip
4150
w: highlightsCompression */
42-
uniform bool u_Tonemap;
4351
uniform vec4 u_TonemapParms;
4452
uniform float u_TonemapExposure;
4553

@@ -57,6 +65,11 @@ void main()
5765
vec4 color = texture2D(u_CurrentMap, st);
5866

5967
if( u_Tonemap ) {
68+
if( u_TonemapAdaptiveExposure ) {
69+
const float l = float( luminanceU ) / ( 256.0f * u_ViewWidth * u_ViewHeight ) - 8;
70+
color.rgb *= clamp( 0.18f / exp2( l * 0.8f + 0.1f ), 0.0f, 2.0f );
71+
}
72+
6073
color.rgb = TonemapLottes( color.rgb * u_TonemapExposure );
6174
}
6275
color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) );
@@ -74,4 +87,5 @@ void main()
7487
color.xyz = pow(color.xyz, vec3(u_InverseGamma));
7588

7689
outputColor = color;
90+
// outputColor = vec4( luminance, color.yzw );
7791
}

src/engine/renderer/glsl_source/common_cp.glsl

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,41 +54,14 @@ array must be in the form of uvec4 array[] */
5454
+ gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x\
5555
+ gl_GlobalInvocationID.x )
5656

57-
/* Common structs */
57+
/* Macro combinations for subgroup ops */
5858

59-
struct BoundingSphere {
60-
vec3 origin;
61-
float radius;
62-
};
59+
#if defined(HAVE_KHR_shader_subgroup_basic) && defined(HAVE_KHR_shader_subgroup_arithmetic)\
60+
&& defined(HAVE_ARB_shader_atomic_counter_ops)
61+
#define HAVE_subgroup_atomic
62+
#endif
6363

64-
struct SurfaceDescriptor {
65-
BoundingSphere boundingSphere;
66-
uint surfaceCommandIDs[MAX_SURFACE_COMMANDS];
67-
};
68-
69-
struct PortalSurface {
70-
BoundingSphere boundingSphere;
71-
72-
uint drawSurfID;
73-
float distance;
74-
vec2 padding;
75-
};
76-
77-
struct GLIndirectCommand {
78-
uint count;
79-
uint instanceCount;
80-
uint firstIndex;
81-
int baseVertex;
82-
uint baseInstance;
83-
};
84-
85-
struct IndirectCompactCommand {
86-
uint count;
87-
uint firstIndex;
88-
uint baseInstance;
89-
};
90-
91-
struct SurfaceCommand {
92-
bool enabled;
93-
IndirectCompactCommand drawCommand;
94-
};
64+
#if defined(HAVE_KHR_shader_subgroup_basic) && defined(HAVE_KHR_shader_subgroup_arithmetic)\
65+
&& defined(HAVE_KHR_shader_subgroup_ballot) && defined(HAVE_ARB_shader_atomic_counter_ops)
66+
#define HAVE_subgroup_stream_compaction
67+
#endif

src/engine/renderer/glsl_source/cull_cp.glsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3535
/* cull_cp.glsl */
3636

3737
#insert common_cp
38+
#insert material_cp
3839

3940
// Keep this to 64 because we don't want extra shared mem etc. to be allocated, and to minimize wasted lanes
4041
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
===========================================================================
3+
4+
Daemon BSD Source Code
5+
Copyright (c) 2024-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+
/* adaptiveLightingReduction_cp.glsl */
36+
37+
#insert common_cp
38+
39+
// Keep this to 8x8 because we don't want extra shared mem etc. to be allocated, and to minimize wasted lanes
40+
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
41+
42+
layout(binding = 0) uniform sampler2D renderImage;
43+
44+
uniform uint u_ViewWidth;
45+
uniform uint u_ViewHeight;
46+
47+
/* x: log2( HDRMax )
48+
y: reserved
49+
z: reserved
50+
w: reserved */
51+
uniform vec4 u_TonemapParms2;
52+
53+
layout (binding = BIND_LUMINANCE) uniform atomic_uint atomicLuminance;
54+
55+
float ColorToLuminance( const in vec3 color ) {
56+
float luminance = dot( color.rgb, vec3( 0.2126f, 0.7152f, 0.0722f ) ); // sRGB luminance
57+
58+
return luminance > 0.0f ? clamp( log2( luminance ) + 8, 0.0, u_TonemapParms2.x + 8 ) : 0.0f;
59+
}
60+
61+
uint FloatLuminanceToUint( const in float luminance ) {
62+
return uint( luminance * 256 );
63+
}
64+
65+
void main() {
66+
const ivec2 position = ivec2( gl_GlobalInvocationID.xy );
67+
if( position.x >= u_ViewWidth || position.y >= u_ViewHeight ) {
68+
return;
69+
};
70+
71+
const float luminance = ColorToLuminance( texelFetch( renderImage, position, 0 ).rgb );
72+
73+
#if defined(HAVE_subgroup_atomic)
74+
const float luminanceSum = subgroupInclusiveAdd( luminance );
75+
76+
if( subgroupElect() ) {
77+
atomicCounterAddARB( atomicLuminance, FloatLuminanceToUint( luminanceSum ) );
78+
}
79+
#else
80+
atomicCounterAddARB( atomicLuminance, FloatLuminanceToUint( luminance ) );
81+
#endif
82+
}

0 commit comments

Comments
 (0)