Page MenuHome

Adaptive Sampling for Cycles.
Needs ReviewPublic

Authored by Stefan Werner (swerner) on Apr 15 2019, 1:49 PM.
"Love" token, awarded by deadpin."Love" token, awarded by Ristovski."Love" token, awarded by bnzs."Love" token, awarded by franMarz."Burninate" token, awarded by hitrpr."Love" token, awarded by Treki26."Love" token, awarded by tuqueque."Love" token, awarded by Sam200."Like" token, awarded by Unreleased75."Like" token, awarded by evantryan."Yellow Medal" token, awarded by MarcelQ."Yellow Medal" token, awarded by symstract."Love" token, awarded by Forshu."Love" token, awarded by zanqdo."Love" token, awarded by jonathanl."Hungry Hippo" token, awarded by Tvaroog."Love" token, awarded by Alex108."Yellow Medal" token, awarded by duarteframos."Love" token, awarded by LucaRood."Love" token, awarded by thornydre."Burninate" token, awarded by kioku."Mountain of Wealth" token, awarded by marcog."Love" token, awarded by ogierm."Love" token, awarded by mandeep."Mountain of Wealth" token, awarded by ace_dragon."Love" token, awarded by rbx775."Like" token, awarded by helloidonthaveanyideaformyusername."The World Burns" token, awarded by filibis."Like" token, awarded by jtheninja."Love" token, awarded by julperado."Love" token, awarded by SteffenD."Love" token, awarded by irfan."Like" token, awarded by TheCharacterhero."Like" token, awarded by YAFU."Like" token, awarded by MJunk."Love" token, awarded by wo262."Party Time" token, awarded by brecht.



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"

In addition, it implements the sampling pattern described in
"Progressive Multi-Jittered Sample Sequences"

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 peridoically over the tile buffer is used for that purpose.

After a tile is 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.

Diff Detail

rB Blender
Build Status
Buildable 3369
Build 3369: arc lint + arc unit

Event Timeline

This is substantial patch and obviously not intended for 2.80. There is still room for improvement, I hope to add more comments/documentation, deduplicate some code and take a closer look at the overall code style.

That said, it is in a stage where input from other developers could be useful. Right now it's in a stage where, as far as I have tested it, it works as expected on CPU, CUDA and OpenCL devices in both mega and split kernels.

Some areas where I would love some feedback:

  • The error metric (kernel_do_adaptive_stopping()) is currently taken straight from [1]. It may be that other metrics behave better.
  • Scheduling work on GPUs is a balance act. Ideally, it would not schedule more than 4 samples at a time before checking for convergence, but that could end up underutilizing powerful cards. The current code is configured to maximize GPU occupancy, at the expense of taking more samples than may be necessary. This might lead to obvious artifacts in CPU+GPU renders.
  • The PMJ sampler turns into random sampling at sample counts > 4096. This could be improved by increasing the lookup table size and implementing a faster sample generation algorithm [2]. Another approach could be using multiple 4096 sized sequences and/or shuffling/scrambling existing sequences.
  • The box filter naturally stops at tile borders. While the "sample count" pass can show grids at tile borders, I was not able to produce noticeable tile artifacts in the resulting beauty pass. This does not mean that those can never happen.
  • The automatic values for threshold/min sample count are just quick guesses. Automatic threshold probably should be different between path tracing and branched path tracing and it might help to take sample clamping into account.


  • Restarting sampling on a previously stopped pixel will make it pick up at a later point in the sample sequence. It should continue sampling as if it was never stopped instead.
  • Pixels that were stopped and then get restarted later by neighboring pixels may end up not getting the full sample count. This is because the main loop over a tile is still for(1...num_samples) instead of a do...while(samples_needed). Adding this on the CPU should not be hard, it will be trickier for GPU kernels.

[1] "A Hierarchical Automatic Stopping Condition for Monte Carlo Global Illumination",
[2] "Efficient Generation of Points that Satisfy Two-Dimensional Elementary Intervals"

Wo!262 (wo262) added a subscriber: Wo!262 (wo262).
Wo!262 (wo262) removed a subscriber: Wo!262 (wo262).

There isn't much to see, but we all enjoy pretty pictures:

I noticed I hadn't added documentation of the user facing parameters:

Adaptive Sampling checkbox: should be self-explanatory. Enables/Disables adaptive sampling. When turned off, Cycles behaves as before. When enabled, Cycles can stop sampling pixels before they reach full sample count based on an error metric.

Adaptive Min Samples: The minimum number of samples a pixel receives before adaptive sampling kicks in. When set to 0 (default), it is automatically set to the square root of the total (max) sample count. Setting it to the same as the total sample count will disable adaptive sampling.
Adaptive Threshold: The error threshold to decide whether to continue sampling a pixel or not. Range 0-1, lower means higher quality. Setting it to exactly 0 lets Cycles guess an automatic value for it based on the total sample count. 1 means all pixels stop sampling after the minimum number of samples. Typical values are in the range of 0.001 to 0.001.

Note that these may change in the future, especially the "Adaptive Threshold" parameter will the sensitive to changes to the error metric.
These may not be the most intuitive parameters, but they roughly mirror the parameters that other renderers use, for example RenderMan or Arnold.

Note that there is now also a new sampling pattern called "Progressive Multi-Jitter" in the advanced sampling section. It is automatically selected when adaptive sampling is turned on, but can also be used on its own.

Cycles: Moved "Adaptive Sampilng" option out of the advanced section in the UI.

Harbormaster completed remote builds in B3333: Diff 14740.

Pretty pictures for a more complex scene:

Pretty pictures for a more complex scene:

Is Christmas coming earlier this year?? =) I've been asking for it for years and was so upset knowing that Adaptive sampling was not considered important for Blender... The render time is cut in half !!! =) isn't it an important improvement for Blender? Although being a GPU renderer Cycles became quite slow in comparison with others, especially with Redshift.

Ok, I'm done with my rant, and seriously I'm super excited that the development has started the right way by referencing Renderman implementation which is an industry standard and works flawlessly. The new overall approach of the Blender community towards being closer to the industry and thus becoming more mature makes all of us very happy, great move!! Good luck in coding =)

Not quite sure if I don't understand the controls or what's happening but I feel it's doing the exact opposite of what I want. It's sampling with max samples the already less noisy bright areas and using even less samples for the noisy dark ones. I'm trying to make the bright areas as noisy as the dark ones, and for the dark ones to remain the same as the base render without adaptative. Maybe it's the build I'm using, idk

For (Forshu) added a subscriber: For (Forshu).
  • Cycles: Fixed a typo in sample_is_even().

@Wo!262 (wo262) This scene looks like it needs a lot more samples, so try increasing that parameter. The adaptive sampler can only pick up and resolve noise that it finds within the "min samples" it takes - if those initial samples turn out all black, the sampler has no reason to continue sampling this seemingly uniform pixel.

Be aware that this sampler will never add samples to noisy areas, it will only omit samples in uniform areas. The overall sample count should at least be high enough to resolve all noise (as far as you want it to).

  • Merge 'master' into 'cycles_adaptive_sampling'
  • Merge 'master' into 'cycles_adaptive_sampling'
  • Merge 'master' into 'cycles_adaptive_sampling'

Does noise detection is done on linear or is useing the color managment we have choosen.. because if it works at linear we are getting huge performance loss.. it should use same LUT/OCIO that is set in color managment... or the best would be if u could choose OCIO just for adaptive. + gamma.

Just chiming in to say that after the big OIDN patch, getting this one in master ASAP will make Cycles render performance compared to 2.80 feel like night and day. I can verify that idea from (OIDN) tests I've done in today's midday buildbot build for Win64.

I hope to see this for version 2.81.