diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 57eb1d7526a..ccbbcc23e42 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -6,8 +6,9 @@
Resource for environment nodes (like [WorldEnvironment]) that define multiple environment operations (such as background [Sky] or [Color], ambient light, fog, depth-of-field...). These parameters affect the final render of the scene. The order of these operations is:
- Depth of Field Blur
+ - Auto Exposure
- Glow
- - Tonemap (Auto Exposure)
+ - Tonemap
- Adjustments
@@ -124,15 +125,15 @@
If set above [code]0.0[/code], renders the scene's directional light(s) in the fog color depending on the view angle. This can be used to give the impression that the sun is "piercing" through the fog.
-
+
The glow blending mode.
- [b]Note:[/b] [member glow_blend_mode] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
+ [b]Note:[/b] The Compatibility renderer always uses [constant GLOW_BLEND_MODE_SCREEN] and [member glow_blend_mode] will have no effect.
The bloom's intensity. If set to a value higher than [code]0[/code], this will make glow visible in areas darker than the [member glow_hdr_threshold].
- If [code]true[/code], the glow effect is enabled. This simulates real world eye/camera behavior where bright pixels bleed onto surrounding pixels.
+ If [code]true[/code], the glow effect is enabled. This simulates real world atmosphere and eye/camera behavior by causing bright pixels to bleed onto surrounding pixels.
[b]Note:[/b] When using the Mobile rendering method, glow looks different due to the lower dynamic range available in the Mobile rendering method.
[b]Note:[/b] When using the Compatibility rendering method, glow uses a different implementation with some properties being unavailable and hidden from the inspector: [code]glow_levels/*[/code], [member glow_normalized], [member glow_strength], [member glow_blend_mode], [member glow_mix], [member glow_map], and [member glow_map_strength]. This implementation is optimized to run on low-end devices and is less flexible as a result.
@@ -145,26 +146,26 @@
The lower threshold of the HDR glow. When using the Mobile rendering method (which only supports a lower dynamic range up to [code]2.0[/code]), this may need to be below [code]1.0[/code] for glow to be visible. A value of [code]0.9[/code] works well in this case. This value also needs to be decreased below [code]1.0[/code] when using glow in 2D, as 2D rendering is performed in SDR.
-
+
The overall brightness multiplier of the glow effect. When using the Mobile rendering method (which only supports a lower dynamic range up to [code]2.0[/code]), this should be increased to [code]1.5[/code] to compensate.
-
+
The intensity of the 1st level of glow. This is the most "local" level (least blurry).
[b]Note:[/b] [member glow_levels/1] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
-
+
The intensity of the 2nd level of glow.
[b]Note:[/b] [member glow_levels/2] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
-
+
The intensity of the 3rd level of glow.
[b]Note:[/b] [member glow_levels/3] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
-
+
The intensity of the 4th level of glow.
[b]Note:[/b] [member glow_levels/4] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
-
+
The intensity of the 5th level of glow.
[b]Note:[/b] [member glow_levels/5] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
@@ -434,19 +435,19 @@
[b]Note:[/b] [member tonemap_white] is fixed at a value of [code]16.29[/code], which makes [constant TONE_MAPPER_AGX] unsuitable for use with the Mobile rendering method.
- Additive glow blending mode. Mostly used for particles, glows (bloom), lens flare, bright sources.
+ Adds the glow effect to the scene.
- Screen glow blending mode. Increases brightness, used frequently with bloom.
+ Adds the glow effect to the scene after modifying the glow influence based on the scene value; dark values will be highly influenced by glow and bright values will not be influenced by glow. This approach avoids bright values becoming overly bright from the glow effect. [member tonemap_white] is used to determine the maximum scene value where the glow should have no influence. When [member tonemap_mode] is set to [constant TONE_MAPPER_LINEAR], a value of [code]1.0[/code] will be used as the maximum scene value.
- Soft light glow blending mode. Modifies contrast, exposes shadows and highlights (vivid bloom).
+ Adds the glow effect to the tonemapped image after modifying the glow influence based on the image value; dark values and bright values will not be influenced by glow and mid-range values will be highly influenced by glow. This approach avoids bright values becoming overly bright from the glow effect. The glow will have the largest influence on image values of [code]0.25[/code] and will have no influence when applied to image values greater than [code]1.0[/code].
- Replace glow blending mode. Replaces all pixels' color by the glow value. This can be used to simulate a full-screen blur effect by tweaking the glow parameters to match the original image's brightness.
+ Replaces all pixels' color by the glow effect. This can be used to simulate a full-screen blur effect by tweaking the glow parameters to match the original image's brightness or to preview glow configuration in the editor.
- Mixes the glow with the underlying color to avoid increasing brightness as much while still maintaining a glow effect.
+ Mixes the glow image with the scene image. Best used with [member glow_bloom] to avoid darkening the scene.
Use a physically-based fog model defined primarily by fog density.
diff --git a/drivers/gles3/effects/post_effects.cpp b/drivers/gles3/effects/post_effects.cpp
index 105c8f6b71a..a7191385b4e 100644
--- a/drivers/gles3/effects/post_effects.cpp
+++ b/drivers/gles3/effects/post_effects.cpp
@@ -87,7 +87,7 @@ void PostEffects::_draw_screen_triangle() {
glBindVertexArray(0);
}
-void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
+void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, float p_srgb_white, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
@@ -124,6 +124,7 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin
post.shader.version_set_uniform(PostShaderGLES3::PIXEL_SIZE, 1.0 / p_source_size.x, 1.0 / p_source_size.y, post.shader_version, mode, flags);
post.shader.version_set_uniform(PostShaderGLES3::GLOW_INTENSITY, p_glow_intensity, post.shader_version, mode, flags);
+ post.shader.version_set_uniform(PostShaderGLES3::SRGB_WHITE, p_srgb_white, post.shader_version, mode, flags);
}
post.shader.version_set_uniform(PostShaderGLES3::VIEW, float(p_view), post.shader_version, mode, flags);
diff --git a/drivers/gles3/effects/post_effects.h b/drivers/gles3/effects/post_effects.h
index e6a836c39ab..8fbf8b3d685 100644
--- a/drivers/gles3/effects/post_effects.h
+++ b/drivers/gles3/effects/post_effects.h
@@ -58,7 +58,7 @@ public:
PostEffects();
~PostEffects();
- void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
+ void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, float p_srgb_white, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
};
} //namespace GLES3
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index e2ae7df580d..3df4d82f7ba 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -2814,6 +2814,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0;
+ float srgb_white = 1.0;
if (p_render_data->environment.is_valid()) {
glow_enabled = environment_get_glow_enabled(p_render_data->environment);
glow_intensity = environment_get_glow_intensity(p_render_data->environment);
@@ -2821,9 +2822,13 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glow_hdr_bleed_threshold = environment_get_glow_hdr_bleed_threshold(p_render_data->environment);
glow_hdr_bleed_scale = environment_get_glow_hdr_bleed_scale(p_render_data->environment);
glow_hdr_luminance_cap = environment_get_glow_hdr_luminance_cap(p_render_data->environment);
+ srgb_white = environment_get_white(p_render_data->environment);
}
if (glow_enabled) {
+ // Only glow requires srgb_white to be calculated.
+ srgb_white = 1.055 * Math::pow(srgb_white, 1.0f / 2.4f) - 0.055;
+
rb->check_glow_buffers();
}
@@ -2890,7 +2895,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
}
// Copy color buffer
- post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, 0, false, bcs_spec_constants);
+ post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, srgb_white, 0, false, bcs_spec_constants);
// Copy depth buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int);
@@ -2958,7 +2963,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v);
- post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true, bcs_spec_constants);
+ post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, srgb_white, v, true, bcs_spec_constants);
}
// Copy depth
diff --git a/drivers/gles3/shaders/effects/post.glsl b/drivers/gles3/shaders/effects/post.glsl
index 904158abcd4..d8912e7063b 100644
--- a/drivers/gles3/shaders/effects/post.glsl
+++ b/drivers/gles3/shaders/effects/post.glsl
@@ -45,6 +45,7 @@ uniform float luminance_multiplier;
uniform sampler2D glow_color; // texunit:1
uniform vec2 pixel_size;
uniform float glow_intensity;
+uniform float srgb_white;
vec4 get_glow_color(vec2 uv) {
vec2 half_pixel = pixel_size * 0.5;
@@ -58,6 +59,10 @@ vec4 get_glow_color(vec2 uv) {
color += textureLod(glow_color, uv + vec2(0.0, -half_pixel.y * 2.0), 0.0);
color += textureLod(glow_color, uv + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0;
+#ifdef USE_LUMINANCE_MULTIPLIER
+ color = color / luminance_multiplier;
+#endif
+
return color / 12.0;
}
#endif // USE_GLOW
@@ -102,18 +107,38 @@ void main() {
vec4 color = texture(source_color, uv_interp);
#endif
-#ifdef USE_GLOW
- vec4 glow = get_glow_color(uv_interp) * glow_intensity;
-
- // Just use softlight...
- glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
- color.rgb = max((color.rgb + glow.rgb) - (color.rgb * glow.rgb), vec3(0.0));
-#endif // USE_GLOW
-
#ifdef USE_LUMINANCE_MULTIPLIER
color = color / luminance_multiplier;
#endif
+#ifdef USE_GLOW
+ // Glow blending is performed before srgb_to_linear because
+ // the glow texture was created from a nonlinear sRGB-encoded
+ // scene, so it only makes sense to add this glow to an equally
+ // nonlinear sRGB-encoded scene.
+
+ vec4 glow = get_glow_color(uv_interp) * glow_intensity;
+
+ // Glow always uses the screen blend mode in the Compatibility renderer:
+
+ // Glow cannot be above 1.0 after normalizing and should be non-negative
+ // to produce expected results. It is possible that glow can be negative
+ // if negative lights were used in the scene.
+ // We clamp to srgb_white because glow will be normalized to this range.
+ // Note: srgb_white cannot be smaller than the maximum output value (1.0).
+ glow.rgb = clamp(glow.rgb, 0.0, srgb_white);
+
+ // Normalize to srgb_white range.
+ //glow.rgb /= srgb_white;
+ //color.rgb /= srgb_white;
+ //color.rgb = (color.rgb + glow.rgb) - (color.rgb * glow.rgb);
+ // Expand back to original range.
+ //color.rgb *= srgb_white;
+
+ // The following is a mathematically simplified version of the above.
+ color.rgb = color.rgb + glow.rgb - (color.rgb * glow.rgb / srgb_white);
+#endif // USE_GLOW
+
color.rgb = srgb_to_linear(color.rgb);
color.rgb = apply_tonemapping(color.rgb, white);
color.rgb = linear_to_srgb(color.rgb);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 979adca4a93..beb116c5fbd 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1247,7 +1247,7 @@ void Environment::_bind_methods() {
ADD_GROUP("Tonemap", "tonemap_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES,AgX"), "set_tonemapper", "get_tonemapper");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "1,16,0.01,or_greater"), "set_tonemap_white", "get_tonemap_white");
// SSR
@@ -1405,7 +1405,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_strength", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_glow_strength", "get_glow_strength");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_mix", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_glow_mix", "get_glow_mix");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_bloom", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_glow_bloom", "get_glow_bloom");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "glow_blend_mode", PROPERTY_HINT_ENUM, "Additive,Screen,Softlight,Replace,Mix"), "set_glow_blend_mode", "get_glow_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "glow_blend_mode", PROPERTY_HINT_ENUM, "Additive,Screen,Soft Light,Replace,Mix"), "set_glow_blend_mode", "get_glow_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_threshold", PROPERTY_HINT_RANGE, "0.0,4.0,0.01"), "set_glow_hdr_bleed_threshold", "get_glow_hdr_bleed_threshold");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_scale", PROPERTY_HINT_RANGE, "0.0,4.0,0.01"), "set_glow_hdr_bleed_scale", "get_glow_hdr_bleed_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_luminance_cap", PROPERTY_HINT_RANGE, "0.0,256.0,0.01"), "set_glow_hdr_luminance_cap", "get_glow_hdr_luminance_cap");
@@ -1572,11 +1572,11 @@ Environment::Environment() {
set_camera_feed_id(bg_camera_feed_id);
glow_levels.resize(7);
- glow_levels.write[0] = 0.0;
- glow_levels.write[1] = 0.0;
- glow_levels.write[2] = 1.0;
- glow_levels.write[3] = 0.0;
- glow_levels.write[4] = 1.0;
+ glow_levels.write[0] = 1.0;
+ glow_levels.write[1] = 0.8;
+ glow_levels.write[2] = 0.4;
+ glow_levels.write[3] = 0.1;
+ glow_levels.write[4] = 0.0;
glow_levels.write[5] = 0.0;
glow_levels.write[6] = 0.0;
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 400a7dfc3cb..21309e93ef9 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -163,11 +163,11 @@ private:
bool glow_enabled = false;
Vector glow_levels;
bool glow_normalize_levels = false;
- float glow_intensity = 0.8;
+ float glow_intensity = 0.3;
float glow_strength = 1.0;
float glow_mix = 0.05;
float glow_bloom = 0.0;
- GlowBlendMode glow_blend_mode = GLOW_BLEND_MODE_SOFTLIGHT;
+ GlowBlendMode glow_blend_mode = GLOW_BLEND_MODE_SCREEN;
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0;
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h
index 25048035062..f14455d9a5f 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.h
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.h
@@ -114,10 +114,10 @@ public:
GLOW_MODE_MIX
};
- GlowMode glow_mode = GLOW_MODE_ADD;
- float glow_intensity = 1.0;
+ GlowMode glow_mode = GLOW_MODE_SCREEN;
+ float glow_intensity = 0.3;
float glow_map_strength = 0.0f;
- float glow_levels[7] = { 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0 };
+ float glow_levels[7] = { 1.0, 0.8, 0.4, 0.1, 0.0, 0.0, 0.0 };
Vector2i glow_texture_size;
bool glow_use_bicubic_upscale = false;
RID glow_texture;
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 8e3eca40977..fbf2149e0cb 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -393,6 +393,8 @@ vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 6).rgb * params.glow_levels[6];
}
+ glow = glow * params.luminance_multiplier;
+
return glow;
}
@@ -402,21 +404,42 @@ vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
#define GLOW_MODE_REPLACE 3
#define GLOW_MODE_MIX 4
-vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode
+// Applies glow using the selected blending mode. Does not handle the mix blend mode.
+vec3 apply_glow(vec3 color, vec3 glow, float white) {
if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) {
- // Needs color clamping.
- glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
- return max((color + glow) - (color * glow), vec3(0.0));
- } else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
- // Needs color clamping.
- glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
- glow = glow * vec3(0.5f) + vec3(0.5f);
+ // Glow cannot be above 1.0 after normalizing and should be non-negative
+ // to produce expected results. It is possible that glow can be negative
+ // if negative lights were used in the scene.
+ // We clamp to white because glow will be normalized to this range.
+ // Note: white cannot be smaller than the maximum output value.
+ glow.rgb = clamp(glow.rgb, 0.0, white);
+
+ // Normalize to white range.
+ //glow.rgb /= white;
+ //color.rgb /= white;
+ //color.rgb = (color.rgb + glow.rgb) - (color.rgb * glow.rgb);
+ // Expand back to original range.
+ //color.rgb *= white;
+
+ // The following is a mathematically simplified version of the above.
+ color.rgb = color.rgb + glow.rgb - (color.rgb * glow.rgb / white);
+
+ return color;
+ } else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
+ // Glow cannot be above 1.0 should be non-negative to produce
+ // expected results. It is possible that glow can be negative
+ // if negative lights were used in the scene.
+ // Note: This approach causes a discontinuity with scene values
+ // at 1.0, but because this glow should have its strongest influence
+ // anchored at 0.25 there is no way around this.
+ glow.rgb = clamp(glow.rgb, 0.0, 1.0);
+
+ color.r = color.r > 1.0 ? color.r : color.r + glow.r * ((color.r <= 0.25f ? ((16.0f * color.r - 12.0f) * color.r + 4.0f) * color.r : sqrt(color.r)) - color.r);
+ color.g = color.g > 1.0 ? color.g : color.g + glow.g * ((color.g <= 0.25f ? ((16.0f * color.g - 12.0f) * color.g + 4.0f) * color.g : sqrt(color.g)) - color.g);
+ color.b = color.b > 1.0 ? color.b : color.b + glow.b * ((color.b <= 0.25f ? ((16.0f * color.b - 12.0f) * color.b + 4.0f) * color.b : sqrt(color.b)) - color.b);
- color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
- color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g)));
- color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b)));
return color;
} else { //replace
return glow;
@@ -859,47 +882,56 @@ void main() {
color.rgb *= exposure;
- // Early Tonemap & SRGB Conversion
+ // Single-pass FXAA and pre-tonemap glow.
+
#ifndef SUBPASS
if (bool(params.flags & FLAG_USE_FXAA)) {
// FXAA must be performed before glow to preserve the "bleed" effect of glow.
color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
}
- if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_MIX) {
- vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier;
- if (params.glow_map_strength > 0.001) {
- glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
+ if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_SOFTLIGHT) {
+ vec3 glow = gather_glow(source_glow, uv_interp);
+ if (params.glow_mode == GLOW_MODE_MIX) {
+ if (params.glow_map_strength > 0.001) {
+ glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
+ }
+ color.rgb = mix(color.rgb, glow, params.glow_intensity);
+ } else {
+ glow = glow * params.glow_intensity;
+ if (params.glow_map_strength > 0.001) {
+ glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
+ }
+ color.rgb = apply_glow(color.rgb, glow, params.white);
}
- color.rgb = mix(color.rgb, glow, params.glow_intensity);
}
#endif
+ // Tonemap to lower dynamic range.
+
color.rgb = apply_tonemapping(color.rgb, params.white);
+ // Additional effects.
+
+#ifndef SUBPASS
+ if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_SOFTLIGHT) {
+ // Apply soft light after tonemapping to mitigate the issue of discontinuity
+ // at 1.0 and higher. This makes the issue only appear with HDR output that
+ // can exceed a 1.0 output value.
+ vec3 glow = gather_glow(source_glow, uv_interp);
+ glow = glow * params.glow_intensity;
+ if (params.glow_map_strength > 0.001) {
+ glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
+ }
+ glow = apply_tonemapping(glow, params.white);
+ color.rgb = apply_glow(color.rgb, glow, params.white);
+ }
+#endif
+
bool convert_to_srgb = bool(params.flags & FLAG_CONVERT_TO_SRGB);
if (convert_to_srgb) {
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
}
-#ifndef SUBPASS
- // Glow
- if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_MIX) {
- vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier;
- if (params.glow_map_strength > 0.001) {
- glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
- }
-
- // high dynamic range -> SRGB
- glow = apply_tonemapping(glow, params.white);
- if (convert_to_srgb) {
- glow = linear_to_srgb(glow);
- }
-
- color.rgb = apply_glow(color.rgb, glow);
- }
-#endif
-
- // Additional effects
if (bool(params.flags & FLAG_USE_BCS)) {
color.rgb = apply_bcs(color.rgb, params.bcs);
diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp
index bfb2852d5f0..da8bd7cb8a8 100644
--- a/servers/rendering/storage/environment_storage.cpp
+++ b/servers/rendering/storage/environment_storage.cpp
@@ -208,7 +208,13 @@ void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::Environm
ERR_FAIL_NULL(env);
env->exposure = p_exposure;
env->tone_mapper = p_tone_mapper;
- env->white = p_white;
+ if (p_tone_mapper == RS::ENV_TONE_MAPPER_LINEAR) {
+ env->white = 1.0; // With HDR output, this should be the output max value instead.
+ } else if (p_tone_mapper == RS::ENV_TONE_MAPPER_AGX) {
+ env->white = 16.29;
+ } else {
+ env->white = MAX(1.0, p_white); // Glow with screen blend mode does not work when white < 1.0.
+ }
}
RS::EnvironmentToneMapper RendererEnvironmentStorage::environment_get_tone_mapper(RID p_env) const {
@@ -471,7 +477,7 @@ Vector RendererEnvironmentStorage::environment_get_glow_levels(RID p_env)
float RendererEnvironmentStorage::environment_get_glow_intensity(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
- ERR_FAIL_NULL_V(env, 0.8);
+ ERR_FAIL_NULL_V(env, 0.3);
return env->glow_intensity;
}
@@ -495,7 +501,7 @@ float RendererEnvironmentStorage::environment_get_glow_mix(RID p_env) const {
RS::EnvironmentGlowBlendMode RendererEnvironmentStorage::environment_get_glow_blend_mode(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
- ERR_FAIL_NULL_V(env, RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT);
+ ERR_FAIL_NULL_V(env, RS::ENV_GLOW_BLEND_MODE_SCREEN);
return env->glow_blend_mode;
}
diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h
index 69cbdc6adce..fab1f025e66 100644
--- a/servers/rendering/storage/environment_storage.h
+++ b/servers/rendering/storage/environment_storage.h
@@ -98,11 +98,11 @@ private:
// Glow
bool glow_enabled = false;
Vector glow_levels;
- float glow_intensity = 0.8;
+ float glow_intensity = 0.3;
float glow_strength = 1.0;
float glow_bloom = 0.0;
float glow_mix = 0.01;
- RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT;
+ RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SCREEN;
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_luminance_cap = 12.0;
float glow_hdr_bleed_scale = 2.0;