mirror of
https://github.com/godotengine/godot.git
synced 2025-12-08 06:09:55 +00:00
Overhaul screen space reflections.
This commit is contained in:
parent
f50d7fa1e8
commit
c128886c63
31 changed files with 1185 additions and 934 deletions
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue