Commit: 613a75a3f1b4ed08938fb335e725bd7738969092 Author: Sergey Sharybin Date: Fri Jun 11 19:01:54 2021 +0200 Branches: cycles-x https://developer.blender.org/rB613a75a3f1b4ed08938fb335e725bd7738969092
Cycles X: Initial implementation of shadow catcher denoising Currently only implemented for OIDN denoiser. Almost straightforward. The tricky part is the albedo pass: using the real albedo causes unexpected discontinuity of the shadow catcher based on the albedo of the surface the shadow is cast on. Worked around by passing a fake albedo values. The downside is that it requires a full frame of constant values. Not sure if there is a way around it. Differential Revision: https://developer.blender.org/D11585 =================================================================== M intern/cycles/blender/blender_sync.cpp M intern/cycles/integrator/denoiser_oidn.cpp M intern/cycles/integrator/pass_accessor.cpp M intern/cycles/integrator/pass_accessor.h M intern/cycles/integrator/path_trace_work.cpp M intern/cycles/kernel/kernel_film.h M intern/cycles/kernel/kernel_types.h M intern/cycles/render/scene.cpp =================================================================== diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 240f5c3b079..98d37878356 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -555,7 +555,8 @@ static BlenderPassInfo get_blender_pass_info(BL::RenderPass &b_pass) MAP_PASS("Denoising Normal", PASS_DENOISING_NORMAL); MAP_PASS("Denoising Albedo", PASS_DENOISING_ALBEDO); - MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER); + MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER, PassMode::DENOISED); + MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER); MAP_PASS("Debug Render Time", PASS_RENDER_TIME); @@ -575,6 +576,8 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v { PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + bool add_denoised_passes = false; + vector<Pass> passes; /* loop over passes */ @@ -601,6 +604,8 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles"); if (get_boolean(crl, "denoising_store_passes")) { + add_denoised_passes = true; + b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str()); Pass::add(passes, PASS_COMBINED, "Noisy Image"); @@ -611,6 +616,8 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v Pass::add(passes, PASS_DENOISING_ALBEDO, "Denoising Albedo"); } else if (get_boolean(cscene, "use_denoising")) { + add_denoised_passes = true; + b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str()); Pass::add(passes, PASS_COMBINED, "Noisy Image"); } @@ -634,7 +641,12 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v if (get_boolean(crl, "use_pass_shadow_catcher")) { b_engine.add_pass("Shadow Catcher", 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(passes, PASS_SHADOW_CATCHER, "Shadow Catcher"); + Pass::add_denoising_read(passes, PASS_SHADOW_CATCHER, "Shadow Catcher"); + + if (add_denoised_passes) { + b_engine.add_pass("Noisy Shadow Catcher", 4, "RGBA", b_view_layer.name().c_str()); + Pass::add(passes, PASS_SHADOW_CATCHER, "Noisy Shadow Catcher"); + } } /* Cryptomatte stores two ID/weight pairs per RGBA layer. diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index e94d570d9de..aca913c92c5 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -82,6 +82,9 @@ class OIDNPass { { offset = buffer_params.get_pass_offset(type, mode); need_scale = (type == PASS_DENOISING_ALBEDO || type == PASS_DENOISING_NORMAL); + + const PassInfo pass_info = Pass::get_info(type); + use_compositing = pass_info.use_compositing; } /* Name of an image which will be passed to the OIDN library. @@ -104,6 +107,8 @@ class OIDNPass { * outside of generic pass handling. */ bool need_scale = false; + bool use_compositing = false; + /* For the scaled passes, the data which holds values of scaled pixels. */ array<float> scaled_buffer; }; @@ -122,33 +127,45 @@ class OIDNDeenoiseContext { num_samples_(num_samples), pass_sample_count_(buffer_params_.get_pass_offset(PASS_SAMPLE_COUNT)) { + if (denoise_params_.use_pass_albedo) { + oidn_albedo_pass_ = OIDNPass(buffer_params_, "albedo", PASS_DENOISING_ALBEDO); + /* NOTE: The albedo pass is always ensured to be set from the denoise() call, since it is + * possible that some passes will not use the real values. */ + } + + if (denoise_params_.use_pass_normal) { + oidn_normal_pass_ = OIDNPass(buffer_params_, "normal", PASS_DENOISING_NORMAL); + set_pass(oidn_normal_pass_); + } } - void denoise() + void denoise(const PassType pass_type) { - /* Add input images. - * - * NOTE: Store passes for the entire duration od denoising because OIDN denoiser might - * reference pixels from the pass buffer. */ - - OIDNPass oidn_color_pass(buffer_params_, "color", PASS_COMBINED); - OIDNPass oidn_albedo_pass; - OIDNPass oidn_normal_pass; - + /* Add input color image. */ + OIDNPass oidn_color_pass(buffer_params_, "color", pass_type); + if (oidn_color_pass.offset == PASS_UNUSED) { + return; + } set_pass(oidn_color_pass); if (denoise_params_.use_pass_albedo) { - oidn_albedo_pass = OIDNPass(buffer_params_, "albedo", PASS_DENOISING_ALBEDO); - set_pass(oidn_albedo_pass); - } - - if (denoise_params_.use_pass_normal) { - oidn_normal_pass = OIDNPass(buffer_params_, "normal", PASS_DENOISING_NORMAL); - set_pass(oidn_normal_pass); + if (pass_type == PASS_SHADOW_CATCHER) { + /* Using albedo for the shadow catcher passes does not give desired results: there are + * an unexpected discontinuity in the shadow catcher pass based on the albedo of the object + * the shadow is cast on. */ + set_fake_albedo_pass(); + } + else { + set_pass(oidn_albedo_pass_); + } } /* Add output pass. */ - OIDNPass oidn_output_pass(buffer_params_, "output", PASS_COMBINED, PassMode::DENOISED); + OIDNPass oidn_output_pass(buffer_params_, "output", pass_type, PassMode::DENOISED); + if (oidn_color_pass.offset == PASS_UNUSED) { + LOG(DFATAL) << "Missing denoised pass " << pass_type_as_string(pass_type); + return; + } set_pass_referenced(oidn_output_pass); /* Execute filter. */ @@ -186,7 +203,7 @@ class OIDNDeenoiseContext { stride * pass_stride * sizeof(float)); } - void set_pass_scaled(OIDNPass &oidn_pass) + void read_pass_pixels(OIDNPass &oidn_pass) { const int64_t width = buffer_params_.width; const int64_t height = buffer_params_.height; @@ -196,6 +213,7 @@ class OIDNDeenoiseContext { PassAccessor::PassAccessInfo pass_access_info; pass_access_info.type = oidn_pass.type; + pass_access_info.mode = oidn_pass.mode; pass_access_info.offset = oidn_pass.offset; /* Denoiser operates on passes which are used to calculate the approximation, and is never used @@ -211,13 +229,34 @@ class OIDNDeenoiseContext { const PassAccessor::Destination destination(scaled_buffer.data(), 3); pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params_, destination); + } - oidn_filter_->setImage( - oidn_pass.name, scaled_buffer.data(), oidn::Format::Float3, width, height, 0, 0, 0); + void set_pass_scaled(OIDNPass &oidn_pass) + { + if (oidn_pass.scaled_buffer.empty()) { + read_pass_pixels(oidn_pass); + } + + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + oidn_filter_->setImage(oidn_pass.name, + oidn_pass.scaled_buffer.data(), + oidn::Format::Float3, + width, + height, + 0, + 0, + 0); } void set_pass(OIDNPass &oidn_pass) { + if (oidn_pass.use_compositing) { + set_pass_scaled(oidn_pass); + return; + } + if (!oidn_pass.need_scale || (num_samples_ == 1 && pass_sample_count_ == PASS_UNUSED)) { set_pass_referenced(oidn_pass); return; @@ -226,6 +265,25 @@ class OIDNDeenoiseContext { set_pass_scaled(oidn_pass); } + void set_fake_albedo_pass() + { + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + /* TODO(sergey): Is there a way to avoid allocation of an entire frame of const values? */ + + if (fake_albedo_pixels_.empty()) { + const int64_t num_pixels = width * height * 3; + fake_albedo_pixels_.resize(num_pixels); + for (int i = 0; i < num_pixels; ++i) { + fake_albedo_pixels_[i] = 0.5f; + } + } + + oidn_filter_->setImage( + "albedo", fake_albedo_pixels_.data(), oidn::Format::Float3, width, height, 0, 0, 0); + } + /* Scale output pass to match adaptive sampling per-pixel scale, as well as bring alpha channel * back. */ void postprocess_output(const OIDNPass &oidn_input_pass, const OIDNPass &oidn_output_pass) @@ -244,6 +302,9 @@ class OIDNDeenoiseContext { float *buffer_data = render_buffers_->buffer.data(); + const bool has_pass_sample_count = (pass_sample_count_ != PASS_UNUSED); + const bool need_scale = has_pass_sample_count || oidn_input_pass.use_compositing; + for (int y = 0; y < height; ++y) { float *buffer_row = buffer_data + buffer_offset + y * row_stride; for (int x = 0; x < width; ++x) { @@ -251,8 +312,10 @@ class OIDNDeenoiseContext { float *noisy_pixel = buffer_pixel + oidn_input_pass.offset; float *denoised_pixel = buffer_pixel + oidn_output_pass.offset; - if (pass_sample_count_ != PASS_UNUSED) { - const float pixel_scale = __float_as_uint(buffer_pixel[pass_sample_count_]); + if (need_scale) { + float pixel_scale = has_pass_sample_count ? + __float_as_uint(buffer_pixel[pass_sample_count_]) : + num_samples_; denoised_pixel[0] = denoised_pixel[0] * pixel_scale; denoised_pixel[1] = denoised_pixel[1] * pixel_scale; @@ -270,6 +333,12 @@ class OIDNDeenoiseContext { oidn::FilterRef *oidn_filter_; int num_samples_; int pass_sample_count_; + + /* Optional albedo and normal passes, reused by denoising of different pass types. */ + OIDNPass oidn_albedo_pass_; + OIDNPass oidn_normal_pass_; + + array<float> fake_albedo_pixels_; }; #endif @@ -288,7 +357,9 @@ void OIDNDenoiser::denoise_buffer(const BufferParams &buffer_params, oidn::FilterRef *oidn_filter = &state_->oidn_filter; OIDNDeenoiseContext context(params_, buffer_params, render_buffers, oidn_filter, num_samples); - context.denoise(); + context.denoise(PASS_COMBINED); + context.denoise(PASS_SHADOW_CATCHER); + context.denoise(PASS_SHADOW_CATCHER_MATTE); #end @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list [email protected] List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs
