Skip to content

Conversation

Mugen87
Copy link
Collaborator

@Mugen87 Mugen87 commented Aug 5, 2025

Related issue: -

Description

The PR improves the performance of SSRNode by removing an outdated check inside the raymarching loop which was necessary for a Chromium bug. Since this bug is fixed since 133 (see here), I think it's safe to remove it. Another if can be removed by simplifying how the raymarching loop is configured. Removing both if statements produce some extra frames so this is a nice performance plus.

While reviewing other SSR implementations I have realized that SSRNode has a very high sampling rate inside the raymarching loop. Meaning it checks every pixel that lies on a ray's path which is the maximum possible quality. No SRR implementation I have checked so far uses such a setting by default. An implementation for Unity uses 80 samples and a value range of [20,80] and Lettier's approach uses a scaling factor in the range [0,1]. I have adapted this approach to SSRNode with the same default value of 0.5. That means only half of the pixels on the ray are processed. That setting is a uniform so it is configurable on app level. Even with 0.5, you barely see a quality difference. However, in a performance restricted test with a 5K resolution, performance went up from 29 to 52 FPS.

You can explore the new quality parameter in the updated example. Below is the one in production for comparison:

https://rawcdn.githack.com/mrdoob/three.js/75456f3498d9e213fbfe282d2cafda5cb4035cf8/examples/webgpu_postprocessing_ssr.html
https://threejs.org/examples/webgpu_postprocessing_ssr

I'm going to invest more time in SSRNode since there is still a lot of room for improvements. I'd like to test the raymarching loop from the Unity effect which works a bit different than ours. Besides, I've seen it supports mipmaps which means you can create blurred samples of the SSR reflections and then pick a mip based on the fragment's roughness. That is a similar approach like PMREM. Since this is quite heavy, it's an optional feature but of course worth checking out. Another thing is that some SSR implementations also have a temporal component which means they use a jitter in their ray marching process to produce different SSR reflections over time. The results are then accumulate similar to TRAA. There are different strategies of implementing such a feature and some of them are quite complex (e.g. "Stochastic Screen-Space Reflections") but it is definitely worth investigating.

@mrdoob
Copy link
Owner

mrdoob commented Aug 5, 2025

Looking great! 🔥

@sunag
Copy link
Collaborator

sunag commented Aug 6, 2025

An implementation for Unity uses 80 samples and a value range of [20,80] and Lettier's approach uses a scaling factor in the range [0,1].

Maybe you could also use Bayer16 in the raymarching offset like I did in the volumetric lighting, the difference was huge. This allows I reduce the number of internal loops.

Besides, I've seen it supports mipmaps which means you can create blurred samples of the SSR reflections and then pick a mip based on the fragment's roughness.

I did something similar and it was relatively simple with TSL, but I didn't publish it because the blur didn't spread beyond the raymarching area, so I thought it would be interesting to think of some algorithm to spread the rays before release the PR.

@sunag sunag added this to the r180 milestone Aug 6, 2025
@Mugen87
Copy link
Collaborator Author

Mugen87 commented Aug 6, 2025

Maybe you could also use Bayer16 in the raymarching offset like I did in the volumetric lighting, the difference was huge. This allows I reduce the number of internal loops.

I've seen the Unity code uses a blue noise which is comparable to Bayer16 for applying a random jitter to the step size. Using Bayer16 to modulate the x and y spans (which are the step sizes in x and y directions) seems to not improve the quality in SSRNode right now (I hope I did not something wrong^^). Feel free to have a go by yourself but I believe Bayer16 should get more interesting when the SRR makes use of temporal filtering. In this case the jitter will vary over each frame which results in different samples that you can accumulate. That should remove artifacts like below which are typical for SSR:

image

With temporal filtering, this surface should be more even like when using the highest quality setting:

image

@Mugen87 Mugen87 merged commit 55abf9d into mrdoob:dev Aug 6, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants