Denoisers, adaptive sampling, and the quality issues brought by dimly lit areas.

I start this thread with the intention of proposing what could be done to remedy the situation brought up in the title. I have noticed after extensive testing of the denoiser that it progressively loses its ability to preserve detail as you go down from highlights to midtones and from midtones to shadowed areas.

Then I noticed that this wasn’t an isolated thing I have seen with patches for more advanced features in Cycles, because the old adaptive sampling patch that Lukas attempted had the exact same issue in terms of quality. After looking at the situation. I can’t help but to conclude that the core of the issue lies in two areas, the algorithms working on the raw values rather than the tonemapped values seen by the user and the equal treatment of brighter areas and darker areas.

The reason why raw values should not be used would appear obvious to those who have sampled the pixels while a render is going, the contrast that is often used to determine feature existence along with the noise level is simply not there in the raw values (far lower than what appears after tonemapping and especially compared to brighter regions).

As for how to resolve this, may I propose two possible solutions (at least for the new denoiser).

  • Treat darker areas with far more powerful feature and neighbor weighting than lighter areas (so details are a bit less likely to get blurred over).
  • Work on tonemapped values (at least values as they would appear with a standard linear tonemapping treatment) instead of the raw values

Theoretically, things could work even better if a combination of both ideas were used, then the denoiser can fulfill its true potential in being a massive accelerator of performance.

Thoughts and ideas?

Rather than adding semi-arbitrary constraints causing the noise reducing algorithms to act differently depending on the brightness of the image, it would be more helpful to improve the metrics on what is considered noise and what is not.

For example:

Adaptive sampling could be implemented by comparing the image at two sample points (ex: 32 and 64 samples), then sampling more in the areas with more differences. Possibly update the comparison after every factor of 2.
It could also look for areas with differences less than less than a certain threshold (ex: 1% of intensity value), and stop sampling those areas.

Denoising in post would look at something similar (maybe comparing the final image with what it looked like at half samples), and use that as a metric for where noise appears.
For animations, a denoiser could look at corresponding pixels between frames (i.e. motion compensation), and any small-scale variations that match temporally are viewed as data rather than noise. Other small-scale variations are treated as noise and blurred out. A median filter would also be desirable for removing salt-and-pepper noise (i.e. fireflies).

An example where there is at least 300 samples with denoiser would be nice to see. Because I haven’t noticed it.

what about volumes? they hardly ever clear easy…

The feature detection issue in dimly lit regions most often occurs when the details start to get more parallel to the camera rather than perpendicular (so the treatment change also needs to look at the normals from the camera perspective). The scene you have used in the past is more or less brightly lit with few dim areas.

Rather than adding semi-arbitrary constraints causing the noise reducing algorithms to act differently depending on the brightness of the image, it would be more helpful to improve the metrics on what is considered noise and what is not.

That would be a rather complex way to resolve things, but when you note (or what I have noted across multiple scenes) that there’s a consistency in feature detection trouble in the situation I describe above, it might be a lot easier to just vastly increase the feature emphasis when such a situation occurs (it’s much like when you use bilateral blur and you can increase the feature emphasis, as much as needed, using a color mix node with white as the second input and ‘overlay’ as the blend mode).

As for an image example, this one made use of 2048 samples and shows the issue I am talking about (note the progressive softening of the carpet and the ceiling displacement as you go from the brighter areas to the darker ones).


A before and after image would really help.

The contrast in the raw image can actually be much higher than in the tonemapped one. Do you know for a fact that it is being for “feature detection”, anyway?

Most denoisers use other hints (distance in terms of normals/position, material id …) to determine filter weights for adjacent pixels. The issue with dark areas is rather that the variance there is usually higher than in bright areas. Even the best denoiser can’t work well with too little information. That’s something adaptive sampling could take into account, but that happens at an earlier stage than denoising.

  • Treat darker areas with far more powerful feature and neighbor weighting than lighter areas (so details are a bit less likely to get blurred over).

What is “powerful” supposed to mean here? Stronger weights mean more blurring.

  • Work on tonemapped values (at least values as they would appear with a standard linear tonemapping treatment) instead of the raw values

That may be worth trying if you have overbright pixels, but I don’t see how it helps in this case. You probably want to treat outliers in your denoising process explicitly instead.

Personally I do not, but I have noticed that tonemapping does not appear to impact the denoising result. As for the higher contrast, that is true for brighter areas, but darker areas actually have less contrast when noting that very low values are significantly raised when tonemapping (you can see for yourself by left-clicking pixels in the render window and reading the values).

What is “powerful” supposed to mean here? Stronger weights mean more blurring.

