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

@ -120,6 +120,7 @@ public:
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_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 "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/render_scene_buffers_rd.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
@ -277,62 +278,67 @@ SSEffects::SSEffects() {
}
// 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;
sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;
sc.constant_id = 0; // SSR_USE_FULL_PROJECTION_MATRIX
sc.bool_value = false;
specialization_constants.push_back(sc);
Vector<String> ssr_downsample_modes;
ssr_downsample_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_DEFAULT
ssr_downsample_modes.push_back("\n#define MODE_ODD_WIDTH\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH
ssr_downsample_modes.push_back("\n#define MODE_ODD_HEIGHT\n"); // SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_HEIGHT
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;
ssr_scale_modes.push_back("\n");
Vector<String> ssr_hiz_modes;
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_scale.shader_version = ssr_scale.shader.version_create();
ssr.hiz_shader.initialize(ssr_hiz_modes);
ssr.hiz_shader_version = ssr.hiz_shader.version_create();
for (int v = 0; v < SSR_VARIATIONS; v++) {
specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false;
ssr_scale.pipelines[v].create_compute_pipeline(ssr_scale.shader.version_get_shader(ssr_scale.shader_version, 0), specialization_constants);
for (uint32_t i = 0; i < SCREEN_SPACE_REFLECTION_HIZ_MAX; i++) {
ssr.hiz_pipelines[i].create_compute_pipeline(ssr.hiz_shader.version_get_shader(ssr.hiz_shader_version, i));
}
}
{
Vector<String> ssr_modes;
ssr_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_NORMAL
ssr_modes.push_back("\n#define MODE_ROUGH\n"); // SCREEN_SPACE_REFLECTION_ROUGH
ssr_modes.push_back("\n");
ssr.shader.initialize(ssr_modes);
ssr.shader_version = ssr.shader.version_create();
ssr.ssr_shader.initialize(ssr_modes);
ssr.ssr_shader_version = ssr.ssr_shader.version_create();
for (int v = 0; v < SSR_VARIATIONS; v++) {
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);
}
}
ssr.ssr_pipeline.create_compute_pipeline(ssr.ssr_shader.version_get_shader(ssr.ssr_shader_version, 0));
}
{
Vector<String> ssr_filter_modes;
ssr_filter_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL
ssr_filter_modes.push_back("\n#define VERTICAL_PASS\n"); // SCREEN_SPACE_REFLECTION_FILTER_VERTICAL
ssr_filter_modes.push_back("\n");
ssr_filter.shader.initialize(ssr_filter_modes);
ssr_filter.shader_version = ssr_filter.shader.version_create();
ssr.filter_shader.initialize(ssr_filter_modes);
ssr.filter_shader_version = ssr.filter_shader.version_create();
for (int v = 0; v < SSR_VARIATIONS; v++) {
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);
}
}
ssr.filter_pipeline.create_compute_pipeline(ssr.filter_shader.version_get_shader(ssr.filter_shader_version, 0));
}
{
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() {
{
// Cleanup SS Reflections
for (int v = 0; v < SSR_VARIATIONS; v++) {
for (int i = 0; i < SCREEN_SPACE_REFLECTION_FILTER_MAX; i++) {
ssr.pipelines[v][i].free();
ssr_filter.pipelines[v][i].free();
}
ssr_scale.pipelines[v].free();
for (int i = 0; i < SCREEN_SPACE_REFLECTION_DOWNSAMPLE_MAX; i++) {
ssr.downsample_pipelines[i].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_filter.shader.version_free(ssr_filter.shader_version);
ssr_scale.shader.version_free(ssr_scale.shader_version);
ssr.downsample_shader.version_free(ssr.downsample_shader_version);
ssr.hiz_shader.version_free(ssr.hiz_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()) {
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);
}
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,
// 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");
// 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_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);
@ -1364,43 +1425,56 @@ void SSEffects::generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORe
/* Screen Space Reflection */
void SSEffects::ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) {
ssr_roughness_quality = p_quality;
void SSEffects::ssr_set_half_size(bool p_half_size) {
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) {
if (p_ssr_buffers.roughness_quality != ssr_roughness_quality) {
// Buffers will already be cleared if view count or viewport size has changed, also cleared them if we change roughness.
void SSEffects::ssr_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RD::DataFormat p_color_format) {
if (p_ssr_buffers.half_size != ssr_half_size) {
p_render_buffers->clear_context(RB_SCOPE_SSR);
}
Size2i internal_size = p_render_buffers->get_internal_size();
p_ssr_buffers.size = Size2i(internal_size.x / 2, internal_size.y / 2);
p_ssr_buffers.roughness_quality = ssr_roughness_quality;
Vector2i internal_size = p_render_buffers->get_internal_size();
p_ssr_buffers.size = ssr_half_size ? (internal_size / 2) : internal_size;
// 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);
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 (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
while (cur_width > 1 && cur_height > 1) {
if (cur_width > 1) {
cur_width /= 2;
}
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_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);
p_ssr_buffers.half_size = ssr_half_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();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
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
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));
}
Projection correction;
correction.set_depth_correction(true);
for (uint32_t v = 0; v < view_count; v++) {
store_camera(p_projections[v], scene_data.projection[v]);
store_camera(p_projections[v].inverse(), scene_data.inv_projection[v]);
Projection projection = correction * p_projections[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][1] = p_eye_offsets[v].y;
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);
}
uint32_t pipeline_specialization = 0;
if (view_count > 1) {
pipeline_specialization |= SSR_MULTIVIEW;
}
RID linear_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID nearest_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
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;
}
if (ssr_half_size) {
RD::get_singleton()->draw_command_begin_label("SSR Downsample");
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[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);
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_filter.pipelines[pipeline_specialization][mode].get_rid());
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, 0, u_output, u_blur_radius), 0);
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, 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);
int32_t downsample_mode;
if (is_width_odd && is_height_odd) {
downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH_AND_HEIGHT;
} else if (is_width_odd) {
downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_WIDTH;
} else if (is_height_odd) {
downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_ODD_HEIGHT;
} else {
downsample_mode = SCREEN_SPACE_REFLECTION_DOWNSAMPLE_DEFAULT;
}
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()->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 */

View file

@ -32,8 +32,10 @@
#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_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_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/ssao.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/rendering_server.h"
#define RB_SCOPE_SSLF SNAME("rb_sslf")
#define RB_SCOPE_SSDS SNAME("rb_ssds")
#define RB_SCOPE_SSIL SNAME("rb_ssil")
#define RB_SCOPE_SSAO SNAME("rb_ssao")
@ -60,16 +63,17 @@
#define RB_IMPORTANCE_MAP SNAME("importance_map")
#define RB_IMPORTANCE_PONG SNAME("importance_pong")
#define RB_DEPTH_SCALED SNAME("depth_scaled")
#define RB_NORMAL_SCALED SNAME("normal_scaled")
#define RB_BLUR_RADIUS SNAME("blur_radius")
#define RB_INTERMEDIATE SNAME("intermediate")
#define RB_OUTPUT SNAME("output")
#define RB_NORMAL_ROUGHNESS SNAME("normal_roughness")
#define RB_HIZ SNAME("hiz")
#define RB_SSR SNAME("ssr")
#define RB_MIP_LEVEL SNAME("mip_level")
class RenderSceneBuffersRD;
namespace RendererRD {
class CopyEffects;
class SSEffects {
private:
static SSEffects *singleton;
@ -80,6 +84,11 @@ public:
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 */
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);
/* Screen Space Reflection */
void ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality);
void ssr_set_half_size(bool p_half_size);
struct SSRRenderBuffers {
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 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 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, 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 */
void sss_set_quality(RS::SubSurfaceScatteringQuality p_quality);
@ -167,7 +177,7 @@ private:
float ssil_fadeout_from = 50.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;
float sss_scale = 0.05;
@ -409,99 +419,85 @@ private:
/* Screen Space Reflection */
enum SSRShaderSpecializations {
SSR_MULTIVIEW = 1 << 0,
SSR_VARIATIONS = 2,
enum ScreenSpaceReflectionDownsampleMode {
SCREEN_SPACE_REFLECTION_DOWNSAMPLE_DEFAULT,
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 {
float projection[2][16];
float inv_projection[2][16];
float reprojection[2][16];
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 {
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
float camera_z_near; // 4 - 28
float camera_z_far; // 4 - 32
struct ScreenSpaceReflectionFilterPushConstant {
int32_t screen_size[2];
uint32_t mip_level;
int32_t pad;
};
int32_t num_steps; // 4 - 36
float depth_tolerance; // 4 - 40
float distance_fade; // 4 - 44
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 ScreenSpaceReflectionResolvePushConstant {
int32_t screen_size[2];
int32_t pad[2];
};
struct ScreenSpaceReflection {
ScreenSpaceReflectionShaderRD shader;
RID shader_version;
PipelineDeferredRD pipelines[SSR_VARIATIONS][SCREEN_SPACE_REFLECTION_MAX];
ScreenSpaceReflectionDownsampleShaderRD downsample_shader;
RID downsample_shader_version;
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;
ScreenSpaceReflectionFilterShaderRD filter_shader;
RID filter_shader_version;
PipelineDeferredRD filter_pipeline;
ScreenSpaceReflectionResolveShaderRD resolve_shader;
RID resolve_shader_version;
PipelineDeferredRD resolve_pipeline;
} 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 */
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);
uint32_t ss_flags = 0;
if (p_opaque_render_buffers) {
ss_flags |= environment_get_ssao_enabled(p_render_data->environment) ? 1 : 0;
ss_flags |= environment_get_ssil_enabled(p_render_data->environment) ? 2 : 0;
ss_flags |= environment_get_ssao_enabled(p_render_data->environment) ? (1 << 0) : 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;
} else {
@ -1407,42 +1416,50 @@ void RenderForwardClustered::_process_ssil(Ref<RenderSceneBuffersRD> p_render_bu
Projection correction;
correction.set_depth_correction(true);
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);
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());
if (p_render_buffers->has_texture(RB_SCOPE_SSIL, RB_LAST_FRAME)) {
Size2i size = p_render_buffers->get_internal_size();
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));
Ref<RenderBufferDataForwardClustered> rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
ERR_FAIL_COND(rb_data.is_null());
int width = size.x;
int height = size.y;
for (uint32_t m = 1; m < mipmaps; m++) {
width = MAX(1, width >> 1);
height = MAX(1, height >> 1);
RENDER_TIMESTAMP("Process SSR");
source = dest;
dest = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_LAST_FRAME, v, m);
ss_effects->ssr_allocate_buffers(p_render_buffers, rb_data->ss_effects_data.ssr, _render_buffers_get_color_format());
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
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::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
// 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) {
RENDER_TIMESTAMP("Prepare Depth for SSAO/SSIL");
// 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);
}
}
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");
@ -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) {
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 (!p_render_data->transparent_bg) {
using_separate_specular = true;
using_ssr = true;
color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR;
} else {
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);
}
}
_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");
@ -2279,16 +2275,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
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
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);
@ -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_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");
_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 (scale_type == SCALE_FSR2) {
rb_data->ensure_fsr2(fsr2_effect);
@ -3577,6 +3564,38 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend
u.append_id(texture);
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);
}
@ -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);
}
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_COND(p_quality < RS::EnvironmentSSRRoughnessQuality::ENV_SSR_ROUGHNESS_QUALITY_DISABLED || p_quality > RS::EnvironmentSSRRoughnessQuality::ENV_SSR_ROUGHNESS_QUALITY_HIGH);
ss_effects->ssr_set_roughness_quality(p_quality);
ss_effects->ssr_set_half_size(p_half_size);
}
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) {

View file

@ -102,8 +102,11 @@ public:
ClusterBuilderRD *cluster_builder = nullptr;
struct SSEffectsData {
Projection last_frame_projections[RendererSceneRender::MAX_RENDER_VIEWS];
Transform3D last_frame_transform;
Projection ssil_last_frame_projections[RendererSceneRender::MAX_RENDER_VIEWS];
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::SSAORenderBuffers ssao;
@ -758,9 +761,9 @@ private:
/* Render Scene */
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 _copy_framebuffer_to_ssil(Ref<RenderSceneBuffersRD> p_render_buffers);
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 _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 _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 _copy_framebuffer_to_ss_effects(Ref<RenderSceneBuffersRD> p_render_buffers, bool p_use_ssil, bool p_use_ssr);
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);
/* 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_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 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_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 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(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_diffuse;
layout(r32f, set = 0, binding = 1) uniform restrict readonly image2D source_depth;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D ssr_image;
#ifdef MODE_ROUGH
layout(r8, set = 1, binding = 1) uniform restrict writeonly image2D blur_radius_image;
#endif
layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal_roughness;
layout(set = 3, binding = 0) uniform sampler2D source_metallic;
layout(set = 0, binding = 0) uniform sampler2D source_last_frame;
layout(set = 0, binding = 1) uniform sampler2D source_hiz;
layout(set = 0, binding = 2) uniform sampler2D source_normal_roughness;
layout(rgba16f, set = 0, binding = 3) uniform restrict writeonly image2D output_color;
layout(r8, set = 0, binding = 4) uniform restrict writeonly image2D output_mip_level;
layout(set = 0, binding = 5, std140) uniform SceneData {
mat4 projection[2];
mat4 inv_projection[2];
mat4 reprojection[2];
vec4 eye_offset[2];
}
scene_data;
layout(push_constant, std430) uniform Params {
vec4 proj_info;
ivec2 screen_size;
float camera_z_near;
float camera_z_far;
int mipmaps;
int num_steps;
float depth_tolerance;
float distance_fade;
float curve_fade_in;
float depth_tolerance;
bool orthogonal;
float filter_mipmap_levels;
bool use_half_res;
uint view_index;
int view_index;
}
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) {
vec4 projected = scene_data.projection[params.view_index] * vec4(view_pos, 1.0);
projected.xyz /= projected.w;
projected.xy = projected.xy * 0.5 + 0.5;
w = projected.w;
return projected.xy;
float linearize_depth(float depth) {
vec4 pos = vec4(0.0, 0.0, depth, 1.0);
pos = scene_data.inv_projection[params.view_index] * pos;
return pos.z / pos.w;
}
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
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
ivec2 pixel_pos = 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;
}
vec2 pixel_size = 1.0 / vec2(params.screen_size);
vec2 uv = vec2(ssC.xy) * pixel_size;
vec4 color = vec4(0.0);
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
vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth);
vec4 normal_roughness = imageLoad(source_normal_roughness, ssC);
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);
// 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;
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);
z_from = z_to;
z_to = z / w;
if (depth > z_to) {
// Test if our ray is hitting the "right" side of the surface, if not we're likely self reflecting and should skip.
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));
// Do not compute SSR for rough materials to improve
// performance at the cost of subtle artifacting.
if (roughness >= 0.7) {
imageStore(output_color, pixel_pos, vec4(0.0));
imageStore(output_mip_level, pixel_pos, vec4(0.0));
return;
}
{
//blend fading out towards inner margin
// 0.5 = midpoint of reflection
vec2 margin_grad = mix(params.screen_size - pos, pos, lessThan(pos, params.screen_size * 0.5));
margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y);
//margin_blend = 1.0;
vec3 geom_normal = normalize(compute_geometric_normal(pixel_pos, screen_pos.z, pos, 0.5));
// Add a small bias towards the geometry normal to prevent self intersections.
pos += geom_normal * (1.0 - pow(clamp(dot(normal, geom_normal), 0.0, 1.0), 8.0));
screen_pos = compute_screen_pos(pos);
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
float grad = (steps_taken + 1.0) / float(params.num_steps);
float initial_fade = 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 grad = ray_len;
float fade_in = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in);
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,
// strong specular light can leak through the reflection.
@ -235,41 +278,23 @@ void main() {
fade = 1.0;
}
// This is an ad-hoc term to fade out the SSR as roughness increases. Values used
// are meant to match the visual appearance of a ReflectionProbe.
float roughness_fade = smoothstep(0.4, 0.7, 1.0 - roughness);
validity *= fade * margin_blend;
// Schlick term.
float metallic = texelFetch(source_metallic, ssC << 1, 0).w;
if (validity > 0.0) {
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).
// Dielectric materials have a widely accepted default value of 0.04. We assume that metals reflect all light, so their F0 is 1.0.
float f0 = mix(0.04, 1.0, metallic);
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;
// Tone map the SSR color to have smoother roughness filtering across samples with varying luminance.
const vec3 rec709_luminance_weights = vec3(0.2126, 0.7152, 0.0722);
color.rgb /= 1.0 + dot(color.rgb, rec709_luminance_weights);
}
if (roughness > 0.001) {
float cone_angle = min(roughness, 0.999) * M_PI * 0.5;
float cone_len = length(final_pos - line_begin);
float op_len = 2.0 * tan(cone_angle) * cone_len; // opposite side of iso triangle
float cone_len = ray_len;
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/
// V
@ -279,19 +304,19 @@ void main() {
float a = op_len;
float h = cone_len;
float a2 = a * a;
float fh2 = 4.0f * h * h;
blur_radius = (a * (sqrt(a2 + fh2) - a)) / (4.0f * h);
float fh2 = 4.0 * h * 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
#endif // MODE_ROUGH
} else {
#ifdef MODE_ROUGH
imageStore(blur_radius_image, ssC, vec4(0.0));
#endif
imageStore(ssr_image, ssC, vec4(0.0));
// 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.
// 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);
}
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(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_ssr;
layout(r8, set = 0, binding = 1) uniform restrict readonly image2D source_radius;
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(set = 0, binding = 0) uniform sampler2D source;
layout(set = 0, binding = 1) uniform restrict writeonly image2D dest;
layout(push_constant, std430) uniform Params {
vec4 proj_info;
bool orthogonal;
float edge_tolerance;
int increment;
uint view_index;
ivec2 screen_size;
bool vertical;
uint steps;
uint mip_level;
}
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[](
0.1847392078702266,
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);
float get_weight(vec4 c) {
return mix(clamp(params.mip_level * 0.2, 0.0, 1.0), 1.0, c.a);
}
#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) {
for (int i = 1; i < params.steps; i++) {
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;
vec4 c1 = cache[local.x - 2][local.y];
float w1 = WEIGHTS[1] * get_weight(c1);
float r = imageLoad(source_radius, tc).r;
float radius = round(r * 255.0);
vec4 c2 = cache[local.x - 1][local.y];
float w2 = WEIGHTS[2] * get_weight(c2);
float angle_n = 1.0 - abs(dot(normal, view_normal));
if (angle_n > params.edge_tolerance) {
break;
}
vec4 c3 = cache[local.x][local.y];
float w3 = WEIGHTS[3] * get_weight(c3);
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) {
break;
}
vec4 c5 = cache[local.x + 2][local.y];
float w5 = WEIGHTS[5] * get_weight(c5);
if (d < radius) {
float w = gauss_weight(d / radius);
accum += imageLoad(source_ssr, tc) * w;
#ifndef VERTICAL_PASS
accum_radius += r * w;
#endif
divisor += w;
}
vec4 c6 = cache[local.x + 3][local.y];
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;
}
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() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
ivec2 global = 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;
}
float base_contrib = gauss_table[0];
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
imageStore(dest, global, apply_gaus_vert(local + ivec2(0, 4)));
}

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 filteredRoughness2 = min(1.0, roughness2 + kernelRoughness2);
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
//apply energy conservation
@ -2029,6 +2035,39 @@ void fragment_shader(in SceneData scene_data) {
#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
{
ambient_light *= ao;

View file

@ -164,8 +164,10 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler;
//3 bits of stride
#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO 1
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL 2
#define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO (1 << 0)
#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 {
LightData data[];
@ -435,8 +437,12 @@ layout(set = 1, binding = 33) uniform texture3D volumetric_fog_texture;
#ifdef USE_MULTIVIEW
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
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

View file

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

View file

@ -202,6 +202,7 @@ public:
float environment_get_ssr_fade_out(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;
// 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_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;
// 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_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_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);
@ -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("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::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_half_size(bool p_half_size) = 0;
enum EnvironmentSSRRoughnessQuality {
ENV_SSR_ROUGHNESS_QUALITY_DISABLED,
ENV_SSR_ROUGHNESS_QUALITY_LOW,

View file

@ -817,6 +817,7 @@ public:
FUNC2(environment_set_camera_feed_id, RID, int)
FUNC6(environment_set_ssr, RID, bool, int, float, float, float)
FUNC1(environment_set_ssr_half_size, bool)
FUNC1(environment_set_ssr_roughness_quality, EnvironmentSSRRoughnessQuality)
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 {
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;
}

View file

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