Overhaul screen space reflections.

This commit is contained in:
Skyth 2025-09-16 15:17:49 +03:00
parent f50d7fa1e8
commit c128886c63
31 changed files with 1185 additions and 934 deletions

View file

@ -298,7 +298,7 @@
<member name="ssil_sharpness" type="float" setter="set_ssil_sharpness" getter="get_ssil_sharpness" default="0.98"> <member name="ssil_sharpness" type="float" setter="set_ssil_sharpness" getter="get_ssil_sharpness" default="0.98">
The amount that the screen-space indirect lighting effect is allowed to blur over the edges of objects. Setting too high will result in aliasing around the edges of objects. Setting too low will make object edges appear blurry. The amount that the screen-space indirect lighting effect is allowed to blur over the edges of objects. Setting too high will result in aliasing around the edges of objects. Setting too low will make object edges appear blurry.
</member> </member>
<member name="ssr_depth_tolerance" type="float" setter="set_ssr_depth_tolerance" getter="get_ssr_depth_tolerance" default="0.2"> <member name="ssr_depth_tolerance" type="float" setter="set_ssr_depth_tolerance" getter="get_ssr_depth_tolerance" default="0.5">
The depth tolerance for screen-space reflections. The depth tolerance for screen-space reflections.
</member> </member>
<member name="ssr_enabled" type="bool" setter="set_ssr_enabled" getter="is_ssr_enabled" default="false"> <member name="ssr_enabled" type="bool" setter="set_ssr_enabled" getter="is_ssr_enabled" default="false">

View file

@ -2838,8 +2838,8 @@
<member name="rendering/environment/glow/upscale_mode.mobile" type="int" setter="" getter="" default="0"> <member name="rendering/environment/glow/upscale_mode.mobile" type="int" setter="" getter="" default="0">
Lower-end override for [member rendering/environment/glow/upscale_mode] on mobile devices, due to performance concerns or driver support. Lower-end override for [member rendering/environment/glow/upscale_mode] on mobile devices, due to performance concerns or driver support.
</member> </member>
<member name="rendering/environment/screen_space_reflection/roughness_quality" type="int" setter="" getter="" default="1"> <member name="rendering/environment/screen_space_reflection/half_size" type="bool" setter="" getter="" default="true">
Sets the quality for rough screen-space reflections. Turning off will make all screen space reflections sharp, while higher values make rough reflections look better. If [code]true[/code], screen-space reflections will be rendered at half size and then upscaled before being added to the scene. This is faster but may look pixelated or cause flickering. If [code]false[/code], screen-space reflections will be rendered at full size.
</member> </member>
<member name="rendering/environment/ssao/adaptive_target" type="float" setter="" getter="" default="0.5"> <member name="rendering/environment/ssao/adaptive_target" type="float" setter="" getter="" default="0.5">
Quality target to use when [member rendering/environment/ssao/quality] is set to [code]Ultra[/code]. A value of [code]0.0[/code] provides a quality and speed similar to [code]Medium[/code] while a value of [code]1.0[/code] provides much higher quality than any of the other settings at the cost of performance. Quality target to use when [member rendering/environment/ssao/quality] is set to [code]Ultra[/code]. A value of [code]0.0[/code] provides a quality and speed similar to [code]Medium[/code] while a value of [code]1.0[/code] provides much higher quality than any of the other settings at the cost of performance.

View file

@ -1500,7 +1500,14 @@
Sets the variables to be used with the screen-space reflections (SSR) post-process effect. See [Environment] for more details. Sets the variables to be used with the screen-space reflections (SSR) post-process effect. See [Environment] for more details.
</description> </description>
</method> </method>
<method name="environment_set_ssr_roughness_quality"> <method name="environment_set_ssr_half_size">
<return type="void" />
<param index="0" name="half_size" type="bool" />
<description>
Sets whether screen-space reflections will be rendered at full or half size. Half size is faster, but may look pixelated or cause flickering.
</description>
</method>
<method name="environment_set_ssr_roughness_quality" deprecated="This option no longer does anything.">
<return type="void" /> <return type="void" />
<param index="0" name="quality" type="int" enum="RenderingServer.EnvironmentSSRRoughnessQuality" /> <param index="0" name="quality" type="int" enum="RenderingServer.EnvironmentSSRRoughnessQuality" />
<description> <description>

View file

@ -1107,6 +1107,9 @@ void RasterizerSceneGLES3::environment_glow_set_use_bicubic_upscale(bool p_enabl
glow_bicubic_upscale = p_enable; glow_bicubic_upscale = p_enable;
} }
void RasterizerSceneGLES3::environment_set_ssr_half_size(bool p_half_size) {
}
void RasterizerSceneGLES3::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) { void RasterizerSceneGLES3::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) {
} }

View file

@ -757,7 +757,6 @@ protected:
float ssao_fadeout_to = 300.0; float ssao_fadeout_to = 300.0;
bool glow_bicubic_upscale = false; bool glow_bicubic_upscale = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;
bool lightmap_bicubic_upscale = false; bool lightmap_bicubic_upscale = false;
@ -878,6 +877,7 @@ public:
void environment_glow_set_use_bicubic_upscale(bool p_enable) override; void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
void environment_set_ssr_half_size(bool p_half_size) override;
void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override; void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override;
void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;

View file

@ -472,8 +472,7 @@ void EditorNode::_update_from_settings() {
bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0; bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0;
RS::get_singleton()->environment_set_ssil_quality(RS::EnvironmentSSILQuality(int(GLOBAL_GET("rendering/environment/ssil/quality"))), GLOBAL_GET("rendering/environment/ssil/half_size"), GLOBAL_GET("rendering/environment/ssil/adaptive_target"), GLOBAL_GET("rendering/environment/ssil/blur_passes"), GLOBAL_GET("rendering/environment/ssil/fadeout_from"), GLOBAL_GET("rendering/environment/ssil/fadeout_to")); RS::get_singleton()->environment_set_ssil_quality(RS::EnvironmentSSILQuality(int(GLOBAL_GET("rendering/environment/ssil/quality"))), GLOBAL_GET("rendering/environment/ssil/half_size"), GLOBAL_GET("rendering/environment/ssil/adaptive_target"), GLOBAL_GET("rendering/environment/ssil/blur_passes"), GLOBAL_GET("rendering/environment/ssil/fadeout_from"), GLOBAL_GET("rendering/environment/ssil/fadeout_to"));
RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic); RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic);
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/environment/screen_space_reflection/roughness_quality"))); RS::get_singleton()->environment_set_ssr_half_size(GLOBAL_GET("rendering/environment/screen_space_reflection/half_size"));
RS::get_singleton()->environment_set_ssr_roughness_quality(ssr_roughness_quality);
RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_quality"))); RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_quality")));
RS::get_singleton()->sub_surface_scattering_set_quality(sss_quality); RS::get_singleton()->sub_surface_scattering_set_quality(sss_quality);
float sss_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_scale"); float sss_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_scale");

View file

@ -1264,7 +1264,7 @@ void Environment::_bind_methods() {
ADD_GROUP("SSR", "ssr_"); ADD_GROUP("SSR", "ssr_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssr_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_ssr_enabled", "is_ssr_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssr_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_ssr_enabled", "is_ssr_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ssr_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps"); ADD_PROPERTY(PropertyInfo(Variant::INT, "ssr_max_steps", PROPERTY_HINT_RANGE, "32,512,1"), "set_ssr_max_steps", "get_ssr_max_steps");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_in", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssr_fade_in", "get_ssr_fade_in"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_in", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssr_fade_in", "get_ssr_fade_in");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_out", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssr_fade_out", "get_ssr_fade_out"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_out", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssr_fade_out", "get_ssr_fade_out");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance");

View file

@ -122,7 +122,7 @@ private:
int ssr_max_steps = 64; int ssr_max_steps = 64;
float ssr_fade_in = 0.15; float ssr_fade_in = 0.15;
float ssr_fade_out = 2.0; float ssr_fade_out = 2.0;
float ssr_depth_tolerance = 0.2; float ssr_depth_tolerance = 0.5;
void _update_ssr(); void _update_ssr();
// SSAO // SSAO

View file

@ -120,6 +120,7 @@ public:
void environment_glow_set_use_bicubic_upscale(bool p_enable) override {} void environment_glow_set_use_bicubic_upscale(bool p_enable) override {}
void environment_set_ssr_half_size(bool p_half_size) override {}
void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override {} void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override {}
void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {}

View file

@ -31,6 +31,7 @@
#include "ss_effects.h" #include "ss_effects.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" #include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
@ -277,62 +278,67 @@ SSEffects::SSEffects() {
} }
// Screen Space Reflections // Screen Space Reflections
ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/environment/screen_space_reflection/roughness_quality"))); ssr_half_size = GLOBAL_GET("rendering/environment/screen_space_reflection/half_size");
{ {
Vector<RD::PipelineSpecializationConstant> specialization_constants;
{ {
RD::PipelineSpecializationConstant sc; Vector<String> ssr_downsample_modes;
sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; ssr_downsample_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_DEFAULT
sc.constant_id = 0; // SSR_USE_FULL_PROJECTION_MATRIX ssr_downsample_modes.push_back("\n#define MODE_ODD_WIDTH\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH
sc.bool_value = false; ssr_downsample_modes.push_back("\n#define MODE_ODD_HEIGHT\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_HEIGHT
specialization_constants.push_back(sc); ssr_downsample_modes.push_back("\n#define MODE_ODD_WIDTH\n#define MODE_ODD_HEIGHT\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH_AND_HEIGHT
ssr.downsample_shader.initialize(ssr_downsample_modes);
ssr.downsample_shader_version = ssr.downsample_shader.version_create();
for (uint32_t i = 0; i < SCREEN_SPACE_REFLECTION_DOWNSAMPLE_MAX; i++) {
ssr.downsample_pipelines[i].create_compute_pipeline(ssr.downsample_shader.version_get_shader(ssr.downsample_shader_version, i));
}
} }
{ {
Vector<String> ssr_scale_modes; Vector<String> ssr_hiz_modes;
ssr_scale_modes.push_back("\n"); ssr_hiz_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_HIZ_DEFAULT
ssr_hiz_modes.push_back("\n#define MODE_ODD_WIDTH\n"); // SCREEN_SPACE_REFLECTION_HIZ_ODD_WIDTH
ssr_hiz_modes.push_back("\n#define MODE_ODD_HEIGHT\n"); // SCREEN_SPACE_REFLECTION_HIZ_ODD_HEIGHT
ssr_hiz_modes.push_back("\n#define MODE_ODD_WIDTH\n#define MODE_ODD_HEIGHT\n"); // SCREEN_SPACE_REFLECTION_HIZ_ODD_WIDTH_AND_HEIGHT
ssr_scale.shader.initialize(ssr_scale_modes); ssr.hiz_shader.initialize(ssr_hiz_modes);
ssr_scale.shader_version = ssr_scale.shader.version_create(); ssr.hiz_shader_version = ssr.hiz_shader.version_create();
for (int v = 0; v < SSR_VARIATIONS; v++) { for (uint32_t i = 0; i < SCREEN_SPACE_REFLECTION_HIZ_MAX; i++) {
specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false; ssr.hiz_pipelines[i].create_compute_pipeline(ssr.hiz_shader.version_get_shader(ssr.hiz_shader_version, i));
ssr_scale.pipelines[v].create_compute_pipeline(ssr_scale.shader.version_get_shader(ssr_scale.shader_version, 0), specialization_constants);
} }
} }
{ {
Vector<String> ssr_modes; Vector<String> ssr_modes;
ssr_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_NORMAL ssr_modes.push_back("\n");
ssr_modes.push_back("\n#define MODE_ROUGH\n"); // SCREEN_SPACE_REFLECTION_ROUGH
ssr.shader.initialize(ssr_modes); ssr.ssr_shader.initialize(ssr_modes);
ssr.shader_version = ssr.shader.version_create(); ssr.ssr_shader_version = ssr.ssr_shader.version_create();
for (int v = 0; v < SSR_VARIATIONS; v++) { ssr.ssr_pipeline.create_compute_pipeline(ssr.ssr_shader.version_get_shader(ssr.ssr_shader_version, 0));
specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false;
for (int i = 0; i < SCREEN_SPACE_REFLECTION_MAX; i++) {
ssr.pipelines[v][i].create_compute_pipeline(ssr.shader.version_get_shader(ssr.shader_version, i), specialization_constants);
}
}
} }
{ {
Vector<String> ssr_filter_modes; Vector<String> ssr_filter_modes;
ssr_filter_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL ssr_filter_modes.push_back("\n");
ssr_filter_modes.push_back("\n#define VERTICAL_PASS\n"); // SCREEN_SPACE_REFLECTION_FILTER_VERTICAL
ssr_filter.shader.initialize(ssr_filter_modes); ssr.filter_shader.initialize(ssr_filter_modes);
ssr_filter.shader_version = ssr_filter.shader.version_create(); ssr.filter_shader_version = ssr.filter_shader.version_create();
for (int v = 0; v < SSR_VARIATIONS; v++) { ssr.filter_pipeline.create_compute_pipeline(ssr.filter_shader.version_get_shader(ssr.filter_shader_version, 0));
specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false; }
for (int i = 0; i < SCREEN_SPACE_REFLECTION_FILTER_MAX; i++) {
ssr_filter.pipelines[v][i].create_compute_pipeline(ssr_filter.shader.version_get_shader(ssr_filter.shader_version, i), specialization_constants); {
} Vector<String> ssr_resolve_modes;
} ssr_resolve_modes.push_back("\n");
ssr.resolve_shader.initialize(ssr_resolve_modes);
ssr.resolve_shader_version = ssr.resolve_shader.version_create();
ssr.resolve_pipeline.create_compute_pipeline(ssr.resolve_shader.version_get_shader(ssr.resolve_shader_version, 0));
} }
} }
@ -357,21 +363,81 @@ SSEffects::SSEffects() {
} }
} }
void SSEffects::allocate_last_frame_buffer(Ref<RenderSceneBuffersRD> p_render_buffers, bool p_use_ssil, bool p_use_ssr) {
Size2i last_frame_size = p_render_buffers->get_internal_size();
uint32_t mipmaps = 1;
uint32_t view_count = p_render_buffers->get_view_count();
if (!p_use_ssil && p_use_ssr && ssr_half_size) {
last_frame_size /= 2;
}
if (p_use_ssil) {
mipmaps = 6;
}
bool should_create = true;
bool has_texture = p_render_buffers->has_texture(RB_SCOPE_SSLF, RB_LAST_FRAME);
if (has_texture) {
RID last_frame_texture = p_render_buffers->get_texture(RB_SCOPE_SSLF, RB_LAST_FRAME);
RD::TextureFormat texture_format = RD::get_singleton()->texture_get_format(last_frame_texture);
should_create = texture_format.width != (uint32_t)last_frame_size.width || texture_format.height != (uint32_t)last_frame_size.height || texture_format.mipmaps != mipmaps || texture_format.array_layers != view_count;
}
if (should_create) {
if (has_texture) {
p_render_buffers->clear_context(RB_SCOPE_SSLF);
}
RID last_frame_texture = p_render_buffers->create_texture(RB_SCOPE_SSLF, RB_LAST_FRAME, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT, RD::TEXTURE_SAMPLES_1, last_frame_size, view_count, mipmaps);
RD::get_singleton()->texture_clear(last_frame_texture, Color(0, 0, 0, 0), 0, mipmaps, 0, view_count);
}
}
void SSEffects::copy_internal_texture_to_last_frame(Ref<RenderSceneBuffersRD> p_render_buffers, CopyEffects &p_copy_effects) {
uint32_t mipmaps = p_render_buffers->get_texture_format(RB_SCOPE_SSLF, RB_LAST_FRAME).mipmaps;
for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) {
for (uint32_t m = 0; m < mipmaps; m++) {
RID source;
if (m == 0) {
source = p_render_buffers->get_internal_texture(v);
} else {
source = p_render_buffers->get_texture_slice(RB_SCOPE_SSLF, RB_LAST_FRAME, v, m - 1);
}
RID dest = p_render_buffers->get_texture_slice(RB_SCOPE_SSLF, RB_LAST_FRAME, v, m);
Size2i source_size = RD::get_singleton()->texture_size(source);
Size2i dest_size = RD::get_singleton()->texture_size(dest);
if (m == 0 && source_size == dest_size) {
p_copy_effects.copy_to_rect(source, dest, Rect2i(Vector2i(), source_size));
} else {
p_copy_effects.make_mipmap(source, dest, dest_size);
}
}
}
}
SSEffects::~SSEffects() { SSEffects::~SSEffects() {
{ {
// Cleanup SS Reflections // Cleanup SS Reflections
for (int v = 0; v < SSR_VARIATIONS; v++) { for (int i = 0; i < SCREEN_SPACE_REFLECTION_DOWNSAMPLE_MAX; i++) {
for (int i = 0; i < SCREEN_SPACE_REFLECTION_FILTER_MAX; i++) { ssr.downsample_pipelines[i].free();
ssr.pipelines[v][i].free();
ssr_filter.pipelines[v][i].free();
}
ssr_scale.pipelines[v].free();
} }
for (int i = 0; i < SCREEN_SPACE_REFLECTION_HIZ_MAX; i++) {
ssr.hiz_pipelines[i].free();
}
ssr.ssr_pipeline.free();
ssr.filter_pipeline.free();
ssr.resolve_pipeline.free();
ssr.shader.version_free(ssr.shader_version); ssr.downsample_shader.version_free(ssr.downsample_shader_version);
ssr_filter.shader.version_free(ssr_filter.shader_version); ssr.hiz_shader.version_free(ssr.hiz_shader_version);
ssr_scale.shader.version_free(ssr_scale.shader_version); ssr.ssr_shader.version_free(ssr.ssr_shader_version);
ssr.filter_shader.version_free(ssr.filter_shader_version);
ssr.resolve_shader.version_free(ssr.resolve_shader_version);
if (ssr.ubo.is_valid()) { if (ssr.ubo.is_valid()) {
RD::get_singleton()->free_rid(ssr.ubo); RD::get_singleton()->free_rid(ssr.ubo);
@ -644,11 +710,6 @@ void SSEffects::ssil_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers
RD::get_singleton()->texture_clear(final, Color(0, 0, 0, 0), 0, 1, 0, view_count); RD::get_singleton()->texture_clear(final, Color(0, 0, 0, 0), 0, 1, 0, view_count);
} }
if (!p_render_buffers->has_texture(RB_SCOPE_SSIL, RB_LAST_FRAME)) {
RID last_frame = p_render_buffers->create_texture(RB_SCOPE_SSIL, RB_LAST_FRAME, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT, RD::TEXTURE_SAMPLES_1, p_settings.full_screen_size, 0, 6);
RD::get_singleton()->texture_clear(last_frame, Color(0, 0, 0, 0), 0, 6, 0, view_count);
}
// As we're not clearing these, and render buffers will return the cached texture if it already exists, // As we're not clearing these, and render buffers will return the cached texture if it already exists,
// we don't first check has_texture here // we don't first check has_texture here
@ -668,7 +729,7 @@ void SSEffects::screen_space_indirect_lighting(Ref<RenderSceneBuffersRD> p_rende
RD::get_singleton()->draw_command_begin_label("Process Screen-Space Indirect Lighting"); RD::get_singleton()->draw_command_begin_label("Process Screen-Space Indirect Lighting");
// Obtain our (cached) buffer slices for the view we are rendering. // Obtain our (cached) buffer slices for the view we are rendering.
RID last_frame = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_LAST_FRAME, p_view, 0, 1, 6); RID last_frame = p_render_buffers->get_texture_slice(RB_SCOPE_SSLF, RB_LAST_FRAME, p_view, 0, 1, 6);
RID deinterleaved = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_DEINTERLEAVED, p_view * 4, 0, 4, 1); RID deinterleaved = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_DEINTERLEAVED, p_view * 4, 0, 4, 1);
RID deinterleaved_pong = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_DEINTERLEAVED_PONG, 4 * p_view, 0, 4, 1); RID deinterleaved_pong = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_DEINTERLEAVED_PONG, 4 * p_view, 0, 4, 1);
RID edges = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_EDGES, 4 * p_view, 0, 4, 1); RID edges = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_EDGES, 4 * p_view, 0, 4, 1);
@ -1364,43 +1425,56 @@ void SSEffects::generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORe
/* Screen Space Reflection */ /* Screen Space Reflection */
void SSEffects::ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) { void SSEffects::ssr_set_half_size(bool p_half_size) {
ssr_roughness_quality = p_quality; ssr_half_size = p_half_size;
} }
void SSEffects::ssr_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format) { void SSEffects::ssr_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RD::DataFormat p_color_format) {
if (p_ssr_buffers.roughness_quality != ssr_roughness_quality) { if (p_ssr_buffers.half_size != ssr_half_size) {
// Buffers will already be cleared if view count or viewport size has changed, also cleared them if we change roughness.
p_render_buffers->clear_context(RB_SCOPE_SSR); p_render_buffers->clear_context(RB_SCOPE_SSR);
} }
Size2i internal_size = p_render_buffers->get_internal_size(); Vector2i internal_size = p_render_buffers->get_internal_size();
p_ssr_buffers.size = Size2i(internal_size.x / 2, internal_size.y / 2); p_ssr_buffers.size = ssr_half_size ? (internal_size / 2) : internal_size;
p_ssr_buffers.roughness_quality = ssr_roughness_quality;
// We are using barriers so we do not need to allocate textures for both views on anything but output... uint32_t cur_width = p_ssr_buffers.size.width;
uint32_t cur_height = p_ssr_buffers.size.height;
p_ssr_buffers.mipmaps = 1;
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_DEPTH_SCALED, RD::DATA_FORMAT_R32_SFLOAT, RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, 1); while (cur_width > 1 && cur_height > 1) {
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_NORMAL_SCALED, RD::DATA_FORMAT_R8G8B8A8_UNORM, RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, 1); if (cur_width > 1) {
cur_width /= 2;
if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED && !p_render_buffers->has_texture(RB_SCOPE_SSR, RB_BLUR_RADIUS)) { }
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_BLUR_RADIUS, RD::DATA_FORMAT_R8_UNORM, RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, 2); // 2 layers, for our two blur stages if (cur_height > 1) {
cur_height /= 2;
}
++p_ssr_buffers.mipmaps;
} }
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_INTERMEDIATE, p_color_format, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, 1); p_ssr_buffers.half_size = ssr_half_size;
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_OUTPUT, p_color_format, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size);
uint32_t view_count = p_render_buffers->get_view_count();
if (ssr_half_size) {
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_NORMAL_ROUGHNESS, RD::DATA_FORMAT_R8G8B8A8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, view_count);
}
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_HIZ, RD::DATA_FORMAT_R32_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, view_count, p_ssr_buffers.mipmaps);
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_SSR, p_color_format, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, view_count, p_ssr_buffers.mipmaps);
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_MIP_LEVEL, RD::DATA_FORMAT_R8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, p_ssr_buffers.size, view_count);
if (ssr_half_size) {
p_render_buffers->create_texture(RB_SCOPE_SSR, RB_FINAL, p_color_format, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT, RD::TEXTURE_SAMPLES_1, internal_size, view_count);
}
} }
void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RID *p_normal_roughness_slices, const RID *p_metallic_slices, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const Projection *p_projections, const Vector3 *p_eye_offsets) { void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RID *p_normal_roughness_slices, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const Projection *p_projections, const Projection *p_reprojections, const Vector3 *p_eye_offsets, RendererRD::CopyEffects &p_copy_effects) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache); ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton(); MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage); ERR_FAIL_NULL(material_storage);
uint32_t view_count = p_render_buffers->get_view_count(); uint32_t view_count = p_render_buffers->get_view_count();
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
{ {
// Store some scene data in a UBO, in the near future we will use a UBO shared with other shaders // Store some scene data in a UBO, in the near future we will use a UBO shared with other shaders
ScreenSpaceReflectionSceneData scene_data; ScreenSpaceReflectionSceneData scene_data;
@ -1409,227 +1483,252 @@ void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffe
ssr.ubo = RD::get_singleton()->uniform_buffer_create(sizeof(ScreenSpaceReflectionSceneData)); ssr.ubo = RD::get_singleton()->uniform_buffer_create(sizeof(ScreenSpaceReflectionSceneData));
} }
Projection correction;
correction.set_depth_correction(true);
for (uint32_t v = 0; v < view_count; v++) { for (uint32_t v = 0; v < view_count; v++) {
store_camera(p_projections[v], scene_data.projection[v]); Projection projection = correction * p_projections[v];
store_camera(p_projections[v].inverse(), scene_data.inv_projection[v]);
store_camera(projection, scene_data.projection[v]);
store_camera(projection.inverse(), scene_data.inv_projection[v]);
store_camera(p_reprojections[v], scene_data.reprojection[v]);
scene_data.eye_offset[v][0] = p_eye_offsets[v].x; scene_data.eye_offset[v][0] = p_eye_offsets[v].x;
scene_data.eye_offset[v][1] = p_eye_offsets[v].y; scene_data.eye_offset[v][1] = p_eye_offsets[v].y;
scene_data.eye_offset[v][2] = p_eye_offsets[v].z; scene_data.eye_offset[v][2] = p_eye_offsets[v].z;
scene_data.eye_offset[v][3] = 0.0; scene_data.eye_offset[v][3] = 0.0f;
} }
RD::get_singleton()->buffer_update(ssr.ubo, 0, sizeof(ScreenSpaceReflectionSceneData), &scene_data); RD::get_singleton()->buffer_update(ssr.ubo, 0, sizeof(ScreenSpaceReflectionSceneData), &scene_data);
} }
uint32_t pipeline_specialization = 0; RID linear_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
if (view_count > 1) { RID nearest_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
pipeline_specialization |= SSR_MULTIVIEW;
}
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); if (ssr_half_size) {
RD::get_singleton()->draw_command_begin_label("SSR Downsample");
for (uint32_t v = 0; v < view_count; v++) {
// get buffers we need to use for this view
RID diffuse_slice = p_render_buffers->get_internal_texture(v);
RID depth_slice = p_render_buffers->get_depth_texture(v);
RID depth_scaled = p_render_buffers->get_texture(RB_SCOPE_SSR, RB_DEPTH_SCALED);
RID normal_scaled = p_render_buffers->get_texture(RB_SCOPE_SSR, RB_NORMAL_SCALED);
RID intermediate = p_render_buffers->get_texture(RB_SCOPE_SSR, RB_INTERMEDIATE);
RID output = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_OUTPUT, v, 0);
RID blur_radius[2];
if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
blur_radius[0] = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_BLUR_RADIUS, 0, 0);
blur_radius[1] = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_BLUR_RADIUS, 1, 0);
}
{
char label[16];
int len = snprintf(label, sizeof(label), "SSR View %d", v);
RD::get_singleton()->draw_command_begin_label(Span<char>(label, len));
}
{ //scale color and depth to half
RD::get_singleton()->draw_command_begin_label("SSR Scale");
ScreenSpaceReflectionScalePushConstant push_constant;
push_constant.view_index = v;
push_constant.camera_z_far = p_projections[v].get_z_far();
push_constant.camera_z_near = p_projections[v].get_z_near();
push_constant.orthogonal = p_projections[v].is_orthogonal();
push_constant.filter = false; // Enabling causes artifacts.
push_constant.screen_size[0] = p_ssr_buffers.size.x;
push_constant.screen_size[1] = p_ssr_buffers.size.y;
RID shader = ssr_scale.shader.version_get_shader(ssr_scale.shader_version, 0);
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_scale.pipelines[pipeline_specialization].get_rid());
RD::Uniform u_diffuse(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, diffuse_slice }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_diffuse), 0);
RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, depth_slice }));
RD::Uniform u_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_normal_roughness_slices[v] }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth, u_normal_roughness), 1);
RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ intermediate }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_intermediate), 2);
RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ depth_scaled }));
RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ normal_scaled }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_scale_depth, u_scale_normal), 3);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionScalePushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssr_buffers.size.width, p_ssr_buffers.size.height, 1);
RD::get_singleton()->compute_list_add_barrier(compute_list);
RD::get_singleton()->draw_command_end_label();
}
{
RD::get_singleton()->draw_command_begin_label("SSR Main");
ScreenSpaceReflectionPushConstant push_constant;
push_constant.view_index = v;
push_constant.camera_z_far = p_projections[v].get_z_far();
push_constant.camera_z_near = p_projections[v].get_z_near();
push_constant.orthogonal = p_projections[v].is_orthogonal();
push_constant.screen_size[0] = p_ssr_buffers.size.x;
push_constant.screen_size[1] = p_ssr_buffers.size.y;
push_constant.curve_fade_in = p_fade_in;
push_constant.distance_fade = p_fade_out;
push_constant.num_steps = p_max_steps;
push_constant.depth_tolerance = p_tolerance;
push_constant.use_half_res = true;
push_constant.proj_info[0] = -2.0f / (p_ssr_buffers.size.width * p_projections[v].columns[0][0]);
push_constant.proj_info[1] = -2.0f / (p_ssr_buffers.size.height * p_projections[v].columns[1][1]);
push_constant.proj_info[2] = (1.0f - p_projections[v].columns[0][2]) / p_projections[v].columns[0][0];
push_constant.proj_info[3] = (1.0f + p_projections[v].columns[1][2]) / p_projections[v].columns[1][1];
ScreenSpaceReflectionMode mode = (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) ? SCREEN_SPACE_REFLECTION_ROUGH : SCREEN_SPACE_REFLECTION_NORMAL;
RID shader = ssr.shader.version_get_shader(ssr.shader_version, mode);
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.pipelines[pipeline_specialization][mode].get_rid());
RD::Uniform u_scene_data(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 0, ssr.ubo);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
// read from intermediate
RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ intermediate }));
RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ depth_scaled }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate, u_scale_depth), 0);
if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
// write to output and blur radius
RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ output }));
RD::Uniform u_blur_radius(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ blur_radius[0] }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_output, u_blur_radius), 1);
} else {
// We are only writing output
RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ output }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_output), 1);
}
RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ normal_scaled }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_scale_normal), 2);
RD::Uniform u_metallic(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_metallic_slices[v] }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_metallic), 3);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionPushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssr_buffers.size.width, p_ssr_buffers.size.height, 1);
RD::get_singleton()->draw_command_end_label();
}
if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
RD::get_singleton()->draw_command_begin_label("SSR Roughness Filter");
//blur
RD::get_singleton()->compute_list_add_barrier(compute_list);
ScreenSpaceReflectionFilterPushConstant push_constant;
push_constant.view_index = v;
push_constant.orthogonal = p_projections[v].is_orthogonal();
push_constant.edge_tolerance = Math::sin(Math::deg_to_rad(15.0));
push_constant.proj_info[0] = -2.0f / (p_ssr_buffers.size.width * p_projections[v].columns[0][0]);
push_constant.proj_info[1] = -2.0f / (p_ssr_buffers.size.height * p_projections[v].columns[1][1]);
push_constant.proj_info[2] = (1.0f - p_projections[v].columns[0][2]) / p_projections[v].columns[0][0];
push_constant.proj_info[3] = (1.0f + p_projections[v].columns[1][2]) / p_projections[v].columns[1][1];
push_constant.vertical = 0;
if (ssr_roughness_quality == RS::ENV_SSR_ROUGHNESS_QUALITY_LOW) {
push_constant.steps = p_max_steps / 3;
push_constant.increment = 3;
} else if (ssr_roughness_quality == RS::ENV_SSR_ROUGHNESS_QUALITY_MEDIUM) {
push_constant.steps = p_max_steps / 2;
push_constant.increment = 2;
} else {
push_constant.steps = p_max_steps;
push_constant.increment = 1;
}
for (uint32_t v = 0; v < view_count; v++) {
ScreenSpaceReflectionDownsamplePushConstant push_constant;
push_constant.screen_size[0] = p_ssr_buffers.size.width; push_constant.screen_size[0] = p_ssr_buffers.size.width;
push_constant.screen_size[1] = p_ssr_buffers.size.height; push_constant.screen_size[1] = p_ssr_buffers.size.height;
// Horizontal pass RID source_depth_texture = p_render_buffers->get_depth_texture(v);
RID dest_depth_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_HIZ, v, 0);
RID dest_normal_roughness_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_NORMAL_ROUGHNESS, v, 0);
SSRReflectionMode mode = SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL; Size2i parent_size = RD::get_singleton()->texture_size(source_depth_texture);
bool is_width_odd = (parent_size.width % 2) != 0;
bool is_height_odd = (parent_size.height % 2) != 0;
RID shader = ssr_filter.shader.version_get_shader(ssr_filter.shader_version, mode); int32_t downsample_mode;
if (is_width_odd && is_height_odd) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_filter.pipelines[pipeline_specialization][mode].get_rid()); downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH_AND_HEIGHT;
} else if (is_width_odd) {
RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ output })); downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH;
RD::Uniform u_blur_radius(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ blur_radius[0] })); } else if (is_height_odd) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_output, u_blur_radius), 0); downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_HEIGHT;
} else {
RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ normal_scaled })); downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_DEFAULT;
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_scale_normal), 1);
RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ intermediate }));
RD::Uniform u_blur_radius2(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ blur_radius[1] }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_intermediate, u_blur_radius2), 2);
RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ depth_scaled }));
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_scale_depth), 3);
RD::Uniform u_scene_data(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 0, ssr.ubo);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionFilterPushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssr_buffers.size.width, p_ssr_buffers.size.height, 1);
RD::get_singleton()->compute_list_add_barrier(compute_list);
// Vertical pass
mode = SCREEN_SPACE_REFLECTION_FILTER_VERTICAL;
shader = ssr_filter.shader.version_get_shader(ssr_filter.shader_version, mode);
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_filter.pipelines[pipeline_specialization][mode].get_rid());
push_constant.vertical = 1;
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate, u_blur_radius2), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_scale_normal), 1);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_output), 2);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_scale_depth), 3);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionFilterPushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssr_buffers.size.width, p_ssr_buffers.size.height, 1);
if (v != view_count - 1) {
RD::get_singleton()->compute_list_add_barrier(compute_list);
} }
RD::get_singleton()->draw_command_end_label(); RID downsample_shader = ssr.downsample_shader.version_get_shader(ssr.downsample_shader_version, downsample_mode);
RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>{ nearest_sampler, source_depth_texture });
RD::Uniform u_source_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>{ nearest_sampler, p_normal_roughness_slices[v] });
RD::Uniform u_dest_depth(RD::UNIFORM_TYPE_IMAGE, 2, dest_depth_texture);
RD::Uniform u_dest_normal_roughness(RD::UNIFORM_TYPE_IMAGE, 3, dest_normal_roughness_texture);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.downsample_pipelines[downsample_mode].get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(downsample_shader, 0, u_source_depth, u_source_normal_roughness, u_dest_depth, u_dest_normal_roughness), 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(push_constant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.screen_size[0], push_constant.screen_size[1], 1);
RD::get_singleton()->compute_list_end();
}
RD::get_singleton()->draw_command_end_label();
} else {
RD::get_singleton()->draw_command_begin_label("SSR Copy Depth");
for (uint32_t v = 0; v < view_count; v++) {
RID src_texture = p_render_buffers->get_depth_texture(v);
RID dest_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_HIZ, v, 0);
p_copy_effects.copy_depth_to_rect(src_texture, dest_texture, Rect2i(Vector2i(), p_ssr_buffers.size));
} }
RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_command_end_label();
} }
RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_begin_label("SSR HI-Z");
for (uint32_t v = 0; v < view_count; v++) {
for (uint32_t m = 1; m < p_ssr_buffers.mipmaps; m++) {
ScreenSpaceReflectionHizPushConstant push_constant;
push_constant.screen_size[0] = MAX(1, p_ssr_buffers.size.width >> m);
push_constant.screen_size[1] = MAX(1, p_ssr_buffers.size.height >> m);
RID source;
if (!ssr_half_size && m == 1) { // Reuse the depth texture to not create a dependency on the previous depth copy pass.
source = p_render_buffers->get_depth_texture(v);
} else {
source = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_HIZ, v, m - 1);
}
RID dest = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_HIZ, v, m);
Size2i parent_size = RD::get_singleton()->texture_size(source);
bool is_width_odd = (parent_size.width % 2) != 0;
bool is_height_odd = (parent_size.height % 2) != 0;
int32_t hiz_mode;
if (is_width_odd && is_height_odd) {
hiz_mode = SCREEN_SPACE_REFLECTION_HIZ_ODD_WIDTH_AND_HEIGHT;
} else if (is_width_odd) {
hiz_mode = SCREEN_SPACE_REFLECTION_HIZ_ODD_WIDTH;
} else if (is_height_odd) {
hiz_mode = SCREEN_SPACE_REFLECTION_HIZ_ODD_HEIGHT;
} else {
hiz_mode = SCREEN_SPACE_REFLECTION_HIZ_DEFAULT;
}
RID hiz_shader = ssr.hiz_shader.version_get_shader(ssr.hiz_shader_version, hiz_mode);
RD::Uniform u_source(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>{ nearest_sampler, source });
RD::Uniform u_dest(RD::UNIFORM_TYPE_IMAGE, 1, dest);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.hiz_pipelines[hiz_mode].get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(hiz_shader, 0, u_source, u_dest), 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(push_constant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.screen_size[0], push_constant.screen_size[1], 1);
RD::get_singleton()->compute_list_end();
}
}
RD::get_singleton()->draw_command_end_label();
RD::get_singleton()->draw_command_begin_label("SSR Main");
RID ssr_shader = ssr.ssr_shader.version_get_shader(ssr.ssr_shader_version, 0);
for (uint32_t v = 0; v < view_count; v++) {
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.ssr_pipeline.get_rid());
ScreenSpaceReflectionPushConstant push_constant;
push_constant.screen_size[0] = p_ssr_buffers.size.width;
push_constant.screen_size[1] = p_ssr_buffers.size.height;
push_constant.mipmaps = p_ssr_buffers.mipmaps;
push_constant.num_steps = p_max_steps;
push_constant.curve_fade_in = p_fade_in;
push_constant.distance_fade = p_fade_out;
push_constant.depth_tolerance = p_tolerance;
push_constant.orthogonal = p_projections[v].is_orthogonal();
push_constant.view_index = v;
RID last_frame_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSLF, RB_LAST_FRAME, v, 0);
if (ssr_half_size && RD::get_singleton()->texture_size(last_frame_texture) != p_ssr_buffers.size) {
// SSIL is likely also enabled. The texture we need is in the second mipmap in this case.
last_frame_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSLF, RB_LAST_FRAME, v, 1);
}
RID hiz_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_HIZ, v, 0, 1, p_ssr_buffers.mipmaps);
RID normal_roughness_texture = ssr_half_size ? p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_NORMAL_ROUGHNESS, v, 0) : p_normal_roughness_slices[v];
RID ssr_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_SSR, v, 0);
RID mip_level_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_MIP_LEVEL, v, 0);
RD::Uniform u_last_frame(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>{ linear_sampler, last_frame_texture });
RD::Uniform u_hiz(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>{ nearest_sampler, hiz_texture });
RD::Uniform u_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 2, Vector<RID>{ nearest_sampler, normal_roughness_texture });
RD::Uniform u_ssr(RD::UNIFORM_TYPE_IMAGE, 3, ssr_texture);
RD::Uniform u_mip_level(RD::UNIFORM_TYPE_IMAGE, 4, mip_level_texture);
RD::Uniform u_scene_data(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 5, ssr.ubo);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(ssr_shader, 0, u_last_frame, u_hiz, u_normal_roughness, u_ssr, u_mip_level, u_scene_data), 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(push_constant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssr_buffers.size.width, p_ssr_buffers.size.height, 1);
RD::get_singleton()->compute_list_end();
}
RD::get_singleton()->draw_command_end_label();
RD::get_singleton()->draw_command_begin_label("SSR Roughness Filter");
RID filter_shader = ssr.filter_shader.version_get_shader(ssr.filter_shader_version, 0);
for (uint32_t v = 0; v < view_count; v++) {
for (uint32_t m = 1; m < p_ssr_buffers.mipmaps; m++) {
ScreenSpaceReflectionFilterPushConstant push_constant;
push_constant.screen_size[0] = MAX(1, p_ssr_buffers.size.width >> m);
push_constant.screen_size[1] = MAX(1, p_ssr_buffers.size.height >> m);
push_constant.mip_level = m;
RID source = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_SSR, v, m - 1);
RID dest = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_SSR, v, m);
RD::Uniform u_source(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>{ linear_sampler, source });
RD::Uniform u_dest(RD::UNIFORM_TYPE_IMAGE, 1, dest);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.filter_pipeline.get_rid());
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(filter_shader, 0, u_source, u_dest), 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(push_constant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.screen_size[0], push_constant.screen_size[1], 1);
RD::get_singleton()->compute_list_end();
}
}
RD::get_singleton()->draw_command_end_label();
if (ssr_half_size) {
RD::get_singleton()->draw_command_begin_label("SSR Resolve");
RID resolve_shader = ssr.resolve_shader.version_get_shader(ssr.resolve_shader_version, 0);
for (uint32_t v = 0; v < view_count; v++) {
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.resolve_pipeline.get_rid());
Vector2i internal_size = p_render_buffers->get_internal_size();
ScreenSpaceReflectionResolvePushConstant push_constant;
push_constant.screen_size[0] = internal_size.x;
push_constant.screen_size[1] = internal_size.y;
RID depth_texture = p_render_buffers->get_depth_texture(v);
RID depth_half_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_HIZ, v, 0);
RID normal_roughness_half_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_NORMAL_ROUGHNESS, v, 0);
RID ssr_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_SSR, v, 0, 1, p_ssr_buffers.mipmaps);
RID mip_level_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_MIP_LEVEL, v, 0);
RID output_texture = p_render_buffers->get_texture_slice(RB_SCOPE_SSR, RB_FINAL, v, 0);
RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>{ nearest_sampler, depth_texture });
RD::Uniform u_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>{ nearest_sampler, p_normal_roughness_slices[v] });
RD::Uniform u_depth_half(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 2, Vector<RID>{ nearest_sampler, depth_half_texture });
RD::Uniform u_normal_roughness_half(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 3, Vector<RID>{ nearest_sampler, normal_roughness_half_texture });
RD::Uniform u_ssr(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 4, Vector<RID>{ linear_sampler, ssr_texture });
RD::Uniform u_mip_level(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 5, Vector<RID>{ nearest_sampler, mip_level_texture });
RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 6, output_texture);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(resolve_shader, 0, u_depth, u_normal_roughness, u_depth_half, u_normal_roughness_half, u_ssr, u_mip_level, u_output), 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(push_constant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.width, internal_size.height, 1);
RD::get_singleton()->compute_list_end();
}
RD::get_singleton()->draw_command_end_label();
}
} }
/* Subsurface scattering */ /* Subsurface scattering */

View file

@ -32,8 +32,10 @@
#include "servers/rendering/renderer_rd/pipeline_deferred_rd.h" #include "servers/rendering/renderer_rd/pipeline_deferred_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_downsample.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_hiz.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_resolve.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/ssao.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/ssao.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/ssao_blur.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/ssao_blur.glsl.gen.h"
@ -46,6 +48,7 @@
#include "servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl.gen.h"
#include "servers/rendering/rendering_server.h" #include "servers/rendering/rendering_server.h"
#define RB_SCOPE_SSLF SNAME("rb_sslf")
#define RB_SCOPE_SSDS SNAME("rb_ssds") #define RB_SCOPE_SSDS SNAME("rb_ssds")
#define RB_SCOPE_SSIL SNAME("rb_ssil") #define RB_SCOPE_SSIL SNAME("rb_ssil")
#define RB_SCOPE_SSAO SNAME("rb_ssao") #define RB_SCOPE_SSAO SNAME("rb_ssao")
@ -60,16 +63,17 @@
#define RB_IMPORTANCE_MAP SNAME("importance_map") #define RB_IMPORTANCE_MAP SNAME("importance_map")
#define RB_IMPORTANCE_PONG SNAME("importance_pong") #define RB_IMPORTANCE_PONG SNAME("importance_pong")
#define RB_DEPTH_SCALED SNAME("depth_scaled") #define RB_NORMAL_ROUGHNESS SNAME("normal_roughness")
#define RB_NORMAL_SCALED SNAME("normal_scaled") #define RB_HIZ SNAME("hiz")
#define RB_BLUR_RADIUS SNAME("blur_radius") #define RB_SSR SNAME("ssr")
#define RB_INTERMEDIATE SNAME("intermediate") #define RB_MIP_LEVEL SNAME("mip_level")
#define RB_OUTPUT SNAME("output")
class RenderSceneBuffersRD; class RenderSceneBuffersRD;
namespace RendererRD { namespace RendererRD {
class CopyEffects;
class SSEffects { class SSEffects {
private: private:
static SSEffects *singleton; static SSEffects *singleton;
@ -80,6 +84,11 @@ public:
SSEffects(); SSEffects();
~SSEffects(); ~SSEffects();
/* Last Frame */
void allocate_last_frame_buffer(Ref<RenderSceneBuffersRD> p_render_buffers, bool p_use_ssil, bool p_use_ssr);
void copy_internal_texture_to_last_frame(Ref<RenderSceneBuffersRD> p_render_buffers, CopyEffects &p_copy_effects);
/* SS Downsampler */ /* SS Downsampler */
void downsample_depth(Ref<RenderSceneBuffersRD> p_render_buffers, uint32_t p_view, const Projection &p_projection); void downsample_depth(Ref<RenderSceneBuffersRD> p_render_buffers, uint32_t p_view, const Projection &p_projection);
@ -133,15 +142,16 @@ public:
void generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORenderBuffers &p_ssao_buffers, uint32_t p_view, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings); void generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORenderBuffers &p_ssao_buffers, uint32_t p_view, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings);
/* Screen Space Reflection */ /* Screen Space Reflection */
void ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality); void ssr_set_half_size(bool p_half_size);
struct SSRRenderBuffers { struct SSRRenderBuffers {
Size2i size; Size2i size;
RenderingServer::EnvironmentSSRRoughnessQuality roughness_quality = RenderingServer::ENV_SSR_ROUGHNESS_QUALITY_DISABLED; uint32_t mipmaps = 1;
bool half_size = false;
}; };
void ssr_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format); void ssr_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RD::DataFormat p_color_format);
void screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RID *p_normal_roughness_slices, const RID *p_metallic_slices, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const Projection *p_projections, const Vector3 *p_eye_offsets); void screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RID *p_normal_roughness_slices, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const Projection *p_projections, const Projection *p_reprojections, const Vector3 *p_eye_offsets, RendererRD::CopyEffects &p_copy_effects);
/* subsurface scattering */ /* subsurface scattering */
void sss_set_quality(RS::SubSurfaceScatteringQuality p_quality); void sss_set_quality(RS::SubSurfaceScatteringQuality p_quality);
@ -167,7 +177,7 @@ private:
float ssil_fadeout_from = 50.0; float ssil_fadeout_from = 50.0;
float ssil_fadeout_to = 300.0; float ssil_fadeout_to = 300.0;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; bool ssr_half_size = false;
RS::SubSurfaceScatteringQuality sss_quality = RS::SUB_SURFACE_SCATTERING_QUALITY_MEDIUM; RS::SubSurfaceScatteringQuality sss_quality = RS::SUB_SURFACE_SCATTERING_QUALITY_MEDIUM;
float sss_scale = 0.05; float sss_scale = 0.05;
@ -409,99 +419,85 @@ private:
/* Screen Space Reflection */ /* Screen Space Reflection */
enum SSRShaderSpecializations { enum ScreenSpaceReflectionDownsampleMode {
SSR_MULTIVIEW = 1 << 0, SCREEN_SPACE_REFLECTION_DOWNSAMPLE_DEFAULT,
SSR_VARIATIONS = 2, SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH,
SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_HEIGHT,
SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH_AND_HEIGHT,
SCREEN_SPACE_REFLECTION_DOWNSAMPLE_MAX
};
struct ScreenSpaceReflectionDownsamplePushConstant {
int32_t screen_size[2];
int32_t pad[2];
};
enum ScreenSpaceReflectionHizMode {
SCREEN_SPACE_REFLECTION_HIZ_DEFAULT,
SCREEN_SPACE_REFLECTION_HIZ_ODD_WIDTH,
SCREEN_SPACE_REFLECTION_HIZ_ODD_HEIGHT,
SCREEN_SPACE_REFLECTION_HIZ_ODD_WIDTH_AND_HEIGHT,
SCREEN_SPACE_REFLECTION_HIZ_MAX
};
struct ScreenSpaceReflectionHizPushConstant {
int32_t screen_size[2];
int32_t pad[2];
}; };
struct ScreenSpaceReflectionSceneData { struct ScreenSpaceReflectionSceneData {
float projection[2][16]; float projection[2][16];
float inv_projection[2][16]; float inv_projection[2][16];
float reprojection[2][16];
float eye_offset[2][4]; float eye_offset[2][4];
}; };
// SSR Scale
struct ScreenSpaceReflectionScalePushConstant {
int32_t screen_size[2];
float camera_z_near;
float camera_z_far;
uint32_t orthogonal;
uint32_t filter;
uint32_t view_index;
uint32_t pad1;
};
struct ScreenSpaceReflectionScale {
ScreenSpaceReflectionScaleShaderRD shader;
RID shader_version;
PipelineDeferredRD pipelines[SSR_VARIATIONS];
} ssr_scale;
// SSR main
enum ScreenSpaceReflectionMode {
SCREEN_SPACE_REFLECTION_NORMAL,
SCREEN_SPACE_REFLECTION_ROUGH,
SCREEN_SPACE_REFLECTION_MAX,
};
struct ScreenSpaceReflectionPushConstant { struct ScreenSpaceReflectionPushConstant {
float proj_info[4]; // 16 - 16 int32_t screen_size[2];
int32_t mipmaps;
int32_t num_steps;
float distance_fade;
float curve_fade_in;
float depth_tolerance;
int32_t orthogonal;
uint32_t view_index;
int32_t pad[3];
};
int32_t screen_size[2]; // 8 - 24 struct ScreenSpaceReflectionFilterPushConstant {
float camera_z_near; // 4 - 28 int32_t screen_size[2];
float camera_z_far; // 4 - 32 uint32_t mip_level;
int32_t pad;
};
int32_t num_steps; // 4 - 36 struct ScreenSpaceReflectionResolvePushConstant {
float depth_tolerance; // 4 - 40 int32_t screen_size[2];
float distance_fade; // 4 - 44 int32_t pad[2];
float curve_fade_in; // 4 - 48
uint32_t orthogonal; // 4 - 52
float filter_mipmap_levels; // 4 - 56
uint32_t use_half_res; // 4 - 60
uint32_t view_index; // 4 - 64
// float projection[16]; // this is in our ScreenSpaceReflectionSceneData now
}; };
struct ScreenSpaceReflection { struct ScreenSpaceReflection {
ScreenSpaceReflectionShaderRD shader; ScreenSpaceReflectionDownsampleShaderRD downsample_shader;
RID shader_version; RID downsample_shader_version;
PipelineDeferredRD pipelines[SSR_VARIATIONS][SCREEN_SPACE_REFLECTION_MAX]; PipelineDeferredRD downsample_pipelines[SCREEN_SPACE_REFLECTION_DOWNSAMPLE_MAX];
ScreenSpaceReflectionHizShaderRD hiz_shader;
RID hiz_shader_version;
PipelineDeferredRD hiz_pipelines[SCREEN_SPACE_REFLECTION_HIZ_MAX];
ScreenSpaceReflectionShaderRD ssr_shader;
RID ssr_shader_version;
PipelineDeferredRD ssr_pipeline;
RID ubo; RID ubo;
ScreenSpaceReflectionFilterShaderRD filter_shader;
RID filter_shader_version;
PipelineDeferredRD filter_pipeline;
ScreenSpaceReflectionResolveShaderRD resolve_shader;
RID resolve_shader_version;
PipelineDeferredRD resolve_pipeline;
} ssr; } ssr;
// SSR Filter
struct ScreenSpaceReflectionFilterPushConstant {
float proj_info[4]; // 16 - 16
uint32_t orthogonal; // 4 - 20
float edge_tolerance; // 4 - 24
int32_t increment; // 4 - 28
uint32_t view_index; // 4 - 32
int32_t screen_size[2]; // 8 - 40
uint32_t vertical; // 4 - 44
uint32_t steps; // 4 - 48
};
enum SSRReflectionMode {
SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL,
SCREEN_SPACE_REFLECTION_FILTER_VERTICAL,
SCREEN_SPACE_REFLECTION_FILTER_MAX,
};
struct ScreenSpaceReflectionFilter {
ScreenSpaceReflectionFilterShaderRD shader;
RID shader_version;
PipelineDeferredRD pipelines[SSR_VARIATIONS][SCREEN_SPACE_REFLECTION_FILTER_MAX];
} ssr_filter;
/* Subsurface scattering */ /* Subsurface scattering */
enum SSSMode { enum SSSMode {

View file

@ -737,8 +737,17 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat
scene_state.ubo.ssao_light_affect = environment_get_ssao_direct_light_affect(p_render_data->environment); scene_state.ubo.ssao_light_affect = environment_get_ssao_direct_light_affect(p_render_data->environment);
uint32_t ss_flags = 0; uint32_t ss_flags = 0;
if (p_opaque_render_buffers) { if (p_opaque_render_buffers) {
ss_flags |= environment_get_ssao_enabled(p_render_data->environment) ? 1 : 0; ss_flags |= environment_get_ssao_enabled(p_render_data->environment) ? (1 << 0) : 0;
ss_flags |= environment_get_ssil_enabled(p_render_data->environment) ? 2 : 0; ss_flags |= environment_get_ssil_enabled(p_render_data->environment) ? (1 << 1) : 0;
ss_flags |= environment_get_ssr_enabled(p_render_data->environment) ? (1 << 2) : 0;
if (rd.is_valid()) {
Ref<RenderBufferDataForwardClustered> rb_data;
if (rd->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) {
rb_data = rd->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
ss_flags |= (rb_data.is_valid() && !rb_data->ss_effects_data.ssr.half_size) ? (1 << 3) : 0;
}
}
} }
scene_state.ubo.ss_effects_flags = ss_flags; scene_state.ubo.ss_effects_flags = ss_flags;
} else { } else {
@ -1407,42 +1416,50 @@ void RenderForwardClustered::_process_ssil(Ref<RenderSceneBuffersRD> p_render_bu
Projection correction; Projection correction;
correction.set_depth_correction(true); correction.set_depth_correction(true);
Projection projection = correction * p_projections[v]; Projection projection = correction * p_projections[v];
Projection last_frame_projection = rb_data->ss_effects_data.last_frame_projections[v] * Projection(rb_data->ss_effects_data.last_frame_transform.affine_inverse()) * Projection(transform) * projection.inverse(); Projection last_frame_projection = rb_data->ss_effects_data.ssil_last_frame_projections[v] * Projection(rb_data->ss_effects_data.ssil_last_frame_transform.affine_inverse()) * Projection(transform) * projection.inverse();
ss_effects->screen_space_indirect_lighting(p_render_buffers, rb_data->ss_effects_data.ssil, v, p_normal_buffers[v], p_projections[v], last_frame_projection, settings); ss_effects->screen_space_indirect_lighting(p_render_buffers, rb_data->ss_effects_data.ssil, v, p_normal_buffers[v], p_projections[v], last_frame_projection, settings);
rb_data->ss_effects_data.last_frame_projections[v] = projection; rb_data->ss_effects_data.ssil_last_frame_projections[v] = projection;
} }
rb_data->ss_effects_data.last_frame_transform = transform; rb_data->ss_effects_data.ssil_last_frame_transform = transform;
} }
void RenderForwardClustered::_copy_framebuffer_to_ssil(Ref<RenderSceneBuffersRD> p_render_buffers) { void RenderForwardClustered::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_slices, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_transform) {
ERR_FAIL_NULL(ss_effects);
ERR_FAIL_COND(p_render_buffers.is_null()); ERR_FAIL_COND(p_render_buffers.is_null());
if (p_render_buffers->has_texture(RB_SCOPE_SSIL, RB_LAST_FRAME)) { Ref<RenderBufferDataForwardClustered> rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
Size2i size = p_render_buffers->get_internal_size(); ERR_FAIL_COND(rb_data.is_null());
uint32_t mipmaps = p_render_buffers->get_texture_format(RB_SCOPE_SSIL, RB_LAST_FRAME).mipmaps;
for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) {
RID source = p_render_buffers->get_internal_texture(v);
RID dest = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_LAST_FRAME, v, 0);
copy_effects->copy_to_rect(source, dest, Rect2i(0, 0, size.x, size.y));
int width = size.x; RENDER_TIMESTAMP("Process SSR");
int height = size.y;
for (uint32_t m = 1; m < mipmaps; m++) {
width = MAX(1, width >> 1);
height = MAX(1, height >> 1);
source = dest; ss_effects->ssr_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.ssr, _render_buffers_get_color_format());
dest = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_LAST_FRAME, v, m);
copy_effects->make_mipmap(source, dest, Size2i(width, height)); Projection reprojections[RendererSceneRender::MAX_RENDER_VIEWS];
}
} for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) {
Projection correction;
correction.set_depth_correction(true);
Projection projection = correction * p_projections[v];
reprojections[v] = rb_data->ss_effects_data.ssr_last_frame_projections[v] * Projection(rb_data->ss_effects_data.ssr_last_frame_transform.affine_inverse()) * Projection(p_transform) * projection.inverse();
rb_data->ss_effects_data.ssr_last_frame_projections[v] = projection;
} }
rb_data->ss_effects_data.ssr_last_frame_transform = p_transform;
ss_effects->screen_space_reflection(p_render_buffers, rb_data->ss_effects_data.ssr, p_normal_slices, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), p_projections, reprojections, p_eye_offsets, *copy_effects);
} }
void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer) { void RenderForwardClustered::_copy_framebuffer_to_ss_effects(Ref<RenderSceneBuffersRD> p_render_buffers, bool p_use_ssil, bool p_use_ssr) {
ERR_FAIL_NULL(ss_effects);
ERR_FAIL_COND(p_render_buffers.is_null());
ss_effects->copy_internal_texture_to_last_frame(p_render_buffers, *copy_effects);
}
void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_ssr, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer) {
// Render shadows while GI is rendering, due to how barriers are handled, this should happen at the same time // Render shadows while GI is rendering, due to how barriers are handled, this should happen at the same time
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
@ -1540,6 +1557,10 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
// This should allow most of the processing to happen in parallel even if we're doing // This should allow most of the processing to happen in parallel even if we're doing
// drawcalls per eye/view. It will all sync up at the barrier. // drawcalls per eye/view. It will all sync up at the barrier.
if (p_use_ssil || p_use_ssr) {
ss_effects->allocate_last_frame_buffer(rb, p_use_ssil, p_use_ssr);
}
if (p_use_ssao || p_use_ssil) { if (p_use_ssao || p_use_ssil) {
RENDER_TIMESTAMP("Prepare Depth for SSAO/SSIL"); RENDER_TIMESTAMP("Prepare Depth for SSAO/SSIL");
// Convert our depth buffer data to linear data in // Convert our depth buffer data to linear data in
@ -1555,6 +1576,10 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
_process_ssil(rb, p_render_data->environment, p_normal_roughness_slices, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform); _process_ssil(rb, p_render_data->environment, p_normal_roughness_slices, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform);
} }
} }
if (p_use_ssr) {
_process_ssr(rb, p_render_data->environment, p_normal_roughness_slices, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform);
}
} }
RENDER_TIMESTAMP("Pre Opaque Render"); RENDER_TIMESTAMP("Pre Opaque Render");
@ -1597,33 +1622,6 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
} }
} }
void RenderForwardClustered::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_slices, RID p_specular_buffer, const RID *p_metallic_slices, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive) {
ERR_FAIL_NULL(ss_effects);
ERR_FAIL_COND(p_render_buffers.is_null());
Ref<RenderBufferDataForwardClustered> rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
ERR_FAIL_COND(rb_data.is_null());
Size2i internal_size = p_render_buffers->get_internal_size();
bool can_use_effects = internal_size.x >= 8 && internal_size.y >= 8;
uint32_t view_count = p_render_buffers->get_view_count();
if (!can_use_effects) {
//just copy
copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : p_render_buffers->get_internal_texture(), RID(), view_count);
return;
}
ERR_FAIL_COND(p_environment.is_null());
ERR_FAIL_COND(!environment_get_ssr_enabled(p_environment));
ss_effects->ssr_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.ssr, _render_buffers_get_color_format());
ss_effects->screen_space_reflection(p_render_buffers, rb_data->ss_effects_data.ssr, p_normal_slices, p_metallic_slices, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), p_projections, p_eye_offsets);
RID output = p_render_buffers->get_texture(RB_SCOPE_SSR, RB_OUTPUT);
copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : p_render_buffers->get_internal_texture(), output, view_count);
}
void RenderForwardClustered::_process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera) { void RenderForwardClustered::_process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera) {
ERR_FAIL_COND(p_render_buffers.is_null()); ERR_FAIL_COND(p_render_buffers.is_null());
@ -1819,9 +1817,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
} }
if (environment_get_ssr_enabled(p_render_data->environment)) { if (environment_get_ssr_enabled(p_render_data->environment)) {
if (!p_render_data->transparent_bg) { if (!p_render_data->transparent_bg) {
using_separate_specular = true;
using_ssr = true; using_ssr = true;
color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR;
} else { } else {
WARN_PRINT_ONCE("Screen-space reflections are not supported in viewports with a transparent background. Disabling SSR in transparent viewport."); WARN_PRINT_ONCE("Screen-space reflections are not supported in viewports with a transparent background. Disabling SSR in transparent viewport.");
} }
@ -2122,7 +2118,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
normal_roughness_views[v] = rb_data->get_normal_roughness(v); normal_roughness_views[v] = rb_data->get_normal_roughness(v);
} }
} }
_pre_opaque_render(p_render_data, using_ssao, using_ssil, using_sdfgi || using_voxelgi, normal_roughness_views, rb_data.is_valid() && rb_data->has_voxelgi() ? rb_data->get_voxelgi() : RID()); _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_ssr, using_sdfgi || using_voxelgi, normal_roughness_views, rb_data.is_valid() && rb_data->has_voxelgi() ? rb_data->get_voxelgi() : RID());
RENDER_TIMESTAMP("Render Opaque Pass"); RENDER_TIMESTAMP("Render Opaque Pass");
@ -2279,16 +2275,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_command_end_label();
} }
if (using_ssr) { {
RENDER_TIMESTAMP("Screen-Space Reflections");
RD::get_singleton()->draw_command_begin_label("Process Screen-Space Reflections");
RID specular_views[RendererSceneRender::MAX_RENDER_VIEWS];
for (uint32_t v = 0; v < p_render_data->scene_data->view_count; v++) {
specular_views[v] = rb_data->get_specular(v);
}
_process_ssr(rb, color_only_framebuffer, normal_roughness_views, rb_data->get_specular(), specular_views, p_render_data->environment, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, !use_msaa);
RD::get_singleton()->draw_command_end_label();
} else {
//just mix specular back //just mix specular back
RENDER_TIMESTAMP("Merge Specular"); RENDER_TIMESTAMP("Merge Specular");
copy_effects->merge_specular(color_only_framebuffer, rb_data->get_specular(), !use_msaa ? RID() : rb->get_internal_texture(), RID(), p_render_data->scene_data->view_count); copy_effects->merge_specular(color_only_framebuffer, rb_data->get_specular(), !use_msaa ? RID() : rb->get_internal_texture(), RID(), p_render_data->scene_data->view_count);
@ -2394,18 +2381,18 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_command_end_label();
RD::get_singleton()->draw_command_begin_label("Copy Framebuffer for SSIL/SSR");
if (using_ssil || using_ssr) {
RENDER_TIMESTAMP("Copy Final Framebuffer (SSIL/SSR)");
_copy_framebuffer_to_ss_effects(rb, using_ssil, using_ssr);
}
RD::get_singleton()->draw_command_end_label();
{ {
RENDER_TIMESTAMP("Process Post Transparent Compositor Effects"); RENDER_TIMESTAMP("Process Post Transparent Compositor Effects");
_process_compositor_effects(RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_TRANSPARENT, p_render_data); _process_compositor_effects(RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_POST_TRANSPARENT, p_render_data);
} }
RD::get_singleton()->draw_command_begin_label("Copy Framebuffer for SSIL");
if (using_ssil) {
RENDER_TIMESTAMP("Copy Final Framebuffer (SSIL)");
_copy_framebuffer_to_ssil(rb);
}
RD::get_singleton()->draw_command_end_label();
if (rb_data.is_valid() && (using_upscaling || using_taa)) { if (rb_data.is_valid() && (using_upscaling || using_taa)) {
if (scale_type == SCALE_FSR2) { if (scale_type == SCALE_FSR2) {
rb_data->ensure_fsr2(fsr2_effect); rb_data->ensure_fsr2(fsr2_effect);
@ -3577,6 +3564,38 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend
u.append_id(texture); u.append_id(texture);
uniforms.push_back(u); uniforms.push_back(u);
} }
{
RD::Uniform u;
u.binding = 35;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
RID ssr;
if (rb_data.is_valid()) {
if (rb_data->ss_effects_data.ssr.half_size) {
if (rb->has_texture(RB_SCOPE_SSR, RB_FINAL)) {
ssr = rb->get_texture(RB_SCOPE_SSR, RB_FINAL);
}
} else {
if (rb->has_texture(RB_SCOPE_SSR, RB_SSR)) {
ssr = rb->get_texture(RB_SCOPE_SSR, RB_SSR);
}
}
}
RID texture = ssr.is_valid() ? ssr : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
u.append_id(texture);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.binding = 36;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
RID ssr_mip_level = (rb_data.is_valid() && !rb_data->ss_effects_data.ssr.half_size && rb->has_texture(RB_SCOPE_SSR, RB_MIP_LEVEL)) ? rb->get_texture(RB_SCOPE_SSR, RB_MIP_LEVEL) : RID();
RID texture = ssr_mip_level.is_valid() ? ssr_mip_level : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
u.append_id(texture);
uniforms.push_back(u);
}
return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_rd, RENDER_PASS_UNIFORM_SET, uniforms); return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_rd, RENDER_PASS_UNIFORM_SET, uniforms);
} }
@ -3814,10 +3833,13 @@ void RenderForwardClustered::environment_set_ssil_quality(RS::EnvironmentSSILQua
ss_effects->ssil_set_quality(p_quality, p_half_size, p_adaptive_target, p_blur_passes, p_fadeout_from, p_fadeout_to); ss_effects->ssil_set_quality(p_quality, p_half_size, p_adaptive_target, p_blur_passes, p_fadeout_from, p_fadeout_to);
} }
void RenderForwardClustered::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) { void RenderForwardClustered::environment_set_ssr_half_size(bool p_half_size) {
ERR_FAIL_NULL(ss_effects); ERR_FAIL_NULL(ss_effects);
ERR_FAIL_COND(p_quality < RS::EnvironmentSSRRoughnessQuality::ENV_SSR_ROUGHNESS_QUALITY_DISABLED || p_quality > RS::EnvironmentSSRRoughnessQuality::ENV_SSR_ROUGHNESS_QUALITY_HIGH); ss_effects->ssr_set_half_size(p_half_size);
ss_effects->ssr_set_roughness_quality(p_quality); }
void RenderForwardClustered::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) {
WARN_PRINT_ONCE("environment_set_ssr_roughness_quality has been deprecated and no longer does anything.");
} }
void RenderForwardClustered::sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) { void RenderForwardClustered::sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) {

View file

@ -102,8 +102,11 @@ public:
ClusterBuilderRD *cluster_builder = nullptr; ClusterBuilderRD *cluster_builder = nullptr;
struct SSEffectsData { struct SSEffectsData {
Projection last_frame_projections[RendererSceneRender::MAX_RENDER_VIEWS]; Projection ssil_last_frame_projections[RendererSceneRender::MAX_RENDER_VIEWS];
Transform3D last_frame_transform; Transform3D ssil_last_frame_transform;
Projection ssr_last_frame_projections[RendererSceneRender::MAX_RENDER_VIEWS];
Transform3D ssr_last_frame_transform;
RendererRD::SSEffects::SSILRenderBuffers ssil; RendererRD::SSEffects::SSILRenderBuffers ssil;
RendererRD::SSEffects::SSAORenderBuffers ssao; RendererRD::SSEffects::SSAORenderBuffers ssao;
@ -758,9 +761,9 @@ private:
/* Render Scene */ /* Render Scene */
void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections); void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections);
void _process_ssil(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections, const Transform3D &p_transform); void _process_ssil(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections, const Transform3D &p_transform);
void _copy_framebuffer_to_ssil(Ref<RenderSceneBuffersRD> p_render_buffers); void _process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_slices, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_transform);
void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer); void _copy_framebuffer_to_ss_effects(Ref<RenderSceneBuffersRD> p_render_buffers, bool p_use_ssil, bool p_use_ssr);
void _process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_buffer_slices, RID p_specular_buffer, const RID *p_metallic_slices, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive); void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_ssr, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer);
void _process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera); void _process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera);
/* Debug */ /* Debug */
@ -774,6 +777,7 @@ protected:
virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
virtual void environment_set_ssr_half_size(bool p_half_size) override;
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override; virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override;
virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override; virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override;

View file

@ -422,6 +422,7 @@ protected:
virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {}
virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {}
virtual void environment_set_ssr_half_size(bool p_half_size) override {}
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override {} virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override {}
virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override {} virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override {}

View file

@ -6,228 +6,271 @@
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_diffuse; layout(set = 0, binding = 0) uniform sampler2D source_last_frame;
layout(r32f, set = 0, binding = 1) uniform restrict readonly image2D source_depth; layout(set = 0, binding = 1) uniform sampler2D source_hiz;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D ssr_image; layout(set = 0, binding = 2) uniform sampler2D source_normal_roughness;
#ifdef MODE_ROUGH layout(rgba16f, set = 0, binding = 3) uniform restrict writeonly image2D output_color;
layout(r8, set = 1, binding = 1) uniform restrict writeonly image2D blur_radius_image; layout(r8, set = 0, binding = 4) uniform restrict writeonly image2D output_mip_level;
#endif
layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal_roughness; layout(set = 0, binding = 5, std140) uniform SceneData {
layout(set = 3, binding = 0) uniform sampler2D source_metallic; mat4 projection[2];
mat4 inv_projection[2];
mat4 reprojection[2];
vec4 eye_offset[2];
}
scene_data;
layout(push_constant, std430) uniform Params { layout(push_constant, std430) uniform Params {
vec4 proj_info;
ivec2 screen_size; ivec2 screen_size;
float camera_z_near; int mipmaps;
float camera_z_far;
int num_steps; int num_steps;
float depth_tolerance;
float distance_fade; float distance_fade;
float curve_fade_in; float curve_fade_in;
float depth_tolerance;
bool orthogonal; bool orthogonal;
float filter_mipmap_levels; int view_index;
bool use_half_res;
uint view_index;
} }
params; params;
#include "screen_space_reflection_inc.glsl" vec2 compute_cell_count(int level) {
int cell_count_x = max(1, params.screen_size.x >> level);
int cell_count_y = max(1, params.screen_size.y >> level);
return vec2(cell_count_x, cell_count_y);
}
vec2 view_to_screen(vec3 view_pos, out float w) { float linearize_depth(float depth) {
vec4 projected = scene_data.projection[params.view_index] * vec4(view_pos, 1.0); vec4 pos = vec4(0.0, 0.0, depth, 1.0);
projected.xyz /= projected.w; pos = scene_data.inv_projection[params.view_index] * pos;
projected.xy = projected.xy * 0.5 + 0.5; return pos.z / pos.w;
w = projected.w; }
return projected.xy;
vec3 compute_view_pos(vec3 screen_pos) {
vec4 pos;
pos.xy = screen_pos.xy * 2.0 - 1.0;
pos.z = screen_pos.z;
pos.w = 1.0;
pos = scene_data.inv_projection[params.view_index] * pos;
return pos.xyz / pos.w;
}
vec3 compute_screen_pos(vec3 pos) {
vec4 screen_pos = scene_data.projection[params.view_index] * vec4(pos, 1.0);
screen_pos.xyz /= screen_pos.w;
screen_pos.xy = screen_pos.xy * 0.5 + 0.5;
return screen_pos.xyz;
}
// https://habr.com/ru/articles/744336/
vec3 compute_geometric_normal(ivec2 pixel_pos, float depth_c, vec3 view_c, float pixel_offset) {
vec4 H = vec4(
texelFetch(source_hiz, pixel_pos + ivec2(-1, 0), 0).x,
texelFetch(source_hiz, pixel_pos + ivec2(-2, 0), 0).x,
texelFetch(source_hiz, pixel_pos + ivec2(1, 0), 0).x,
texelFetch(source_hiz, pixel_pos + ivec2(2, 0), 0).x);
vec4 V = vec4(
texelFetch(source_hiz, pixel_pos + ivec2(0, -1), 0).x,
texelFetch(source_hiz, pixel_pos + ivec2(0, -2), 0).x,
texelFetch(source_hiz, pixel_pos + ivec2(0, 1), 0).x,
texelFetch(source_hiz, pixel_pos + ivec2(0, 2), 0).x);
vec2 he = abs((2.0 * H.xz - H.yw) - depth_c);
vec2 ve = abs((2.0 * V.xz - V.yw) - depth_c);
int h_sign = he.x < he.y ? -1 : 1;
int v_sign = ve.x < ve.y ? -1 : 1;
vec3 view_h = compute_view_pos(vec3((pixel_pos + vec2(h_sign, 0) + pixel_offset) / params.screen_size, H[1 + int(h_sign)]));
vec3 view_v = compute_view_pos(vec3((pixel_pos + vec2(0, v_sign) + pixel_offset) / params.screen_size, V[1 + int(v_sign)]));
vec3 h_der = h_sign * (view_h - view_c);
vec3 v_der = v_sign * (view_v - view_c);
return cross(v_der, h_der);
} }
#define M_PI 3.14159265359 #define M_PI 3.14159265359
void main() { void main() {
// Pixel being shaded ivec2 pixel_pos = ivec2(gl_GlobalInvocationID.xy);
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing if (any(greaterThanEqual(pixel_pos, params.screen_size))) {
return; return;
} }
vec2 pixel_size = 1.0 / vec2(params.screen_size); vec4 color = vec4(0.0);
vec2 uv = vec2(ssC.xy) * pixel_size; float mip_level = 0.0;
uv += pixel_size * 0.5; vec3 screen_pos;
screen_pos.xy = vec2(pixel_pos + 0.5) / params.screen_size;
screen_pos.z = texelFetch(source_hiz, pixel_pos, 0).x;
float base_depth = imageLoad(source_depth, ssC).r; bool should_trace = screen_pos.z != 0.0;
if (should_trace) {
vec3 pos = compute_view_pos(screen_pos);
// World space point being shaded vec4 normal_roughness = texelFetch(source_normal_roughness, pixel_pos, 0);
vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth); vec3 normal = normalize(normal_roughness.xyz * 2.0 - 1.0);
float roughness = normal_roughness.w;
vec4 normal_roughness = imageLoad(source_normal_roughness, ssC); if (roughness > 0.5) {
vec3 normal = normalize(normal_roughness.xyz * 2.0 - 1.0); roughness = 1.0 - roughness;
float roughness = normal_roughness.w;
if (roughness > 0.5) {
roughness = 1.0 - roughness;
}
roughness /= (127.0 / 255.0);
// The roughness cutoff of 0.6 is chosen to match the roughness fadeout from GH-69828.
if (roughness > 0.6) {
// Do not compute SSR for rough materials to improve performance at the cost of
// subtle artifacting.
#ifdef MODE_ROUGH
imageStore(blur_radius_image, ssC, vec4(0.0));
#endif
imageStore(ssr_image, ssC, vec4(0.0));
return;
}
normal = normalize(normal);
normal.y = -normal.y; //because this code reads flipped
vec3 view_dir;
if (sc_multiview) {
view_dir = normalize(vertex + scene_data.eye_offset[params.view_index].xyz);
} else {
view_dir = params.orthogonal ? vec3(0.0, 0.0, -1.0) : normalize(vertex);
}
vec3 ray_dir = normalize(reflect(view_dir, normal));
if (dot(ray_dir, normal) < 0.001) {
imageStore(ssr_image, ssC, vec4(0.0));
return;
}
////////////////
// make ray length and clip it against the near plane (don't want to trace beyond visible)
float ray_len = (vertex.z + ray_dir.z * params.camera_z_far) > -params.camera_z_near ? (-params.camera_z_near - vertex.z) / ray_dir.z : params.camera_z_far;
vec3 ray_end = vertex + ray_dir * ray_len;
float w_begin;
vec2 vp_line_begin = view_to_screen(vertex, w_begin);
float w_end;
vec2 vp_line_end = view_to_screen(ray_end, w_end);
vec2 vp_line_dir = vp_line_end - vp_line_begin;
// we need to interpolate w along the ray, to generate perspective correct reflections
w_begin = 1.0 / w_begin;
w_end = 1.0 / w_end;
float z_begin = vertex.z * w_begin;
float z_end = ray_end.z * w_end;
vec2 line_begin = vp_line_begin / pixel_size;
vec2 line_dir = vp_line_dir / pixel_size;
float z_dir = z_end - z_begin;
float w_dir = w_end - w_begin;
// clip the line to the viewport edges
float scale_max_x = min(1.0, 0.99 * (1.0 - vp_line_begin.x) / max(1e-5, vp_line_dir.x));
float scale_max_y = min(1.0, 0.99 * (1.0 - vp_line_begin.y) / max(1e-5, vp_line_dir.y));
float scale_min_x = min(1.0, 0.99 * vp_line_begin.x / max(1e-5, -vp_line_dir.x));
float scale_min_y = min(1.0, 0.99 * vp_line_begin.y / max(1e-5, -vp_line_dir.y));
float line_clip = min(scale_max_x, scale_max_y) * min(scale_min_x, scale_min_y);
line_dir *= line_clip;
z_dir *= line_clip;
w_dir *= line_clip;
// clip z and w advance to line advance
vec2 line_advance = normalize(line_dir); // down to pixel
float step_size = 1.0 / length(line_dir);
float z_advance = z_dir * step_size; // adapt z advance to line advance
float w_advance = w_dir * step_size; // adapt w advance to line advance
// make line advance faster if direction is closer to pixel edges (this avoids sampling the same pixel twice)
float advance_angle_adj = 1.0 / max(abs(line_advance.x), abs(line_advance.y));
line_advance *= advance_angle_adj; // adapt z advance to line advance
z_advance *= advance_angle_adj;
w_advance *= advance_angle_adj;
vec2 pos = line_begin;
float z = z_begin;
float w = w_begin;
float z_from = z / w;
float z_to = z_from;
float depth;
vec2 prev_pos = pos;
if (ivec2(pos + line_advance - 0.5) == ssC) {
// It is possible for rounding to cause our first pixel to check to be the pixel we're reflecting.
// Make sure we skip it
pos += line_advance;
z += z_advance;
w += w_advance;
}
bool found = false;
float steps_taken = 0.0;
for (int i = 0; i < params.num_steps; i++) {
pos += line_advance;
z += z_advance;
w += w_advance;
// convert to linear depth
ivec2 test_pos = ivec2(pos - 0.5);
depth = imageLoad(source_depth, test_pos).r;
if (sc_multiview) {
depth = depth * 2.0 - 1.0;
depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
depth = -depth;
} }
roughness /= (127.0 / 255.0);
z_from = z_to; // Do not compute SSR for rough materials to improve
z_to = z / w; // performance at the cost of subtle artifacting.
if (roughness >= 0.7) {
if (depth > z_to) { imageStore(output_color, pixel_pos, vec4(0.0));
// Test if our ray is hitting the "right" side of the surface, if not we're likely self reflecting and should skip. imageStore(output_mip_level, pixel_pos, vec4(0.0));
vec4 test_normal_roughness = imageLoad(source_normal_roughness, test_pos);
vec3 test_normal = test_normal_roughness.xyz * 2.0 - 1.0;
test_normal = normalize(test_normal);
test_normal.y = -test_normal.y; // Because this code reads flipped.
if (dot(ray_dir, test_normal) < 0.001) {
// if depth was surpassed
if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far * 0.95) {
// check the depth tolerance and far clip
// check that normal is valid
found = true;
}
break;
}
}
steps_taken += 1.0;
prev_pos = pos;
}
if (found) {
float margin_blend = 1.0;
vec2 final_pos = pos;
vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.05); // make a uniform margin
if (any(bvec4(lessThan(pos, vec2(0.0, 0.0)), greaterThan(pos, params.screen_size)))) {
// clip at the screen edges
imageStore(ssr_image, ssC, vec4(0.0));
return; return;
} }
{ vec3 geom_normal = normalize(compute_geometric_normal(pixel_pos, screen_pos.z, pos, 0.5));
//blend fading out towards inner margin
// 0.5 = midpoint of reflection // Add a small bias towards the geometry normal to prevent self intersections.
vec2 margin_grad = mix(params.screen_size - pos, pos, lessThan(pos, params.screen_size * 0.5)); pos += geom_normal * (1.0 - pow(clamp(dot(normal, geom_normal), 0.0, 1.0), 8.0));
margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y); screen_pos = compute_screen_pos(pos);
//margin_blend = 1.0;
vec3 view_dir = params.orthogonal ? vec3(0.0, 0.0, -1.0) : normalize(pos + scene_data.eye_offset[params.view_index].xyz);
vec3 ray_dir = normalize(reflect(view_dir, normal));
// Check if the ray is immediately intersecting with itself. If so, bounce!
if (dot(ray_dir, geom_normal) < 0.0) {
ray_dir = normalize(reflect(ray_dir, geom_normal));
} }
vec3 end_pos = pos + ray_dir;
// Clip to near plane. Add a small bias so we don't go to infinity.
if (end_pos.z > 0.0) {
end_pos -= ray_dir / ray_dir.z * (end_pos.z + 0.00001);
}
vec3 screen_end_pos = compute_screen_pos(end_pos);
// Normalize Z to -1.0 or +1.0 and do parametric T tracing as suggested here:
// https://hacksoflife.blogspot.com/2020/10/a-tip-for-hiz-ssr-parametric-t-tracing.html
vec3 screen_ray_dir = screen_end_pos - screen_pos;
screen_ray_dir /= abs(screen_ray_dir.z);
bool facing_camera = screen_ray_dir.z >= 0.0;
// Find the screen edge point where we will stop tracing.
vec2 t0 = (vec2(0.0) - screen_pos.xy) / screen_ray_dir.xy;
vec2 t1 = (vec2(1.0) - screen_pos.xy) / screen_ray_dir.xy;
vec2 t2 = max(t0, t1);
float t_max = min(t2.x, t2.y);
vec2 cell_step = vec2(screen_ray_dir.x < 0.0 ? -1.0 : 1.0, screen_ray_dir.y < 0.0 ? -1.0 : 1.0);
int cur_level = 0;
int cur_iteration = params.num_steps;
// Advance the start point to the closest next cell to prevent immediate self intersection.
float t;
{
vec2 cell_index = floor(screen_pos.xy * params.screen_size);
vec2 new_cell_index = cell_index + clamp(cell_step, vec2(0.0), vec2(1.0));
vec2 new_cell_pos = (new_cell_index / params.screen_size) + cell_step * 0.000001;
vec2 pos_t = (new_cell_pos - screen_pos.xy) / screen_ray_dir.xy;
float edge_t = min(pos_t.x, pos_t.y);
t = edge_t;
}
while (cur_level >= 0 && cur_iteration > 0 && t < t_max) {
vec3 cur_screen_pos = screen_pos + screen_ray_dir * t;
vec2 cell_count = compute_cell_count(cur_level);
vec2 cell_index = floor(cur_screen_pos.xy * cell_count);
float cell_depth = texelFetch(source_hiz, ivec2(cell_index), cur_level).x;
float depth_t = (cell_depth - screen_pos.z) * screen_ray_dir.z; // Z is either -1.0 or 1.0 so we don't need to do a divide.
vec2 new_cell_index = cell_index + clamp(cell_step, vec2(0.0), vec2(1.0));
vec2 new_cell_pos = (new_cell_index / cell_count) + cell_step * 0.000001;
vec2 pos_t = (new_cell_pos - screen_pos.xy) / screen_ray_dir.xy;
float edge_t = min(pos_t.x, pos_t.y);
bool hit = facing_camera ? (t <= depth_t) : (depth_t <= edge_t);
int mip_offset = hit ? -1 : +1;
if (cur_level == 0) {
float z0 = linearize_depth(cell_depth);
float z1 = linearize_depth(cur_screen_pos.z);
if ((z0 - z1) > params.depth_tolerance) {
hit = false;
mip_offset = 0; // Keep the mip index the same to prevent it from decreasing and increasing in repeat.
}
}
if (hit) {
if (!facing_camera) {
t = max(t, depth_t);
}
} else {
t = edge_t;
}
cur_level = min(cur_level + mip_offset, params.mipmaps - 1);
--cur_iteration;
}
vec3 cur_screen_pos = screen_pos + screen_ray_dir * t;
vec4 reprojected_pos;
reprojected_pos.xy = cur_screen_pos.xy * 2.0 - 1.0;
reprojected_pos.z = cur_screen_pos.z;
reprojected_pos.w = 1.0;
reprojected_pos = scene_data.reprojection[params.view_index] * reprojected_pos;
reprojected_pos.xy = reprojected_pos.xy / reprojected_pos.w * 0.5 + 0.5;
// Instead of hard rejecting samples, write sample validity to the alpha channel.
// This allows invalid samples to write mip levels to let valid samples have smoother roughness transitions.
float validity = 1.0;
// Hit validation logic is referenced from here:
// https://github.com/GPUOpen-Effects/FidelityFX-SSSR/blob/master/ffx-sssr/ffx_sssr.h
ivec2 cur_pixel_pos = ivec2(cur_screen_pos.xy * params.screen_size);
float hit_depth = texelFetch(source_hiz, cur_pixel_pos, 0).x;
if (t >= t_max || hit_depth == 0.0) {
validity = 0.0;
}
if (all(lessThan(abs(screen_ray_dir.xy * t), 2.0 / params.screen_size))) {
vec3 hit_normal = texelFetch(source_normal_roughness, cur_pixel_pos, 0).xyz * 2.0 - 1.0;
if (dot(ray_dir, hit_normal) >= 0.0) {
validity = 0.0;
}
}
vec3 cur_pos = compute_view_pos(cur_screen_pos);
vec3 hit_pos = compute_view_pos(vec3(cur_screen_pos.xy, hit_depth));
float delta = length(cur_pos - hit_pos);
float confidence = 1.0 - smoothstep(0.0, params.depth_tolerance, delta);
validity *= clamp(confidence * confidence, 0.0, 1.0);
float margin_blend = 1.0;
vec2 reprojected_pixel_pos = reprojected_pos.xy * params.screen_size;
vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.05); // Make a uniform margin.
{
// Blend fading out towards inner margin.
// 0.5 = midpoint of reflection
vec2 margin_grad = mix(params.screen_size - reprojected_pixel_pos, reprojected_pixel_pos, lessThan(reprojected_pixel_pos, params.screen_size * 0.5));
margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y);
}
float ray_len = length(screen_ray_dir.xy * t);
// Fade In / Fade Out // Fade In / Fade Out
float grad = (steps_taken + 1.0) / float(params.num_steps); float grad = ray_len;
float initial_fade = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in); float fade_in = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in);
float fade = pow(clamp(1.0 - grad, 0.0, 1.0), params.distance_fade) * initial_fade; float fade_out = params.distance_fade == 0.0 ? 1.0 : pow(clamp(1.0 - grad, 0.0, 1.0), params.distance_fade);
float fade = fade_in * fade_out;
// Ensure that precision errors do not introduce any fade. Even if it is just slightly below 1.0, // Ensure that precision errors do not introduce any fade. Even if it is just slightly below 1.0,
// strong specular light can leak through the reflection. // strong specular light can leak through the reflection.
@ -235,41 +278,23 @@ void main() {
fade = 1.0; fade = 1.0;
} }
// This is an ad-hoc term to fade out the SSR as roughness increases. Values used validity *= fade * margin_blend;
// are meant to match the visual appearance of a ReflectionProbe.
float roughness_fade = smoothstep(0.4, 0.7, 1.0 - roughness);
// Schlick term. if (validity > 0.0) {
float metallic = texelFetch(source_metallic, ssC << 1, 0).w; color = vec4(textureLod(source_last_frame, reprojected_pos.xy, 0).xyz, 1.0) * validity;
// F0 is the reflectance of normally incident light (perpendicular to the surface). // Tone map the SSR color to have smoother roughness filtering across samples with varying luminance.
// Dielectric materials have a widely accepted default value of 0.04. We assume that metals reflect all light, so their F0 is 1.0. const vec3 rec709_luminance_weights = vec3(0.2126, 0.7152, 0.0722);
float f0 = mix(0.04, 1.0, metallic); color.rgb /= 1.0 + dot(color.rgb, rec709_luminance_weights);
float m = clamp(1.0 - dot(normal, -view_dir), 0.0, 1.0); }
float m2 = m * m;
m = m2 * m2 * m; // pow(m,5)
float fresnel_term = f0 + (1.0 - f0) * m; // Fresnel Schlick term.
// The alpha value of final_color controls the blending with specular light in specular_merge.glsl.
// Note that the Fresnel term is multiplied with the RGB color instead of being a part of the alpha value.
// There is a key difference:
// - multiplying a term with RGB darkens the SSR light without introducing/taking away specular light.
// - combining a term into the Alpha value introduces specular light at the expense of the SSR light.
vec4 final_color = vec4(imageLoad(source_diffuse, ivec2(final_pos - 0.5)).rgb * fresnel_term, fade * margin_blend * roughness_fade);
imageStore(ssr_image, ssC, final_color);
#ifdef MODE_ROUGH
// if roughness is enabled, do screen space cone tracing
float blur_radius = 0.0;
if (roughness > 0.001) { if (roughness > 0.001) {
float cone_angle = min(roughness, 0.999) * M_PI * 0.5; float cone_angle = min(roughness, 0.999) * M_PI * 0.5;
float cone_len = length(final_pos - line_begin); float cone_len = ray_len;
float op_len = 2.0 * tan(cone_angle) * cone_len; // opposite side of iso triangle float op_len = 2.0 * tan(cone_angle) * cone_len; // Opposite side of iso triangle.
float blur_radius;
{ {
// fit to sphere inside cone (sphere ends at end of cone), something like this: // Fit to sphere inside cone (sphere ends at end of cone), something like this:
// ___ // ___
// \O/ // \O/
// V // V
@ -279,19 +304,19 @@ void main() {
float a = op_len; float a = op_len;
float h = cone_len; float h = cone_len;
float a2 = a * a; float a2 = a * a;
float fh2 = 4.0f * h * h; float fh2 = 4.0 * h * h;
blur_radius = (a * (sqrt(a2 + fh2) - a)) / (4.0f * h); blur_radius = (a * (sqrt(a2 + fh2) - a)) / (4.0 * h);
} }
mip_level = clamp(log2(blur_radius * max(params.screen_size.x, params.screen_size.y) / 16.0), 0, params.mipmaps - 1);
} }
imageStore(blur_radius_image, ssC, vec4(blur_radius / 255.0)); //stored in r8 // Because we still write mip level for invalid pixels to allow for smooth roughness transitions,
// this sometimes ends up creating a pyramid-like shape at very rough levels.
#endif // MODE_ROUGH // We can fade the mip level near the end to make it significantly less visible.
mip_level *= pow(clamp(1.25 - ray_len, 0.0, 1.0), 0.2);
} else {
#ifdef MODE_ROUGH
imageStore(blur_radius_image, ssC, vec4(0.0));
#endif
imageStore(ssr_image, ssC, vec4(0.0));
} }
imageStore(output_color, pixel_pos, color);
imageStore(output_mip_level, pixel_pos, vec4(mip_level / 14.0, 0.0, 0.0, 0.0));
} }

View file

@ -0,0 +1,58 @@
#[compute]
#version 450
#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_depth;
layout(set = 0, binding = 1) uniform sampler2D source_normal_roughness;
layout(r32f, set = 0, binding = 2) uniform restrict writeonly image2D dest_depth;
layout(rgba8, set = 0, binding = 3) uniform restrict writeonly image2D dest_normal_roughness;
layout(push_constant, std430) uniform Params {
ivec2 screen_size;
}
params;
void get_sample(ivec2 sample_pos, inout float depth, inout ivec2 winner_sample_pos) {
float sample_depth = texelFetch(source_depth, sample_pos, 0).x;
if (depth < sample_depth) {
depth = sample_depth;
winner_sample_pos = sample_pos;
}
}
void main() {
ivec2 pixel_pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pixel_pos, params.screen_size))) {
return;
}
ivec2 sample_pos = pixel_pos * 2 + ivec2(0, 0);
float depth = texelFetch(source_depth, sample_pos, 0).x;
get_sample(pixel_pos * 2 + ivec2(1, 0), depth, sample_pos);
get_sample(pixel_pos * 2 + ivec2(0, 1), depth, sample_pos);
get_sample(pixel_pos * 2 + ivec2(1, 1), depth, sample_pos);
#ifdef MODE_ODD_WIDTH
get_sample(pixel_pos * 2 + ivec2(2, 0), depth, sample_pos);
get_sample(pixel_pos * 2 + ivec2(2, 1), depth, sample_pos);
#endif
#ifdef MODE_ODD_HEIGHT
get_sample(pixel_pos * 2 + ivec2(0, 2), depth, sample_pos);
get_sample(pixel_pos * 2 + ivec2(1, 2), depth, sample_pos);
#endif
#if defined(MODE_ODD_WIDTH) && defined(MODE_ODD_HEIGHT)
get_sample(pixel_pos * 2 + ivec2(2, 2), depth, sample_pos);
#endif
imageStore(dest_depth, pixel_pos, vec4(depth, 0.0, 0.0, 0.0));
imageStore(dest_normal_roughness, pixel_pos, texelFetch(source_normal_roughness, sample_pos, 0));
}

View file

@ -6,143 +6,125 @@
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_ssr; layout(set = 0, binding = 0) uniform sampler2D source;
layout(r8, set = 0, binding = 1) uniform restrict readonly image2D source_radius; layout(set = 0, binding = 1) uniform restrict writeonly image2D dest;
layout(rgba8, set = 1, binding = 0) uniform restrict readonly image2D source_normal;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly image2D dest_ssr;
#ifndef VERTICAL_PASS
layout(r8, set = 2, binding = 1) uniform restrict writeonly image2D dest_radius;
#endif
layout(r32f, set = 3, binding = 0) uniform restrict readonly image2D source_depth;
layout(push_constant, std430) uniform Params { layout(push_constant, std430) uniform Params {
vec4 proj_info;
bool orthogonal;
float edge_tolerance;
int increment;
uint view_index;
ivec2 screen_size; ivec2 screen_size;
bool vertical; uint mip_level;
uint steps;
} }
params; params;
#include "screen_space_reflection_inc.glsl" shared vec4 cache[16][16];
#define GAUSS_TABLE_SIZE 15 const float WEIGHTS[7] = float[7](
0.07130343198685299,
0.1315141208431224,
0.18987923288883812,
0.21460642856237303,
0.18987923288883812,
0.1315141208431224,
0.07130343198685299);
const float gauss_table[GAUSS_TABLE_SIZE + 1] = float[]( float get_weight(vec4 c) {
0.1847392078702266, return mix(clamp(params.mip_level * 0.2, 0.0, 1.0), 1.0, c.a);
0.16595854345772326,
0.12031364177766891,
0.07038755277896766,
0.03322925565155569,
0.012657819729901945,
0.0038903040680094217,
0.0009646503390864025,
0.00019297087402915717,
0.000031139936308099136,
0.000004053309048174758,
4.255228059965837e-7,
3.602517634249573e-8,
2.4592560765896795e-9,
1.3534945386863618e-10,
0.0 //one more for interpolation
);
float gauss_weight(float p_val) {
float idxf;
float c = modf(max(0.0, p_val * float(GAUSS_TABLE_SIZE)), idxf);
int idx = int(idxf);
if (idx >= GAUSS_TABLE_SIZE + 1) {
return 0.0;
}
return mix(gauss_table[idx], gauss_table[idx + 1], c);
} }
#define M_PI 3.14159265359 vec4 apply_gaus_horz(ivec2 local) {
vec4 c0 = cache[local.x - 3][local.y];
float w0 = WEIGHTS[0] * get_weight(c0);
void do_filter(inout vec4 accum, inout float accum_radius, inout float divisor, ivec2 texcoord, ivec2 increment, vec3 p_pos, vec3 normal, float p_limit_radius) { vec4 c1 = cache[local.x - 2][local.y];
for (int i = 1; i < params.steps; i++) { float w1 = WEIGHTS[1] * get_weight(c1);
float d = float(i * params.increment);
ivec2 tc = texcoord + increment * i;
float depth = imageLoad(source_depth, tc).r;
vec3 view_pos = reconstructCSPosition(vec2(tc) + 0.5, depth);
vec3 view_normal = normalize(imageLoad(source_normal, tc).rgb * 2.0 - 1.0);
view_normal.y = -view_normal.y;
float r = imageLoad(source_radius, tc).r; vec4 c2 = cache[local.x - 1][local.y];
float radius = round(r * 255.0); float w2 = WEIGHTS[2] * get_weight(c2);
float angle_n = 1.0 - abs(dot(normal, view_normal)); vec4 c3 = cache[local.x][local.y];
if (angle_n > params.edge_tolerance) { float w3 = WEIGHTS[3] * get_weight(c3);
break;
}
float angle = abs(dot(normal, normalize(view_pos - p_pos))); vec4 c4 = cache[local.x + 1][local.y];
float w4 = WEIGHTS[4] * get_weight(c4);
if (angle > params.edge_tolerance) { vec4 c5 = cache[local.x + 2][local.y];
break; float w5 = WEIGHTS[5] * get_weight(c5);
}
if (d < radius) { vec4 c6 = cache[local.x + 3][local.y];
float w = gauss_weight(d / radius); float w6 = WEIGHTS[6] * get_weight(c6);
accum += imageLoad(source_ssr, tc) * w;
#ifndef VERTICAL_PASS vec4 c = c0 * w0 + c1 * w1 + c2 * w2 + c3 * w3 + c4 * w4 + c5 * w5 + c6 * w6;
accum_radius += r * w; float w = w0 + w1 + w2 + w3 + w4 + w5 + w6;
#endif
divisor += w; if (w > 0.0) {
} c /= w;
} else {
c = vec4(0.0);
} }
return c;
}
shared vec4 temp_cache[8][16];
vec4 apply_gaus_vert(ivec2 local) {
vec4 c0 = temp_cache[local.x][local.y - 3];
float w0 = WEIGHTS[0] * get_weight(c0);
vec4 c1 = temp_cache[local.x][local.y - 2];
float w1 = WEIGHTS[1] * get_weight(c1);
vec4 c2 = temp_cache[local.x][local.y - 1];
float w2 = WEIGHTS[2] * get_weight(c2);
vec4 c3 = temp_cache[local.x][local.y];
float w3 = WEIGHTS[3] * get_weight(c3);
vec4 c4 = temp_cache[local.x][local.y + 1];
float w4 = WEIGHTS[4] * get_weight(c4);
vec4 c5 = temp_cache[local.x][local.y + 2];
float w5 = WEIGHTS[5] * get_weight(c5);
vec4 c6 = temp_cache[local.x][local.y + 3];
float w6 = WEIGHTS[6] * get_weight(c6);
vec4 c = c0 * w0 + c1 * w1 + c2 * w2 + c3 * w3 + c4 * w4 + c5 * w5 + c6 * w6;
float w = w0 + w1 + w2 + w3 + w4 + w5 + w6;
if (w > 0.0) {
c /= w;
} else {
c = vec4(0.0);
}
return c;
}
vec4 get_sample(ivec2 pixel_pos) {
return textureLod(source, (vec2(pixel_pos) + 0.5) / params.screen_size, 0);
} }
void main() { void main() {
// Pixel being shaded ivec2 global = ivec2(gl_GlobalInvocationID.xy);
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy); ivec2 local = ivec2(gl_LocalInvocationID.xy);
if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing cache[local.x * 2 + 0][local.y * 2 + 0] = get_sample(global + local - 4 + ivec2(0, 0));
cache[local.x * 2 + 1][local.y * 2 + 0] = get_sample(global + local - 4 + ivec2(1, 0));
cache[local.x * 2 + 0][local.y * 2 + 1] = get_sample(global + local - 4 + ivec2(0, 1));
cache[local.x * 2 + 1][local.y * 2 + 1] = get_sample(global + local - 4 + ivec2(1, 1));
memoryBarrierShared();
barrier();
temp_cache[local.x][local.y * 2 + 0] = apply_gaus_horz(ivec2(local.x + 4, local.y * 2 + 0));
temp_cache[local.x][local.y * 2 + 1] = apply_gaus_horz(ivec2(local.x + 4, local.y * 2 + 1));
memoryBarrierShared();
barrier();
if (any(greaterThanEqual(global, params.screen_size))) {
return; return;
} }
float base_contrib = gauss_table[0]; imageStore(dest, global, apply_gaus_vert(local + ivec2(0, 4)));
vec4 accum = imageLoad(source_ssr, ssC);
float accum_radius = imageLoad(source_radius, ssC).r;
float radius = accum_radius * 255.0;
float divisor = gauss_table[0];
accum *= divisor;
accum_radius *= divisor;
#ifdef VERTICAL_PASS
ivec2 direction = ivec2(0, params.increment);
#else
ivec2 direction = ivec2(params.increment, 0);
#endif
float depth = imageLoad(source_depth, ssC).r;
vec3 pos = reconstructCSPosition(vec2(ssC.xy) + 0.5, depth);
vec3 normal = imageLoad(source_normal, ssC).xyz * 2.0 - 1.0;
normal = normalize(normal);
normal.y = -normal.y;
do_filter(accum, accum_radius, divisor, ssC.xy, direction, pos, normal, radius);
do_filter(accum, accum_radius, divisor, ssC.xy, -direction, pos, normal, radius);
if (divisor > 0.0) {
accum /= divisor;
accum_radius /= divisor;
} else {
accum = vec4(0.0);
accum_radius = 0.0;
}
imageStore(dest_ssr, ssC, accum);
#ifndef VERTICAL_PASS
imageStore(dest_radius, ssC, vec4(accum_radius));
#endif
} }

View file

@ -0,0 +1,44 @@
#[compute]
#version 450
#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source;
layout(r32f, set = 0, binding = 1) uniform restrict writeonly image2D dest;
layout(push_constant, std430) uniform Params {
ivec2 screen_size;
}
params;
void main() {
ivec2 pixel_pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pixel_pos, params.screen_size))) {
return;
}
float depth = texelFetch(source, pixel_pos * 2 + ivec2(0, 0), 0).x;
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(1, 0), 0).x);
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(0, 1), 0).x);
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(1, 1), 0).x);
#ifdef MODE_ODD_WIDTH
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(2, 0), 0).x);
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(2, 1), 0).x);
#endif
#ifdef MODE_ODD_HEIGHT
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(0, 2), 0).x);
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(1, 2), 0).x);
#endif
#if defined(MODE_ODD_WIDTH) && defined(MODE_ODD_HEIGHT)
depth = max(depth, texelFetch(source, pixel_pos * 2 + ivec2(2, 2), 0).x);
#endif
imageStore(dest, pixel_pos, vec4(depth, 0.0, 0.0, 0.0));
}

View file

@ -1,28 +0,0 @@
layout(constant_id = 0) const bool sc_multiview = false;
layout(set = 4, binding = 0, std140) uniform SceneData {
mat4x4 projection[2];
mat4x4 inv_projection[2];
vec4 eye_offset[2];
}
scene_data;
vec3 reconstructCSPosition(vec2 screen_pos, float z) {
if (sc_multiview) {
vec4 pos;
pos.xy = (2.0 * vec2(screen_pos) / vec2(params.screen_size)) - 1.0;
pos.z = z * 2.0 - 1.0;
pos.w = 1.0;
pos = scene_data.inv_projection[params.view_index] * pos;
pos.xyz /= pos.w;
return pos.xyz;
} else {
if (params.orthogonal) {
return vec3(-(screen_pos.xy * params.proj_info.xy + params.proj_info.zw), z);
} else {
return vec3((screen_pos.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
}
}
}

View file

@ -0,0 +1,98 @@
#[compute]
#version 450
#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_depth;
layout(set = 0, binding = 1) uniform sampler2D source_normal_roughness;
layout(set = 0, binding = 2) uniform sampler2D source_depth_half;
layout(set = 0, binding = 3) uniform sampler2D source_normal_roughness_half;
layout(set = 0, binding = 4) uniform sampler2D source_color;
layout(set = 0, binding = 5) uniform sampler2D source_mip_level;
layout(rgba16f, set = 0, binding = 6) uniform restrict writeonly image2D output_color;
layout(push_constant, std430) uniform Params {
ivec2 screen_size;
}
params;
void get_sample(float depth, vec3 normal, float roughness, ivec2 pixel_pos, out vec4 color, out float weight) {
float sample_depth = texelFetch(source_depth_half, pixel_pos, 0).x;
vec4 sample_normal_roughness = texelFetch(source_normal_roughness_half, pixel_pos, 0);
vec3 sample_normal = normalize(sample_normal_roughness.xyz * 2.0 - 1.0);
float sample_roughness = sample_normal_roughness.w;
if (sample_roughness > 0.5) {
sample_roughness = 1.0 - sample_roughness;
}
sample_roughness /= (127.0 / 255.0);
vec2 uv = (pixel_pos + 0.5) / (params.screen_size * 0.5);
float mip_level = texelFetch(source_mip_level, pixel_pos, 0).x * 14.0;
color = textureLod(source_color, uv, mip_level);
// Invert the tone mapping we applied in the main trace pass.
const vec3 rec709_luminance_weights = vec3(0.2126, 0.7152, 0.0722);
color.rgb /= 1.0 - dot(color.rgb, rec709_luminance_weights);
const float DEPTH_FACTOR = 2048.0;
const float NORMAL_FACTOR = 32.0;
const float ROUGHNESS_FACTOR = 16.0;
float depth_diff = abs(depth - sample_depth);
float weight_depth = exp(-depth_diff * DEPTH_FACTOR);
float normal_diff = clamp(1.0 - dot(normal, sample_normal), 0.0, 1.0);
float weight_normal = exp(-normal_diff * NORMAL_FACTOR);
float roughness_diff = abs(roughness - sample_roughness);
float weight_roughness = exp(-roughness_diff * ROUGHNESS_FACTOR);
weight = weight_depth * weight_normal * weight_roughness;
}
void main() {
ivec2 pixel_pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pixel_pos, params.screen_size))) {
return;
}
float depth = texelFetch(source_depth, pixel_pos, 0).x;
vec4 normal_roughness = texelFetch(source_normal_roughness, pixel_pos, 0);
vec3 normal = normalize(normal_roughness.xyz * 2.0 - 1.0);
float roughness = normal_roughness.w;
if (roughness > 0.5) {
roughness = 1.0 - roughness;
}
roughness /= (127.0 / 255.0);
vec2 half_tex_coord = (pixel_pos + 0.5) * 0.5;
vec2 bilinear_weights = fract(half_tex_coord);
vec4 color0, color1, color2, color3;
float weight0, weight1, weight2, weight3;
get_sample(depth, normal, roughness, ((pixel_pos - 1) / 2) + ivec2(0, 0), color0, weight0);
get_sample(depth, normal, roughness, ((pixel_pos - 1) / 2) + ivec2(1, 0), color1, weight1);
get_sample(depth, normal, roughness, ((pixel_pos - 1) / 2) + ivec2(0, 1), color2, weight2);
get_sample(depth, normal, roughness, ((pixel_pos - 1) / 2) + ivec2(1, 1), color3, weight3);
weight0 *= bilinear_weights.x * bilinear_weights.y;
weight1 *= (1.0 - bilinear_weights.x) * bilinear_weights.y;
weight2 *= bilinear_weights.x * (1.0 - bilinear_weights.y);
weight3 *= (1.0 - bilinear_weights.x) * (1.0 - bilinear_weights.y);
vec4 result_color = color0 * weight0 + color1 * weight1 + color2 * weight2 + color3 * weight3;
float result_weight = weight0 + weight1 + weight2 + weight3;
if (result_weight > 0.0) {
result_color /= result_weight;
} else {
result_color = vec4(0.0);
}
imageStore(output_color, pixel_pos, result_color);
}

View file

@ -1,112 +0,0 @@
#[compute]
#version 450
#VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
/* Specialization Constants (Toggles) */
layout(constant_id = 0) const bool sc_multiview = false;
/* inputs */
layout(set = 0, binding = 0) uniform sampler2D source_ssr;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
layout(set = 1, binding = 1) uniform sampler2D source_normal;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly image2D dest_ssr;
layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_depth;
layout(rgba8, set = 3, binding = 1) uniform restrict writeonly image2D dest_normal;
layout(push_constant, std430) uniform Params {
ivec2 screen_size;
float camera_z_near;
float camera_z_far;
bool orthogonal;
bool filtered;
uint pad[2];
}
params;
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing
return;
}
//do not filter, SSR will generate artifacts if this is done
float divisor = 0.0;
vec4 color;
float depth;
vec4 normal;
if (params.filtered) {
color = vec4(0.0);
depth = 0.0;
normal = vec4(0.0);
for (int i = 0; i < 4; i++) {
ivec2 ofs = ssC << 1;
if (bool(i & 1)) {
ofs.x += 1;
}
if (bool(i & 2)) {
ofs.y += 1;
}
color += texelFetch(source_ssr, ofs, 0);
float d = texelFetch(source_depth, ofs, 0).r;
vec4 nr = texelFetch(source_normal, ofs, 0);
normal.xyz += normalize(nr.xyz * 2.0 - 1.0);
float roughness = normal.w;
if (roughness > 0.5) {
roughness = 1.0 - roughness;
}
roughness /= (127.0 / 255.0);
normal.w += roughness;
if (sc_multiview) {
// we're doing a full unproject so we need the value as is.
depth += d;
} else {
// unproject our Z value so we can use it directly.
d = d * 2.0 - 1.0;
if (params.orthogonal) {
d = ((d + (params.camera_z_far + params.camera_z_near) / (params.camera_z_far - params.camera_z_near)) * (params.camera_z_far - params.camera_z_near)) / 2.0;
} else {
d = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - d * (params.camera_z_far - params.camera_z_near));
}
depth += -d;
}
}
color /= 4.0;
depth /= 4.0;
normal.xyz = normalize(normal.xyz / 4.0) * 0.5 + 0.5;
normal.w /= 4.0;
normal.w = normal.w * (127.0 / 255.0);
} else {
ivec2 ofs = ssC << 1;
color = texelFetch(source_ssr, ofs, 0);
depth = texelFetch(source_depth, ofs, 0).r;
normal = texelFetch(source_normal, ofs, 0);
if (!sc_multiview) {
// unproject our Z value so we can use it directly.
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
depth = -(depth * (params.camera_z_far - params.camera_z_near) - (params.camera_z_far + params.camera_z_near)) / 2.0;
} else {
depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near + depth * (params.camera_z_far - params.camera_z_near));
}
depth = -depth;
}
}
imageStore(dest_ssr, ssC, color);
imageStore(dest_depth, ssC, vec4(depth));
imageStore(dest_normal, ssC, normal);
}

View file

@ -1597,6 +1597,12 @@ void fragment_shader(in SceneData scene_data) {
float kernelRoughness2 = min(2.0 * variance, scene_data.roughness_limiter_limit); //limit effect float kernelRoughness2 = min(2.0 * variance, scene_data.roughness_limiter_limit); //limit effect
float filteredRoughness2 = min(1.0, roughness2 + kernelRoughness2); float filteredRoughness2 = min(1.0, roughness2 + kernelRoughness2);
roughness = sqrt(filteredRoughness2); roughness = sqrt(filteredRoughness2);
// Reject very small roughness values. Lack of precision can collapse
// roughness^4 to 0 in GGX specular equations and cause divisions by zero.
if (roughness < 0.00000001) {
roughness = 0.0;
}
} }
#endif #endif
//apply energy conservation //apply energy conservation
@ -2029,6 +2035,39 @@ void fragment_shader(in SceneData scene_data) {
#endif #endif
} }
//process ssr
if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSR)) {
bool resolve_ssr = bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_RESOLVE_SSR);
float ssr_mip_level = 0.0;
if (resolve_ssr) {
#ifdef USE_MULTIVIEW
ssr_mip_level = textureLod(sampler2DArray(ssr_mip_level_buffer, SAMPLER_NEAREST_CLAMP), vec3(screen_uv, ViewIndex), 0.0).x;
#else
ssr_mip_level = textureLod(sampler2D(ssr_mip_level_buffer, SAMPLER_NEAREST_CLAMP), screen_uv, 0.0).x;
#endif // USE_MULTIVIEW
ssr_mip_level *= 14.0;
}
#ifdef USE_MULTIVIEW
vec4 ssr = textureLod(sampler2DArray(ssr_buffer, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(screen_uv, ViewIndex), ssr_mip_level);
#else
vec4 ssr = textureLod(sampler2D(ssr_buffer, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), screen_uv, ssr_mip_level);
#endif // USE_MULTIVIEW
if (resolve_ssr) {
const vec3 rec709_luminance_weights = vec3(0.2126, 0.7152, 0.0722);
ssr.rgb /= 1.0 - dot(ssr.rgb, rec709_luminance_weights);
}
// Apply fade when approaching 0.7 roughness to smoothen the harsh cutoff in the main SSR trace pass.
ssr *= smoothstep(0.0, 1.0, 1.0 - clamp((roughness - 0.6) / (0.7 - 0.6), 0.0, 1.0));
// Alpha is premultiplied.
indirect_specular_light = indirect_specular_light * (1.0 - ssr.a) + ssr.rgb;
}
//finalize ambient light here //finalize ambient light here
{ {
ambient_light *= ao; ambient_light *= ao;

View file

@ -164,8 +164,10 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler;
//3 bits of stride //3 bits of stride
#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF #define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO 1 #define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO (1 << 0)
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL 2 #define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL (1 << 1)
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSR (1 << 2)
#define SCREEN_SPACE_EFFECTS_FLAGS_RESOLVE_SSR (1 << 3)
layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights { layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights {
LightData data[]; LightData data[];
@ -435,8 +437,12 @@ layout(set = 1, binding = 33) uniform texture3D volumetric_fog_texture;
#ifdef USE_MULTIVIEW #ifdef USE_MULTIVIEW
layout(set = 1, binding = 34) uniform texture2DArray ssil_buffer; layout(set = 1, binding = 34) uniform texture2DArray ssil_buffer;
layout(set = 1, binding = 35) uniform texture2DArray ssr_buffer;
layout(set = 1, binding = 36) uniform texture2DArray ssr_mip_level_buffer;
#else #else
layout(set = 1, binding = 34) uniform texture2D ssil_buffer; layout(set = 1, binding = 34) uniform texture2D ssil_buffer;
layout(set = 1, binding = 35) uniform texture2D ssr_buffer;
layout(set = 1, binding = 36) uniform texture2D ssr_mip_level_buffer;
#endif // USE_MULTIVIEW #endif // USE_MULTIVIEW
#endif #endif

View file

@ -1311,6 +1311,7 @@ public:
PASS1RC(float, environment_get_ssr_fade_out, RID) PASS1RC(float, environment_get_ssr_fade_out, RID)
PASS1RC(float, environment_get_ssr_depth_tolerance, RID) PASS1RC(float, environment_get_ssr_depth_tolerance, RID)
PASS1(environment_set_ssr_half_size, bool)
PASS1(environment_set_ssr_roughness_quality, RS::EnvironmentSSRRoughnessQuality) PASS1(environment_set_ssr_roughness_quality, RS::EnvironmentSSRRoughnessQuality)
// SSAO // SSAO

View file

@ -202,6 +202,7 @@ public:
float environment_get_ssr_fade_out(RID p_env) const; float environment_get_ssr_fade_out(RID p_env) const;
float environment_get_ssr_depth_tolerance(RID p_env) const; float environment_get_ssr_depth_tolerance(RID p_env) const;
virtual void environment_set_ssr_half_size(bool p_half_size) = 0;
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) = 0; virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) = 0;
// SSAO // SSAO

View file

@ -260,6 +260,7 @@ public:
virtual float environment_get_ssr_fade_out(RID p_env) const = 0; virtual float environment_get_ssr_fade_out(RID p_env) const = 0;
virtual float environment_get_ssr_depth_tolerance(RID p_env) const = 0; virtual float environment_get_ssr_depth_tolerance(RID p_env) const = 0;
virtual void environment_set_ssr_half_size(bool p_half_size) = 0;
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) = 0; virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) = 0;
// SSAO // SSAO

View file

@ -3053,6 +3053,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog", "env", "enable", "density", "albedo", "emission", "emission_energy", "anisotropy", "length", "p_detail_spread", "gi_inject", "temporal_reprojection", "temporal_reprojection_amount", "ambient_inject", "sky_affect"), &RenderingServer::environment_set_volumetric_fog); ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog", "env", "enable", "density", "albedo", "emission", "emission_energy", "anisotropy", "length", "p_detail_spread", "gi_inject", "temporal_reprojection", "temporal_reprojection_amount", "ambient_inject", "sky_affect"), &RenderingServer::environment_set_volumetric_fog);
ClassDB::bind_method(D_METHOD("environment_glow_set_use_bicubic_upscale", "enable"), &RenderingServer::environment_glow_set_use_bicubic_upscale); ClassDB::bind_method(D_METHOD("environment_glow_set_use_bicubic_upscale", "enable"), &RenderingServer::environment_glow_set_use_bicubic_upscale);
ClassDB::bind_method(D_METHOD("environment_set_ssr_half_size", "half_size"), &RenderingServer::environment_set_ssr_half_size);
ClassDB::bind_method(D_METHOD("environment_set_ssr_roughness_quality", "quality"), &RenderingServer::environment_set_ssr_roughness_quality); ClassDB::bind_method(D_METHOD("environment_set_ssr_roughness_quality", "quality"), &RenderingServer::environment_set_ssr_roughness_quality);
ClassDB::bind_method(D_METHOD("environment_set_ssao_quality", "quality", "half_size", "adaptive_target", "blur_passes", "fadeout_from", "fadeout_to"), &RenderingServer::environment_set_ssao_quality); ClassDB::bind_method(D_METHOD("environment_set_ssao_quality", "quality", "half_size", "adaptive_target", "blur_passes", "fadeout_from", "fadeout_to"), &RenderingServer::environment_set_ssao_quality);
ClassDB::bind_method(D_METHOD("environment_set_ssil_quality", "quality", "half_size", "adaptive_target", "blur_passes", "fadeout_from", "fadeout_to"), &RenderingServer::environment_set_ssil_quality); ClassDB::bind_method(D_METHOD("environment_set_ssil_quality", "quality", "half_size", "adaptive_target", "blur_passes", "fadeout_from", "fadeout_to"), &RenderingServer::environment_set_ssil_quality);
@ -3732,7 +3733,7 @@ void RenderingServer::init() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/glow/upscale_mode", PROPERTY_HINT_ENUM, "Linear (Fast),Bicubic (Slow)"), 1); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/glow/upscale_mode", PROPERTY_HINT_ENUM, "Linear (Fast),Bicubic (Slow)"), 1);
GLOBAL_DEF("rendering/environment/glow/upscale_mode.mobile", 0); GLOBAL_DEF("rendering/environment/glow/upscale_mode.mobile", 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/screen_space_reflection/roughness_quality", PROPERTY_HINT_ENUM, "Disabled (Fastest),Low (Fast),Medium (Average),High (Slow)"), 1); GLOBAL_DEF("rendering/environment/screen_space_reflection/half_size", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/subsurface_scattering/subsurface_scattering_quality", PROPERTY_HINT_ENUM, "Disabled (Fastest),Low (Fast),Medium (Average),High (Slow)"), 1); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/subsurface_scattering/subsurface_scattering_quality", PROPERTY_HINT_ENUM, "Disabled (Fastest),Low (Fast),Medium (Average),High (Slow)"), 1);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.05); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.05);

View file

@ -1287,6 +1287,8 @@ public:
virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance) = 0; virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance) = 0;
virtual void environment_set_ssr_half_size(bool p_half_size) = 0;
enum EnvironmentSSRRoughnessQuality { enum EnvironmentSSRRoughnessQuality {
ENV_SSR_ROUGHNESS_QUALITY_DISABLED, ENV_SSR_ROUGHNESS_QUALITY_DISABLED,
ENV_SSR_ROUGHNESS_QUALITY_LOW, ENV_SSR_ROUGHNESS_QUALITY_LOW,

View file

@ -817,6 +817,7 @@ public:
FUNC2(environment_set_camera_feed_id, RID, int) FUNC2(environment_set_camera_feed_id, RID, int)
FUNC6(environment_set_ssr, RID, bool, int, float, float, float) FUNC6(environment_set_ssr, RID, bool, int, float, float, float)
FUNC1(environment_set_ssr_half_size, bool)
FUNC1(environment_set_ssr_roughness_quality, EnvironmentSSRRoughnessQuality) FUNC1(environment_set_ssr_roughness_quality, EnvironmentSSRRoughnessQuality)
FUNC10(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, float) FUNC10(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, float)

View file

@ -578,7 +578,7 @@ float RendererEnvironmentStorage::environment_get_ssr_fade_out(RID p_env) const
float RendererEnvironmentStorage::environment_get_ssr_depth_tolerance(RID p_env) const { float RendererEnvironmentStorage::environment_get_ssr_depth_tolerance(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.2); ERR_FAIL_NULL_V(env, 0.5);
return env->ssr_depth_tolerance; return env->ssr_depth_tolerance;
} }

View file

@ -114,7 +114,7 @@ private:
int ssr_max_steps = 64; int ssr_max_steps = 64;
float ssr_fade_in = 0.15; float ssr_fade_in = 0.15;
float ssr_fade_out = 2.0; float ssr_fade_out = 2.0;
float ssr_depth_tolerance = 0.2; float ssr_depth_tolerance = 0.5;
// SSAO // SSAO
bool ssao_enabled = false; bool ssao_enabled = false;