Skip to content
Open
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
25 changes: 24 additions & 1 deletion src/engine/renderer/gl_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,29 @@ static std::string GenEngineConstants() {
AddDefine( str, "r_highPrecisionRendering", 1 );
}

if ( r_toneMappingLowLightRestorationSteps.Get() )
{
AddDefine( str, "r_toneMappingLowLightRestorationSteps", r_toneMappingLowLightRestorationSteps.Get() );
AddDefine( str, "r_toneMappingLowLightRestorationThreshold", r_toneMappingLowLightRestorationThreshold.Get() );

if ( r_showToneMappingLowLightRestoration.Get() )
{
AddDefine( str, "r_showToneMappingLowLightRestoration", 1 );
}
}

if ( r_lowLightDithering.Get() )
{
AddDefine( str, "r_lowLightDithering", 1 );

AddDefine( str, "r_lowLightDitheringThreshold", r_lowLightDitheringThreshold.Get() );

if ( r_showLowLightDithering.Get() )
{
AddDefine( str, "r_showLowLightDithering", 1 );
}
}

return str;
}

Expand Down Expand Up @@ -3066,4 +3089,4 @@ GlobalUBOProxy::GlobalUBOProxy() :
u_Tonemap( this ),
u_TonemapParms( this ),
u_Exposure( this ) {
}
}
108 changes: 107 additions & 1 deletion src/engine/renderer/glsl_source/cameraEffects_fp.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,116 @@ void main()

#if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float)
if( u_Tonemap ) {
color.rgb = TonemapLottes( color.rgb );
#if defined(r_toneMappingLowLightRestorationSteps)
vec3 mapped = TonemapLottes( color.rgb );

/* The threshold is the color channel value under which we blend
the tone mapped color with the original color to restore low
light in dark shadows clipped by the tone mapper.

The threshold is in sRGB space for convenience. For example,
a threshold of 5 would make sure we start restoring low light
when the sRGB-converted tone mapped color goes below #050505,
a threshold of 10 would do the same for #0A0A0A and 16 would
target #101010.

We need to convert the threshold back to linear because we're
still operating in linear space at this point.

We don't exactly restore up to the threshold, we blend half
the tone-mapped color with half the color, to avoid producing
color artifacts that would happen by bumping the RGB channels
directly. Each step does the half blend again, so we can say
that the restoration _tends to the threshold_ with each step.

Each step is done with a small bias to lower the threshold a bit
every step to not not always use the exact same frontier between
the area having light restored and the area being kept unmodified,
then smoothly blending the restoratiin with a gradient.

Since we blend with half the colors, a threshold smaller than 2
would do nothing. */
float threshold;

if ( u_SRGB )
{
threshold = pow( float( r_toneMappingLowLightRestorationThreshold ) / 255.0f, 2.2f );
}
else
{
threshold = float( r_toneMappingLowLightRestorationThreshold ) / 255.0f;
}

#if defined(r_showToneMappingLowLightRestoration)
color.rgb = vec3(1.0f, 0.0f, 0.0f);
#endif

bvec3 colorCutoff = lessThan( color.rgb, vec3( threshold ) );

for ( int i = 0; i < r_toneMappingLowLightRestorationSteps; i++ )
{
float t = threshold - ( i * ( threshold / 10 ) );

bvec3 mappedCutoff = lessThan( mapped, vec3(t) );

bvec3 cutoff = bvec3(ivec3(mappedCutoff) * ivec3(colorCutoff));

#if __VERSION__ > 120
vec3 interpolation = vec3(0.5f) * vec3(cutoff);
#else
vec3 interpolation = 0.5f * cutoff;
#endif

mapped = mix( mapped, color.rgb, interpolation );
}

color.rgb = mapped;
#else
color.rgb = TonemapLottes( color.rgb );
#endif
}
#endif

#if defined(r_lowLightDithering)
{
vec3 dithered = color.rgb;

const float bayer[ 16 ] = {
0.0f, 8.0f, 2.0f, 10.0f,
12.0f, 4.0f, 14.0f, 6.0f,
3.0f, 11.0f, 1.0f, 9.0f,
15.0f, 7.0f, 13.0f, 5.0f
};

float dither = bayer[
int( mod( gl_FragCoord.x, 4.0 ) ) +
int( mod( gl_FragCoord.y, 4.0 ) ) * 4
] / 16.0;

dithered += vec3( ( dither - 0.50 ) / 255.0 );

float threshold;

if ( u_SRGB )
{
threshold = pow( float( r_lowLightDitheringThreshold ) / 255.0f, 2.2f );
}
else
{
threshold = float( r_lowLightDitheringThreshold ) / 255.0f;
}

if ( ( color.r + color.g + color.b ) < ( 3 * threshold ) )
{
color.rgb = dithered;

#if defined(r_showLowLightDithering)
color.rgb = vec3(1.0f, 0.0f, 0.0f);
#endif
}
}
#endif

color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) );

if ( u_SRGB )
Expand Down
28 changes: 27 additions & 1 deletion src/engine/renderer/tr_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ Cvar::Cvar<int> r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul
"r_toneMappingExposure", "Exposure (brightness adjustment)", Cvar::NONE, 1.0f );
Cvar::Range<Cvar::Cvar<float>> r_toneMappingContrast(
"r_toneMappingContrast", "Makes dark areas light up faster",
Cvar::NONE, 1.6f, 1.0f, 10.0f );
Cvar::NONE, 1.4f, 1.0f, 10.0f );
Cvar::Range<Cvar::Cvar<float>> r_toneMappingHighlightsCompressionSpeed(
"r_toneMappingHighlightsCompressionSpeed", "Highlights saturation",
Cvar::NONE, 0.977f, 0.0f, 10.0f );
Expand All @@ -206,6 +206,24 @@ Cvar::Cvar<int> r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul
"r_toneMappingDarkAreaPointLDR", "Convert to this brightness at dark area cut-off",
Cvar::NONE, 0.268f, 0.0f, 1.0f );

Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationSteps(
"r_toneMappingLowLightRestorationSteps", "Amount of steps to restore the low lights",
Cvar::NONE, 1, 0, 5 );
Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationThreshold(
"r_toneMappingLowLightRestorationThreshold", "Color channel sRGB value under which low light is restored",
Cvar::NONE, 10, 2, 20 );
Cvar::Cvar<bool> r_showToneMappingLowLightRestoration(
"r_showToneMappingLowLightRestoration", "Show pixels affected by tone mapping low light restoration",
Cvar::CHEAT, false );

Cvar::Cvar<bool> r_lowLightDithering(
"r_lowLightDithering", "Use dithering in low light areas", Cvar::NONE, true );
Cvar::Range<Cvar::Cvar<int>> r_lowLightDitheringThreshold(
"r_lowLightDitheringThreshold", "Color channel sRGB value under which low light is dithered",
Cvar::NONE, 6, 2, 20 );
Cvar::Cvar<bool> r_showLowLightDithering(
"r_showLowLightDithering", "Show dithering applied on low light areas", Cvar::CHEAT, false );

cvar_t *r_lockpvs;
cvar_t *r_noportals;

Expand Down Expand Up @@ -1268,6 +1286,14 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p
Cvar::Latch( r_highPrecisionRendering );
Cvar::Latch( r_accurateSRGB );

Cvar::Latch( r_toneMappingLowLightRestorationSteps );
Cvar::Latch( r_toneMappingLowLightRestorationThreshold );
Cvar::Latch( r_showToneMappingLowLightRestoration );

Cvar::Latch( r_lowLightDithering );
Cvar::Latch( r_lowLightDitheringThreshold );
Cvar::Latch( r_showLowLightDithering );

r_drawBuffer = Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT );
r_lockpvs = Cvar_Get( "r_lockpvs", "0", CVAR_CHEAT );
r_noportals = Cvar_Get( "r_noportals", "0", CVAR_CHEAT );
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 @@ -2717,6 +2717,14 @@ enum
extern Cvar::Cvar<bool> r_highPrecisionRendering;
extern Cvar::Cvar<bool> r_accurateSRGB;

extern Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationSteps;
extern Cvar::Range<Cvar::Cvar<int>> r_toneMappingLowLightRestorationThreshold;
extern Cvar::Cvar<bool> r_showToneMappingLowLightRestoration;

extern Cvar::Cvar<bool> r_lowLightDithering;
extern Cvar::Range<Cvar::Cvar<int>> r_lowLightDitheringThreshold;
extern Cvar::Cvar<bool> r_showLowLightDithering;

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

extern cvar_t *r_lockpvs;
Expand Down
Loading