Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New accurate sun sampling method #1604

Merged
merged 23 commits into from
Mar 4, 2024

Conversation

JustinTimeCuber
Copy link
Contributor

@JustinTimeCuber JustinTimeCuber commented Jul 19, 2023

The problem

Chunky currently has two sun sampling methods: FAST, which converges quickly but does not provide accurate visuals in all scenarios, and HIGH_QUALITY, which from my testing just seems to be a middleground between FAST and OFF - depending on the ratio between Sun luminosity and Sunlight intensity, HIGH_QUALITY sun sampling can be made "wrong but fast" or "noisy but accurate" or somewhere in-between:
image
image
image

Besides the fact that having two separate controls for sun brightness is confusing (and probably should be looked into in the future), this doesn't really solve the issue of FAST sun sampling being inaccurate, because in order to get accurate results, you're not really getting reduced noise anyway.

Overview of the new algorithm

The idea here is to do the sun sampling as a direct part of calculating diffuse reflections, rather than as an additional ray that has to be combined with the actual diffuse ray to give a result.
Without sun sampling, diffuse scattering looks something like this. Depending on the exact settings (sun size, altitude) as well as relative angle of the surface, and ignoring any obstructions, there is a roughly 1 in 1000 chance of a direct sun hit per diffuse reflection:
image
However, what if we biased the sample, and then corrected for it? That is, we alter the distribution to be something like this:
image
Then, if the ray is in the 10%, we divide its contribution by 100 (and if it is in the 90%, we multiply it by 1.11). This corrects for the bias introduced by changing the distribution. Another way to think of it is that instead of getting 1 ray of very bright sunlight per 1000 diffuse reflections, we will get 100 relatively dim ones. And notably, although we are technically getting fewer samples from the rest of the scene (and slightly amplifying those samples accordingly), the difference is rather small, in this case about 10%. And this value is configurable, so even a chance of 1% for sun sampling can improve sunlight convergence by a factor of 10 while having a negligible (0.9%) loss in quality for other light sources. This approach is a form of importance sampling.

Noise reduction examples

Generally, noise is reduced in direct and diffusely-reflected sunlight, but not in other less common effects such as specularly-reflected sunlight and caustics. Here is a scene with only diffuse surfaces, rendered with SS off (baseline), SS FAST, and my algorithm at 1%, 10%, and 50% sample chances:
image
image
image
image
image
A few things to note here, besides the general reduction in noise:

  • I don't have much of an explanation for why it is so much faster with 50% sun sampling. Maybe something to do with similar rays being cast more often, improving cache hit rate? I wouldn't read to much into it.
  • You may notice some "speckles"; this is due to the sun being at an azimuth of 90 degrees and hence hitting a few blocks at a very shallow angle. The current implementation basically just gives up if the sun is at an angle shallower than the sample radius. While I have a couple ideas to fix this (and definitely want to do so), it is technically not incorrect, because eventually this noise should converge.
  • This scene is actually an example where FAST sampling works pretty well and gives similar results (albeit with less yellow sunlight). However, the next example shows where this method really excels.

Here is a scene that includes both diffuse and specular reflections:
image
image
image
image
image

  • The key takeaway here is that this algorithm can greatly reduce noise (like FAST, although not quite as much) while still preserving certain effects such as reflected sunlight.
  • However, if you compare the last two renders (10% and 50% sampling), you'll notice that although general noise is reduced somewhat, the noise in the reflected sunlight becomes worse at 50%. This is expected and is due to a greater proportion of the bounces being dedicated to sun samples (which do not capture specularly reflected sunlight).

Correctness

To ensure that this algorithm works as expected, I have tested it in a few complex scenes at high SPP, in comparison to sun sampling off:
image
image
I was hoping to see caustics in this render, since I had sunlight passing through water. It's possible that they are there but extremely subtle. However, just to ensure that caustics are working as expected, I ran this render:
image
image

Things to consider before merging

  • The issue with speckles appearing in certain situations should probably be fixed if possible. [Mostly fixed]
  • Should HIGH_QUALITY stay as-is, be renamed, or removed?
  • If it is removed, is there any reason to have two separate sun brightness controls?
  • Does calling this algorithm DIFFUSE make sense?
  • I added a couple variables to the Sun class, should those be moved elsewhere?
  • Are there any weird situations where this method might fail/produce incorrect results?

@JustinTimeCuber
Copy link
Contributor Author

With the latest changes:
image
Before:
image
Definitely an improvement in terms of speckles. Not exactly sure why they aren't completely eliminated, but I'll think about it more.

@JustinTimeCuber
Copy link
Contributor Author

More analysis that should hopefully be pretty convincing that this is unbiased:

10 million samples, with SS=DIFFUSE
SS-Diffuse

SS=OFF:
SS-Off

Difference (zero difference mapped to 127 RGB value)
difference
image

@JustinTimeCuber JustinTimeCuber marked this pull request as ready for review August 1, 2023 04:35
@leMaik
Copy link
Member

leMaik commented Nov 3, 2023

Looks good to me so far and the results are amazing! This PR needs a rebase now.

Some more thoughts:

  • How about renaming the new method to Importance sampling? That would make clear what's going on on a high level. And make sure to document it in the code too, maybe also link to this PR.
  • Let's keep the old names for the other methods to not break existing scenes.

@JustinTimeCuber
Copy link
Contributor Author

I have fixed the merge conflict and changed the name to IMPORTANCE as you suggested.

Comment on lines 139 to 140
// Hide HIGH_QUALITY in the GUI but leave it available through JSON editing/loading existing scenes
sunSamplingStrategy.getItems().remove(SunSamplingStrategy.HIGH_QUALITY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of #1681, SunSamplingStrategy can now implement getDeprecationStatus to return HIDDEN if it is HIGH_QUALITY. The UI logic will then be implemented at a later point (for all selectors at once).

Suggested change
// Hide HIGH_QUALITY in the GUI but leave it available through JSON editing/loading existing scenes
sunSamplingStrategy.getItems().remove(SunSamplingStrategy.HIGH_QUALITY);

@leMaik
Copy link
Member

leMaik commented Mar 4, 2024

Great work! 👏 Especially all the renders and the description of the algorithm! 🎉

@leMaik leMaik merged commit 4f3dfac into chunky-dev:master Mar 4, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants