Skip to content

Commit 0c7f013

Browse files
committed
Improve Environment color adjustments; specifically brightness and HDR 2D contrast.
This commit changes adjustments to behave as follows for all rendering configurations: - Apply brightness to linear-encoded values, preventing contrast, saturation, and hue from being affected. - Apply contrast to perceptually uniform (nonlinear sRGB-encoded) values, matching existing behavior when HDR 2D is disabled and producing optimal visual quality. - Apply saturation with even color channel weights. This causes brightness of certain colors to change, but matches existing behavior when HDR 2D is disabled. Adjustments are applied after glow and tonemapping to match existing behavior.
1 parent f50d7fa commit 0c7f013

File tree

5 files changed

+70
-46
lines changed

5 files changed

+70
-46
lines changed

doc/classes/Environment.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,19 @@
3636
</methods>
3737
<members>
3838
<member name="adjustment_brightness" type="float" setter="set_adjustment_brightness" getter="get_adjustment_brightness" default="1.0">
39-
The global brightness value of the rendered scene. Effective only if [member adjustment_enabled] is [code]true[/code].
39+
Applies a simple brightness adjustment to the rendered image after tonemaping. To adjust scene brightness use [member tonemap_exposure] instead, which is applied before tonemapping and thus less prone to issues with bright colors. Effective only if [member adjustment_enabled] is [code]true[/code].
4040
</member>
4141
<member name="adjustment_color_correction" type="Texture" setter="set_adjustment_color_correction" getter="get_adjustment_color_correction">
4242
The [Texture2D] or [Texture3D] lookup table (LUT) to use for the built-in post-process color grading. Can use a [GradientTexture1D] for a 1-dimensional LUT, or a [Texture3D] for a more complex LUT. Effective only if [member adjustment_enabled] is [code]true[/code].
4343
</member>
4444
<member name="adjustment_contrast" type="float" setter="set_adjustment_contrast" getter="get_adjustment_contrast" default="1.0">
45-
The global contrast value of the rendered scene (default value is 1). Effective only if [member adjustment_enabled] is [code]true[/code].
45+
Increasing [member adjustment_contrast] will make dark values darker and bright values brighter. This simple adjustment is applied to the rendered image after tonemaping. When set to a value greater than [code]1.0[/code], [member adjustment_contrast] is prone to clipping colors that become too bright or too dark. Effective only if [member adjustment_enabled] is [code]true[/code].
4646
</member>
4747
<member name="adjustment_enabled" type="bool" setter="set_adjustment_enabled" getter="is_adjustment_enabled" default="false">
4848
If [code]true[/code], enables the [code]adjustment_*[/code] properties provided by this resource. If [code]false[/code], modifications to the [code]adjustment_*[/code] properties will have no effect on the rendered scene.
4949
</member>
5050
<member name="adjustment_saturation" type="float" setter="set_adjustment_saturation" getter="get_adjustment_saturation" default="1.0">
51-
The global color saturation value of the rendered scene (default value is 1). Effective only if [member adjustment_enabled] is [code]true[/code].
51+
Applies a simple saturation adjustment to the rendered image after tonemaping. When [member adjustment_saturation] is set to [code]0.0[/code], the rendered image will be fully converted to a grayscale image. Effective only if [member adjustment_enabled] is [code]true[/code].
5252
</member>
5353
<member name="ambient_light_color" type="Color" setter="set_ambient_light_color" getter="get_ambient_light_color" default="Color(0, 0, 0, 1)">
5454
The ambient light's [Color]. Only effective if [member ambient_light_sky_contribution] is lower than [code]1.0[/code] (exclusive).

drivers/gles3/shaders/effects/post.glsl

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,6 @@ vec3 apply_color_correction(vec3 color) {
8686
#endif // USE_1D_LUT
8787
#endif // USE_COLOR_CORRECTION
8888

89-
#ifdef USE_BCS
90-
vec3 apply_bcs(vec3 color) {
91-
color = mix(vec3(0.0), color, brightness);
92-
color = mix(vec3(0.5), color, contrast);
93-
color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation);
94-
95-
return color;
96-
}
97-
#endif
98-
9989
in vec2 uv_interp;
10090

10191
layout(location = 0) out vec4 frag_color;
@@ -140,12 +130,33 @@ void main() {
140130
#endif // USE_GLOW
141131

142132
color.rgb = srgb_to_linear(color.rgb);
133+
143134
color.rgb = apply_tonemapping(color.rgb, white);
144-
color.rgb = linear_to_srgb(color.rgb);
145135

146136
#ifdef USE_BCS
147-
color.rgb = apply_bcs(color.rgb);
148-
#endif
137+
// Apply brightness:
138+
// Apply to relative luminance. This ensures that the hue and saturation of
139+
// colors is not affected by the adjustment, but requires the multiplication
140+
// to be performed on linear-encoded values.
141+
color.rgb = color.rgb * brightness;
142+
143+
color.rgb = linear_to_srgb(color.rgb);
144+
145+
// Apply contrast:
146+
// By applying contrast to RGB values that are perceptually uniform (nonlinear),
147+
// the darkest values are not hard-clipped as badly, which produces a
148+
// higher quality contrast adjustment and maintains compatibility with
149+
// existing projects.
150+
color.rgb = mix(vec3(0.5), color.rgb, contrast);
151+
152+
// Apply saturation:
153+
// By applying saturation adjustment to nonlinear sRGB-encoded values with
154+
// even weights the preceived brightness of blues are affected, but this
155+
// maintains compatibility with existing projects.
156+
color.rgb = mix(vec3(dot(vec3(1.0), color.rgb) * (1.0 / 3.0)), color.rgb, saturation);
157+
#else
158+
color.rgb = linear_to_srgb(color.rgb);
159+
#endif // USE_BCS
149160

150161
#ifdef USE_COLOR_CORRECTION
151162
color.rgb = apply_color_correction(color.rgb);

scene/resources/environment.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ void Environment::_bind_methods() {
12461246

12471247
ADD_GROUP("Tonemap", "tonemap_");
12481248
ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES,AgX"), "set_tonemapper", "get_tonemapper");
1249-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure");
1249+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,4,0.01,or_greater"), "set_tonemap_exposure", "get_tonemap_exposure");
12501250
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "1,16,0.01,or_greater"), "set_tonemap_white", "get_tonemap_white");
12511251

12521252
// SSR
@@ -1522,9 +1522,9 @@ void Environment::_bind_methods() {
15221522

15231523
ADD_GROUP("Adjustments", "adjustment_");
15241524
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "adjustment_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_adjustment_enabled", "is_adjustment_enabled");
1525-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_brightness", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_brightness", "get_adjustment_brightness");
1526-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_contrast", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_contrast", "get_adjustment_contrast");
1527-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_saturation", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_saturation", "get_adjustment_saturation");
1525+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_brightness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_adjustment_brightness", "get_adjustment_brightness");
1526+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_contrast", PROPERTY_HINT_RANGE, "0.75,1.25,0.005,or_less,or_greater"), "set_adjustment_contrast", "get_adjustment_contrast");
1527+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_saturation", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_less,or_greater"), "set_adjustment_saturation", "get_adjustment_saturation");
15281528
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "adjustment_color_correction", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D,Texture3D"), "set_adjustment_color_correction", "get_adjustment_color_correction");
15291529

15301530
// Constants

servers/rendering/renderer_rd/shaders/effects/tonemap.glsl

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,15 @@ vec3 tonemap_agx(vec3 color) {
329329
}
330330

331331
vec3 linear_to_srgb(vec3 color) {
332-
// Clamping is not strictly necessary for floating point nonlinear sRGB encoding,
333-
// but many cases that call this function need the result clamped.
334-
color = clamp(color, vec3(0.0), vec3(1.0));
335332
const vec3 a = vec3(0.055f);
336333
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
337334
}
338335

336+
vec3 srgb_to_linear(vec3 color) {
337+
const vec3 a = vec3(0.055f);
338+
return mix(pow((color.rgb + a) * (1.0f / (vec3(1.0f) + a)), vec3(2.4f)), color.rgb * (1.0f / 12.92f), lessThan(color.rgb, vec3(0.04045f)));
339+
}
340+
339341
#define TONEMAPPER_LINEAR 0
340342
#define TONEMAPPER_REINHARD 1
341343
#define TONEMAPPER_FILMIC 2
@@ -446,13 +448,6 @@ vec3 apply_glow(vec3 color, vec3 glow, float white) {
446448
}
447449
}
448450

