diff options
author | Stefan Werner <stefan.werner@tangent-animation.com> | 2020-03-05 14:05:42 +0300 |
---|---|---|
committer | Stefan Werner <stefan.werner@tangent-animation.com> | 2020-03-05 14:21:38 +0300 |
commit | 51e898324de30c0985a80e5bc067358b5ccedbfc (patch) | |
tree | 5efddead1b7ca5655f1d6d2422b59e7da51fe271 /intern/cycles/render | |
parent | 4ccbbd308060f0330472828b317c59e054c9ee7b (diff) |
Adaptive Sampling for Cycles.
This feature takes some inspiration from
"RenderMan: An Advanced Path Tracing Architecture for Movie Rendering" and
"A Hierarchical Automatic Stopping Condition for Monte Carlo Global Illumination"
The basic principle is as follows:
While samples are being added to a pixel, the adaptive sampler writes half
of the samples to a separate buffer. This gives it two separate estimates
of the same pixel, and by comparing their difference it estimates convergence.
Once convergence drops below a given threshold, the pixel is considered done.
When a pixel has not converged yet and needs more samples than the minimum,
its immediate neighbors are also set to take more samples. This is done in order
to more reliably detect sharp features such as caustics. A 3x3 box filter that
is run periodically over the tile buffer is used for that purpose.
After a tile has finished rendering, the values of all passes are scaled as if
they were rendered with the full number of samples. This way, any code operating
on these buffers, for example the denoiser, does not need to be changed for
per-pixel sample counts.
Reviewed By: brecht, #cycles
Differential Revision: https://developer.blender.org/D4686
Diffstat (limited to 'intern/cycles/render')
-rw-r--r-- | intern/cycles/render/buffers.cpp | 21 | ||||
-rw-r--r-- | intern/cycles/render/film.cpp | 14 | ||||
-rw-r--r-- | intern/cycles/render/film.h | 2 | ||||
-rw-r--r-- | intern/cycles/render/integrator.cpp | 20 | ||||
-rw-r--r-- | intern/cycles/render/integrator.h | 3 | ||||
-rw-r--r-- | intern/cycles/render/session.cpp | 4 | ||||
-rw-r--r-- | intern/cycles/render/session.h | 3 |
7 files changed, 67 insertions, 0 deletions
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 41e1b73fdac..19d3f00bf82 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -260,6 +260,22 @@ bool RenderBuffers::get_pass_rect( return false; } + float *sample_count = NULL; + if (name == "Combined") { + int sample_offset = 0; + for (size_t j = 0; j < params.passes.size(); j++) { + Pass &pass = params.passes[j]; + if (pass.type != PASS_SAMPLE_COUNT) { + sample_offset += pass.components; + continue; + } + else { + sample_count = buffer.data() + sample_offset; + break; + } + } + } + int pass_offset = 0; for (size_t j = 0; j < params.passes.size(); j++) { @@ -420,6 +436,11 @@ bool RenderBuffers::get_pass_rect( } else { for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { + if (sample_count && sample_count[i * pass_stride] < 0.0f) { + scale = (pass.filter) ? -1.0f / (sample_count[i * pass_stride]) : 1.0f; + scale_exposure = (pass.exposure) ? scale * exposure : scale; + } + float4 f = make_float4(in[0], in[1], in[2], in[3]); pixels[0] = f.x * scale_exposure; diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 172ea3dd31b..48d9c97e0fb 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -183,6 +183,13 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name) case PASS_CRYPTOMATTE: pass.components = 4; break; + case PASS_ADAPTIVE_AUX_BUFFER: + pass.components = 4; + break; + case PASS_SAMPLE_COUNT: + pass.components = 1; + pass.exposure = false; + break; case PASS_AOV_COLOR: pass.components = 4; break; @@ -311,6 +318,7 @@ NODE_DEFINE(Film) SOCKET_BOOLEAN(denoising_clean_pass, "Generate Denoising Clean Pass", false); SOCKET_BOOLEAN(denoising_prefiltered_pass, "Generate Denoising Prefiltered Pass", false); SOCKET_INT(denoising_flags, "Denoising Flags", 0); + SOCKET_BOOLEAN(use_adaptive_sampling, "Use Adaptive Sampling", false); return type; } @@ -482,6 +490,12 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_stride; have_cryptomatte = true; break; + case PASS_ADAPTIVE_AUX_BUFFER: + kfilm->pass_adaptive_aux_buffer = kfilm->pass_stride; + break; + case PASS_SAMPLE_COUNT: + kfilm->pass_sample_count = kfilm->pass_stride; + break; case PASS_AOV_COLOR: if (!have_aov_color) { kfilm->pass_aov_color = kfilm->pass_stride; diff --git a/intern/cycles/render/film.h b/intern/cycles/render/film.h index 95e54cb54d8..aae8fb404b0 100644 --- a/intern/cycles/render/film.h +++ b/intern/cycles/render/film.h @@ -81,6 +81,8 @@ class Film : public Node { CryptomatteType cryptomatte_passes; int cryptomatte_depth; + bool use_adaptive_sampling; + bool need_update; Film(); diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index f289e11fe14..ee1aa5988bf 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -27,6 +27,7 @@ #include "kernel/kernel_types.h" #include "util/util_foreach.h" +#include "util/util_logging.h" #include "util/util_hash.h" CCL_NAMESPACE_BEGIN @@ -69,6 +70,9 @@ NODE_DEFINE(Integrator) SOCKET_INT(volume_samples, "Volume Samples", 1); SOCKET_INT(start_sample, "Start Sample", 0); + SOCKET_FLOAT(adaptive_threshold, "Adaptive Threshold", 0.0f); + SOCKET_INT(adaptive_min_samples, "Adaptive Min Samples", 0); + SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true); SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true); SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.05f); @@ -178,6 +182,22 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->sampling_pattern = sampling_pattern; kintegrator->aa_samples = aa_samples; + if (aa_samples > 0 && adaptive_min_samples == 0) { + kintegrator->adaptive_min_samples = max(4, (int)sqrtf(aa_samples)); + VLOG(1) << "Cycles adaptive sampling: automatic min samples = " + << kintegrator->adaptive_min_samples; + } + else { + kintegrator->adaptive_min_samples = max(4, adaptive_min_samples); + } + if (aa_samples > 0 && adaptive_threshold == 0.0f) { + kintegrator->adaptive_threshold = max(0.001f, 1.0f / (float)aa_samples); + VLOG(1) << "Cycles adaptive sampling: automatic threshold = " + << kintegrator->adaptive_threshold; + } + else { + kintegrator->adaptive_threshold = adaptive_threshold; + } if (light_sampling_threshold > 0.0f) { kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold; diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 32d84c27072..9930e907aea 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -75,6 +75,9 @@ class Integrator : public Node { bool sample_all_lights_indirect; float light_sampling_threshold; + int adaptive_min_samples; + float adaptive_threshold; + enum Method { BRANCHED_PATH = 0, PATH = 1, diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 4231403e39a..6bf2160f9fa 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -1103,6 +1103,10 @@ void Session::render(bool with_denoising) task.need_finish_queue = params.progressive_refine; task.integrator_branched = scene->integrator->method == Integrator::BRANCHED_PATH; + task.adaptive_sampling.use = (scene->integrator->sampling_pattern == SAMPLING_PATTERN_PMJ) && + scene->dscene.data.film.pass_adaptive_aux_buffer; + task.adaptive_sampling.min_samples = scene->dscene.data.integrator.adaptive_min_samples; + /* Acquire render tiles by default. */ task.tile_types = RenderTile::PATH_TRACE; diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 7f3614ccb19..8053e46b12e 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -56,6 +56,7 @@ class SessionParams { int denoising_start_sample; int pixel_size; int threads; + bool adaptive_sampling; bool use_profiling; @@ -89,6 +90,7 @@ class SessionParams { denoising_start_sample = 0; pixel_size = 1; threads = 0; + adaptive_sampling = false; use_profiling = false; @@ -117,6 +119,7 @@ class SessionParams { progressive == params.progressive && experimental == params.experimental && tile_size == params.tile_size && start_resolution == params.start_resolution && pixel_size == params.pixel_size && threads == params.threads && + adaptive_sampling == params.adaptive_sampling && use_profiling == params.use_profiling && display_buffer_linear == params.display_buffer_linear && cancel_timeout == params.cancel_timeout && reset_timeout == params.reset_timeout && |