Blend Environment glow before tonemapping and change default blend mode to screen.

Additionally, change the minimum `tonemap_white` parameter to `1.0`; users can increase `tonemap_exposure` for a similar effect to decreasing `tonemap_white` below `1.0`.

Co-authored-by: Hei <40064911+Lielay9@users.noreply.github.com>
Co-authored-by: Hugo Locurcio <hugo.locurcio@hugo.pro>
This commit is contained in:
Allen Pestaluky 2025-09-17 17:09:46 -04:00
parent 9a5d6d1049
commit cafc012b05
11 changed files with 150 additions and 80 deletions

View file

@ -6,8 +6,9 @@
<description> <description>
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: 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 - Depth of Field Blur
- Auto Exposure
- Glow - Glow
- Tonemap (Auto Exposure) - Tonemap
- Adjustments - Adjustments
</description> </description>
<tutorials> <tutorials>
@ -124,15 +125,15 @@
<member name="fog_sun_scatter" type="float" setter="set_fog_sun_scatter" getter="get_fog_sun_scatter" default="0.0"> <member name="fog_sun_scatter" type="float" setter="set_fog_sun_scatter" getter="get_fog_sun_scatter" default="0.0">
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. 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.
</member> </member>
<member name="glow_blend_mode" type="int" setter="set_glow_blend_mode" getter="get_glow_blend_mode" enum="Environment.GlowBlendMode" default="2"> <member name="glow_blend_mode" type="int" setter="set_glow_blend_mode" getter="get_glow_blend_mode" enum="Environment.GlowBlendMode" default="1">
The glow blending mode. 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.
</member> </member>
<member name="glow_bloom" type="float" setter="set_glow_bloom" getter="get_glow_bloom" default="0.0"> <member name="glow_bloom" type="float" setter="set_glow_bloom" getter="get_glow_bloom" default="0.0">
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]. 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].
</member> </member>
<member name="glow_enabled" type="bool" setter="set_glow_enabled" getter="is_glow_enabled" default="false"> <member name="glow_enabled" type="bool" setter="set_glow_enabled" getter="is_glow_enabled" default="false">
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 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. [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.
</member> </member>
@ -145,26 +146,26 @@
<member name="glow_hdr_threshold" type="float" setter="set_glow_hdr_bleed_threshold" getter="get_glow_hdr_bleed_threshold" default="1.0"> <member name="glow_hdr_threshold" type="float" setter="set_glow_hdr_bleed_threshold" getter="get_glow_hdr_bleed_threshold" default="1.0">
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 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.
</member> </member>
<member name="glow_intensity" type="float" setter="set_glow_intensity" getter="get_glow_intensity" default="0.8"> <member name="glow_intensity" type="float" setter="set_glow_intensity" getter="get_glow_intensity" default="0.3">
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 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.
</member> </member>
<member name="glow_levels/1" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0"> <member name="glow_levels/1" type="float" setter="set_glow_level" getter="get_glow_level" default="1.0">
The intensity of the 1st level of glow. This is the most "local" level (least blurry). 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. [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.
</member> </member>
<member name="glow_levels/2" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0"> <member name="glow_levels/2" type="float" setter="set_glow_level" getter="get_glow_level" default="0.8">
The intensity of the 2nd level of glow. 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. [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.
</member> </member>
<member name="glow_levels/3" type="float" setter="set_glow_level" getter="get_glow_level" default="1.0"> <member name="glow_levels/3" type="float" setter="set_glow_level" getter="get_glow_level" default="0.4">
The intensity of the 3rd level of glow. 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. [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.
</member> </member>
<member name="glow_levels/4" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0"> <member name="glow_levels/4" type="float" setter="set_glow_level" getter="get_glow_level" default="0.1">
The intensity of the 4th level of glow. 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. [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.
</member> </member>
<member name="glow_levels/5" type="float" setter="set_glow_level" getter="get_glow_level" default="1.0"> <member name="glow_levels/5" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 5th level of glow. 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. [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.
</member> </member>
@ -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. [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.
</constant> </constant>
<constant name="GLOW_BLEND_MODE_ADDITIVE" value="0" enum="GlowBlendMode"> <constant name="GLOW_BLEND_MODE_ADDITIVE" value="0" enum="GlowBlendMode">
Additive glow blending mode. Mostly used for particles, glows (bloom), lens flare, bright sources. Adds the glow effect to the scene.
</constant> </constant>
<constant name="GLOW_BLEND_MODE_SCREEN" value="1" enum="GlowBlendMode"> <constant name="GLOW_BLEND_MODE_SCREEN" value="1" enum="GlowBlendMode">
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.
</constant> </constant>
<constant name="GLOW_BLEND_MODE_SOFTLIGHT" value="2" enum="GlowBlendMode"> <constant name="GLOW_BLEND_MODE_SOFTLIGHT" value="2" enum="GlowBlendMode">
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].
</constant> </constant>
<constant name="GLOW_BLEND_MODE_REPLACE" value="3" enum="GlowBlendMode"> <constant name="GLOW_BLEND_MODE_REPLACE" value="3" enum="GlowBlendMode">
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.
</constant> </constant>
<constant name="GLOW_BLEND_MODE_MIX" value="4" enum="GlowBlendMode"> <constant name="GLOW_BLEND_MODE_MIX" value="4" enum="GlowBlendMode">
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.
</constant> </constant>
<constant name="FOG_MODE_EXPONENTIAL" value="0" enum="FogMode"> <constant name="FOG_MODE_EXPONENTIAL" value="0" enum="FogMode">
Use a physically-based fog model defined primarily by fog density. Use a physically-based fog model defined primarily by fog density.

View file

@ -87,7 +87,7 @@ void PostEffects::_draw_screen_triangle() {
glBindVertexArray(0); 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); glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
glDisable(GL_BLEND); 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::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::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); post.shader.version_set_uniform(PostShaderGLES3::VIEW, float(p_view), post.shader_version, mode, flags);

View file

@ -58,7 +58,7 @@ public:
PostEffects(); PostEffects();
~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 } //namespace GLES3

View file

@ -2814,6 +2814,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
float glow_hdr_bleed_threshold = 1.0; float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0; float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0; float glow_hdr_luminance_cap = 12.0;
float srgb_white = 1.0;
if (p_render_data->environment.is_valid()) { if (p_render_data->environment.is_valid()) {
glow_enabled = environment_get_glow_enabled(p_render_data->environment); glow_enabled = environment_get_glow_enabled(p_render_data->environment);
glow_intensity = environment_get_glow_intensity(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_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_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); 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) { 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(); rb->check_glow_buffers();
} }
@ -2890,7 +2895,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
} }
// Copy color buffer // 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 // Copy depth buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int);
@ -2958,7 +2963,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]); glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); 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 // Copy depth