449-
vec3 apply_bcs(vec3 color, vec3 bcs) {
450-
color = mix(vec3(0.0f), color, bcs.x);
451-
color = mix(vec3(0.5f), color, bcs.y);
452-
color = mix(vec3(dot(vec3(1.0f), color) * 0.33333f), color, bcs.z);
453-
454-
return color;
455-
}
456451
#ifdef USE_1D_LUT
457452
vec3 apply_color_correction(vec3 color) {
458453
color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
@@ -928,23 +923,39 @@ void main() {
928923
}
929924
#endif
930925

931-
bool convert_to_srgb = bool(params.flags & FLAG_CONVERT_TO_SRGB);
932-
if (convert_to_srgb) {
933-
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
934-
}
935-
936926
if (bool(params.flags & FLAG_USE_BCS)) {
937-
color.rgb = apply_bcs(color.rgb, params.bcs);
938-
}
939-
940-
if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) {
941-
// apply_color_correction requires nonlinear sRGB encoding
942-
if (!convert_to_srgb) {
943-
color.rgb = linear_to_srgb(color.rgb);
927+
// Apply brightness:
928+
// Apply to relative luminance. This ensures that the hue and saturation of
929+
// colors is not affected by the adjustment, but requires the multiplication
930+
// to be performed on linear-encoded values.
931+
color.rgb = color.rgb * params.bcs.x;
932+
933+
color.rgb = linear_to_srgb(color.rgb);
934+
935+
// Apply contrast:
936+
// By applying contrast to RGB values that are perceptually uniform (nonlinear),
937+
// the darkest values are not hard-clipped as badly, which produces a
938+
// higher quality contrast adjustment and maintains compatibility with
939+
// existing projects.
940+
color.rgb = mix(vec3(0.5), color.rgb, params.bcs.y);
941+
942+
// Apply saturation:
943+
// By applying saturation adjustment to nonlinear sRGB-encoded values with
944+
// even weights the preceived brightness of blues are affected, but this
945+
// maintains compatibility with existing projects.
946+
color.rgb = mix(vec3(dot(vec3(1.0), color.rgb) * (1.0 / 3.0)), color.rgb, params.bcs.z);
947+
948+
if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) {
949+
color.rgb = clamp(color.rgb, vec3(0.0), vec3(1.0));
950+
color.rgb = apply_color_correction(color.rgb);
951+
// When using color correction and FLAG_CONVERT_TO_SRGB is false, there
952+
// is no need to convert back to linear because the color correction
953+
// texture sampling does this for us.
954+
} else if (!bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
955+
color.rgb = srgb_to_linear(color.rgb);
944956
}
945-
color.rgb = apply_color_correction(color.rgb);
946-
// When convert_to_srgb is false, there is no need to convert back to
947-
// linear because the color correction texture sampling does this for us.
957+
} else if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
958+
color.rgb = linear_to_srgb(color.rgb);
948959
}
949960

950961
// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.

servers/rendering/storage/environment_storage.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,9 @@ void RendererEnvironmentStorage::environment_set_adjustment(RID p_env, bool p_en
793793
ERR_FAIL_NULL(env);
794794

795795
env->adjustments_enabled = p_enable;
796-
env->adjustments_brightness = p_brightness;
796+
// Scale brightness via the nonlinear sRGB transfer function to provide a
797+
// somewhat perceptually uniform brightness adjustment.
798+
env->adjustments_brightness = p_brightness < 0.04045f ? p_brightness * (1.0f / 12.92f) : Math::pow(float((p_brightness + 0.055f) * (1.0f / (1.055f))), 2.4f);
797799
env->adjustments_contrast = p_contrast;
798800
env->adjustments_saturation = p_saturation;
799801
env->use_1d_color_correction = p_use_1d_color_correction;

0 commit comments

Comments
 (0)