I mean the idea that in low-light areas, edges would have an increased ability to prevent the effect of the denoising radius from crossing it (look at what happens when you plug a feature pass in the determinator input of the bilateral blur node, note how the blur cannot cross edges and note how a color mix set to ‘overlay’ can increase the amount of edges that do just that).


Even the best denoiser can’t work well with too little information. That’s something adaptive sampling could take into account, but that happens at an earlier stage than denoising.

I can see that, but fortunately, the issue can potentially be greatly reduced or even eliminated by a combination of a lower radius, a higher min-bounce parameter, a non-aggressive light sampling threshold value, and the smart application of sharpening in the compositor based on one or more feature pass outputs (though the last one may require the use of an external app. for those using Cycles outside of Blender via a plugin).

Still, improving the ability for the denoiser to detect features so a larger and larger radius can be used would be good for everyone, as it would create an ever larger performance boost when it is used and turn Cycles into a seriously powerful solution for individuals to do animation even.

@BeerBaron
I am wondering, for future detecting wouldnt it be easier to use a normal map of a rendered scene.
Once you know the normals (perhaps as as a ratio of how much they are off the camera viewer angle).
Then you know how all colors should smooth/fade out, and hair wouldnt be any problem either
(since their normals are so hectic one would know their blur area is small to zero).
The other thing i wonder though is how to address textures / color changes.

// i’m just thinking about how to optimize a neural network for this task, i see it might work pretty good on such data.
// as currently i’m training on test images gray scales (not Blender images).

It is. Like I mentioned, distance in terms of normals, position etc. are commonly used to determine filter weights.

The other thing i wonder though is how to address textures / color changes.

Diffuse color, texture id, material id, many things can be used. You don’t have to rely on just one thing. It depends on what kind of information you can out of the render engine. You may be able to denoise only the illumination pass, so diffuse textures can never blur over.

There’s also the idea of a “virtual flash image”, where you render a cheap and noise-free version (e.g. OpenGL render) of the scene. I’ve recently seen someone advertise a neural-network based denoiser that used this, on these forums.

i’m just thinking about how to optimize a neural network for this task, i see it might work pretty good on such data.

I’d definitely look into using geometry passes for inputs. However, there’s also research going on here, so you should first look into what other people have done (and to which success).

I don’t see the connection there.

As for the higher contrast, that is true for brighter areas, but darker areas actually have less contrast when noting that very low values are significantly raised when tonemapping (you can see for yourself by left-clicking pixels in the render window and reading the values).

Not sure I follow. Tonemapping tends to decrease contrast, because the range of values needs to be compressed to a displayable range.

I mean the idea that in low-light areas, edges would have an increased ability to prevent the effect of the denoising radius from crossing it (look at what happens when you plug a feature pass in the determinator input of the bilateral blur node, note how the blur cannot cross edges and note how a color mix set to ‘overlay’ can increase the amount of edges that do just that).

First of all, I’d verify that this overblurring in dark areas actually happens due to the area being dark, as opposed to e.g. higher variance (too little samples) in dark areas.

Secondly, if what you propose should work then you should be able to test it by changing the denoiser setting, then comparing the dark areas. Also, always render each with two different seeds to rule out chance.

If this is the case, shouldn’t the feature weights or radius be adaptive based on the total amount of variance then (and see the over-blurring in shadowed areas like in my example resolved that way)?

The denoising is adaptive in many ways, see e.g. this paper if you want to learn about details of one implementation. In Cycles you can enable denoising passes in Experimental mode to see what kind of input data it uses. These can also be saved and used to test other algorithms.

The denoiser uses things like normals and albedo, which themselves will be noisy in case of DoF or motion blur, and there may not be a single albedo or normal value per pixel when there are mixed sharp and soft BSDFs, multiple transparent layers or edges. For simple diffuse flat surfaces this is not an issue, but the denoiser is intended to work on more than those.

It is important to understand that the algorithm has to deal with incomplete information and often has to guess if something is just noise or an actual detailed feature. And even if it can figure out if there is a feature, the denoiser may not be able to reconstruct it if there is not enough information, so reducing the amount of blur would not necessarily give a better looking result.

Let me illustrate what I mean:

For a detail to even be visible, light needs to reflect off it. In darker areas, it is often less likely that a given path results in such a reflection. There is less information available.

If you were to then reduce the filter intensity you would simply make the noise more pronounced, but you could not hope to recover information that was never there to begin with. It may still look to you like an increase in detail/sharpness though, that’s why you need to at least verify with different seeds.

Furthermore, to determine that such “darker” areas are in fact noisier and therefore require more samples is (part of) the challenge behind adaptive sampling.

The practice to search for sufficient information was more obvious when user had ability to define window width.

Default strength is also too high.
Nobody will think to set filter strength below 0.1 if default is set to 0.5.