View file

@ -45,6 +45,7 @@ uniform float luminance_multiplier;
uniform sampler2D glow_color; // texunit:1 uniform sampler2D glow_color; // texunit:1
uniform vec2 pixel_size; uniform vec2 pixel_size;
uniform float glow_intensity; uniform float glow_intensity;
uniform float srgb_white;
vec4 get_glow_color(vec2 uv) { vec4 get_glow_color(vec2 uv) {
vec2 half_pixel = pixel_size * 0.5; 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(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; 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; return color / 12.0;
} }
#endif // USE_GLOW #endif // USE_GLOW
@ -102,18 +107,38 @@ void main() {
vec4 color = texture(source_color, uv_interp); vec4 color = texture(source_color, uv_interp);
#endif #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 #ifdef USE_LUMINANCE_MULTIPLIER
color = color / luminance_multiplier; color = color / luminance_multiplier;
#endif #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 = srgb_to_linear(color.rgb);
color.rgb = apply_tonemapping(color.rgb, white); color.rgb = apply_tonemapping(color.rgb, white);
color.rgb = linear_to_srgb(color.rgb); color.rgb = linear_to_srgb(color.rgb);

View file

@ -1247,7 +1247,7 @@ void Environment::_bind_methods() {
ADD_GROUP("Tonemap", "tonemap_"); 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::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_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 // 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_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_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::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_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_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"); 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); set_camera_feed_id(bg_camera_feed_id);
glow_levels.resize(7); glow_levels.resize(7);
glow_levels.write[0] = 0.0; glow_levels.write[0] = 1.0;
glow_levels.write[1] = 0.0; glow_levels.write[1] = 0.8;
glow_levels.write[2] = 1.0; glow_levels.write[2] = 0.4;
glow_levels.write[3] = 0.0; glow_levels.write[3] = 0.1;
glow_levels.write[4] = 1.0; glow_levels.write[4] = 0.0;
glow_levels.write[5] = 0.0; glow_levels.write[5] = 0.0;
glow_levels.write[6] = 0.0; glow_levels.write[6] = 0.0;

View file

@ -163,11 +163,11 @@ private:
bool glow_enabled = false; bool glow_enabled = false;
Vector<float> glow_levels; Vector<float> glow_levels;
bool glow_normalize_levels = false; bool glow_normalize_levels = false;
float glow_intensity = 0.8; float glow_intensity = 0.3;
float glow_strength = 1.0; float glow_strength = 1.0;
float glow_mix = 0.05; float glow_mix = 0.05;
float glow_bloom = 0.0; 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_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0; float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0; float glow_hdr_luminance_cap = 12.0;

View file

@ -114,10 +114,10 @@ public:
GLOW_MODE_MIX GLOW_MODE_MIX
}; };
GlowMode glow_mode = GLOW_MODE_ADD; GlowMode glow_mode = GLOW_MODE_SCREEN;
float glow_intensity = 1.0; float glow_intensity = 0.3;
float glow_map_strength = 0.0f; 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; Vector2i glow_texture_size;
bool glow_use_bicubic_upscale = false; bool glow_use_bicubic_upscale = false;
RID glow_texture; RID glow_texture;

View file

@ -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_TEXTURE_SAMPLE(tex, uv, 6).rgb * params.glow_levels[6];
} }
glow = glow * params.luminance_multiplier;
return glow; 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_REPLACE 3
#define GLOW_MODE_MIX 4 #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) { if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow; return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) { } else if (params.glow_mode == GLOW_MODE_SCREEN) {
// Needs color clamping. // Glow cannot be above 1.0 after normalizing and should be non-negative
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f)); // to produce expected results. It is possible that glow can be negative
return max((color + glow) - (color * glow), vec3(0.0)); // if negative lights were used in the scene.
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) { // We clamp to white because glow will be normalized to this range.
// Needs color clamping. // Note: white cannot be smaller than the maximum output value.
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f)); glow.rgb = clamp(glow.rgb, 0.0, white);
glow = glow * vec3(0.5f) + vec3(0.5f);
// 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; return color;
} else { //replace } else { //replace
return glow; return glow;
@ -859,47 +882,56 @@ void main() {
color.rgb *= exposure; color.rgb *= exposure;
// Early Tonemap & SRGB Conversion // Single-pass FXAA and pre-tonemap glow.
#ifndef SUBPASS #ifndef SUBPASS
if (bool(params.flags & FLAG_USE_FXAA)) { if (bool(params.flags & FLAG_USE_FXAA)) {
// FXAA must be performed before glow to preserve the "bleed" effect of glow. // FXAA must be performed before glow to preserve the "bleed" effect of glow.
color.rgb = do_fxaa(color.rgb, exposure, uv_interp); color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
} }
if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_MIX) { if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_SOFTLIGHT) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier; vec3 glow = gather_glow(source_glow, uv_interp);
if (params.glow_map_strength > 0.001) { if (params.glow_mode == GLOW_MODE_MIX) {
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength); 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 #endif
// Tonemap to lower dynamic range.
color.rgb = apply_tonemapping(color.rgb, params.white); 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); bool convert_to_srgb = bool(params.flags & FLAG_CONVERT_TO_SRGB);
if (convert_to_srgb) { if (convert_to_srgb) {
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion. 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)) { if (bool(params.flags & FLAG_USE_BCS)) {
color.rgb = apply_bcs(color.rgb, params.bcs); color.rgb = apply_bcs(color.rgb, params.bcs);

View file

@ -208,7 +208,13 @@ void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::Environm
ERR_FAIL_NULL(env); ERR_FAIL_NULL(env);
env->exposure = p_exposure; env->exposure = p_exposure;
env->tone_mapper = p_tone_mapper; 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 { RS::EnvironmentToneMapper RendererEnvironmentStorage::environment_get_tone_mapper(RID p_env) const {
@ -471,7 +477,7 @@ Vector<float> RendererEnvironmentStorage::environment_get_glow_levels(RID p_env)
float RendererEnvironmentStorage::environment_get_glow_intensity(RID p_env) const { float RendererEnvironmentStorage::environment_get_glow_intensity(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env); 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; 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 { RS::EnvironmentGlowBlendMode RendererEnvironmentStorage::environment_get_glow_blend_mode(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env); 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; return env->glow_blend_mode;
} }

View file

@ -98,11 +98,11 @@ private:
// Glow // Glow
bool glow_enabled = false; bool glow_enabled = false;
Vector<float> glow_levels; Vector<float> glow_levels;
float glow_intensity = 0.8; float glow_intensity = 0.3;
float glow_strength = 1.0; float glow_strength = 1.0;
float glow_bloom = 0.0; float glow_bloom = 0.0;
float glow_mix = 0.01; 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_bleed_threshold = 1.0;
float glow_hdr_luminance_cap = 12.0; float glow_hdr_luminance_cap = 12.0;
float glow_hdr_bleed_scale = 2.0; float glow_hdr_bleed_scale = 2.0;