Skip to content

Commit 2d449fc

Browse files
committed
renderer: implement low light restoration for when tone mapping is enabled
1 parent a9b3fdc commit 2d449fc

File tree

4 files changed

+67
-1
lines changed

4 files changed

+67
-1
lines changed

src/engine/renderer/gl_shader.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,11 @@ static std::string GenEngineConstants() {
799799
AddDefine( str, "r_highPrecisionRendering", 1 );
800800
}
801801

802+
if ( r_toneMappingLowLightRestorationSteps.Get() ) {
803+
AddDefine( str, "r_toneMappingLowLightRestorationSteps", r_toneMappingLowLightRestorationSteps.Get() );
804+
AddDefine( str, "r_toneMappingLowLightRestorationThreshold", r_toneMappingLowLightRestorationThreshold.Get() );
805+
}
806+
802807
if ( r_dithering.Get() ) {
803808
AddDefine( str, "r_dithering", 1 );
804809
}

src/engine/renderer/glsl_source/cameraEffects_fp.glsl

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,58 @@ void main()
8888

8989
#if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float)
9090
if( u_Tonemap ) {
91-
color.rgb = TonemapLottes( color.rgb );
91+
#if defined(r_toneMappingLowLightRestorationSteps)
92+
vec3 mapped = TonemapLottes( color.rgb );
93+
94+
/* The threshold is the color channel value under which we blend
95+
the tone mapped color with the original color to restore low
96+
light in dark shadows clipped by the tone mapper.
97+
98+
The threshold is in sRGB space for convenience. For example,
99+
a threshold of 5 would make sure we start restoring low light
100+
when the sRGB-converted tone mapped color goes below #050505,
101+
a threshold of 10 would do the same for #0A0A0A and 16 would
102+
target #101010.
103+
104+
We need to convert the threshold back to linear because we're
105+
still operating in linear space at this point.
106+
107+
We don't exactly restore up to the threshold, we blend half
108+
the tone-mapped color with half the color, to avoid producing
109+
color artifacts that would happen by bumping the RGB channels
110+
directly. Each step does the half blend again, so we can say
111+
that the restoration _tends to the threshold_ with each step.
112+
113+
Each step is done with a small bias to lower the threshold a bit
114+
every step to not not always use the exact same frontier between
115+
the area having light restored and the area being kept unmodified,
116+
then smoothly blending the restoratiin with a gradient.
117+
118+
Since we blend with half the colors, a threshold smaller than 2 would
119+
do nothing. */
120+
const float threshold =
121+
pow( float( r_toneMappingLowLightRestorationThreshold ) / 255.0f, 2.2f );
122+
123+
for ( int i = 0; i < r_toneMappingLowLightRestorationSteps; i++ )
124+
{
125+
float t = threshold - ( i * ( threshold / 10 ) );
126+
127+
bvec3 cutoff = lessThan(mapped, vec3(t));
128+
129+
#if __VERSION__ > 120
130+
vec3 interpolation = vec3(0.5f) * vec3(cutoff);
131+
#else
132+
vec3 interpolation = 0.5f * cutoff;
133+
#endif
134+
135+
mapped = mix( mapped, vec3(0.0f), interpolation )
136+
+ mix( vec3(0.0f), color.rgb, interpolation );
137+
}
138+
139+
color.rgb = mapped;
140+
#else
141+
color.rgb = TonemapLottes( color.rgb );
142+
#endif
92143
}
93144
#endif
94145

src/engine/renderer/tr_init.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ Cvar::Cvar<int> r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul
205205
Cvar::Range<Cvar::Cvar<float>> r_toneMappingDarkAreaPointLDR(
206206
"r_toneMappingDarkAreaPointLDR", "Convert to this brightness at dark area cut-off",
207207
Cvar::NONE, 0.268f, 0.0f, 1.0f );
208+
Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationSteps(
209+
"r_toneMappingLowLightRestorationSteps", "Amount of steps to restore the low lights",
210+
Cvar::NONE, 1, 0, 5 );
211+
Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationThreshold(
212+
"r_toneMappingLowLightRestorationThreshold", "Color channel sRGB value under which low light is restored",
213+
Cvar::NONE, 10, 2, 20 );
208214

209215
Cvar::Cvar<bool> r_dithering(
210216
"r_dithering", "Use dithering", Cvar::NONE, true );
@@ -1270,6 +1276,8 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p
12701276

12711277
Cvar::Latch( r_highPrecisionRendering );
12721278
Cvar::Latch( r_accurateSRGB );
1279+
Cvar::Latch( r_toneMappingLowLightRestorationSteps );
1280+
Cvar::Latch( r_toneMappingLowLightRestorationThreshold );
12731281
Cvar::Latch( r_dithering );
12741282

12751283
r_drawBuffer = Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT );

src/engine/renderer/tr_local.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,8 @@ enum
27162716

27172717
extern Cvar::Cvar<bool> r_highPrecisionRendering;
27182718
extern Cvar::Cvar<bool> r_accurateSRGB;
2719+
extern Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationSteps;
2720+
extern Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationThreshold;
27192721
extern Cvar::Cvar<bool> r_dithering;
27202722

27212723
extern Cvar::Range<Cvar::Cvar<int>> r_shadows;

0 commit comments

Comments
 (0)