Skip to content

Conversation

@shotamatsuda
Copy link
Contributor

@shotamatsuda shotamatsuda commented Nov 21, 2025

Related issue: #31892

Description

This PR tries to reduce smearing by introducing variance clipping and subpixel correction in TRAANode.

With #32296, most ghosting will be removed when the camera frustum is properly configured. What remains must be mostly smearing (if I understand correctly), and that can be slightly reduced by adjusting the tolerance of neighborhood clipping/clamping based on the amount of motion. This PR uses variance clipping with an adaptive gamma.

As I mentioned in the issue above, smearing is considerably reduced by increasing the temporal weight towards the current frame when the velocity is more subpixel. It might be more fair to say that even a little increase in the temporal weight greatly reduces smearing because it's exponential, and the subpixel correction acts as a rapid gain. Of course, the original purpose is to reduce blurriness under motion, and we can clearly see that effect as well. A drawback is that it reveals blocky artifacts when used with screen space effects such as SSGI, so a parameter is added to let users decide whether this trade off is acceptable.

With SSGI (for smearing): https://raw.githack.com/shotamatsuda/three.js/2aa2e8ae6b6dffad1a86c6127595ffb82e7ff759/examples/webgpu_postprocessing_ssgi.html
With SSS (for sharpness): https://raw.githack.com/shotamatsuda/three.js/2aa2e8ae6b6dffad1a86c6127595ffb82e7ff759/examples/webgpu_postprocessing_sss.html

Here's a comparison of smearing:

Note
r181
ssgi-r181.mov
Smearing visible
PR with subpixel correction disabled (variance clipping only)
ssgi-no-correction.mov
Not much improvement
PR with subpixel correction enabled
ssgi-correction.mov
Much less smearing

And below shows the reduction of blurriness under motion:

r181 PR
sss-r181.mov
sss-correction.mov

You can see the blocky artifact in the video below (in the shadow of the box. I know SSGI thickness is too large here). Generally, it becomes visible when pixel changes are unrelated to the velocity. This pattern comes directly from the subpixel pattern, and there's no way to avoid it as long as we use the subpixel correction.

Screen.Recording.2025-11-22.at.4.20.57.mov

@mrdoob mrdoob added this to the r182 milestone Nov 21, 2025
* @type {number}
*/
this.depthThreshold = 0.0001;
this.depthThreshold = 0.0005;
Copy link
Contributor Author

@shotamatsuda shotamatsuda Nov 21, 2025

Choose a reason for hiding this comment

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

I changed it to better match r181. Intuitively, a depth threshold of 1/10000 also feels too small.

* @type {number}
*/
this.edgeDepthDiff = 0.0001;
this.edgeDepthDiff = 0.001;
Copy link
Contributor Author

@shotamatsuda shotamatsuda Nov 21, 2025

Choose a reason for hiding this comment

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

Similar to depthThreshold, and we don't want that many edges to be preserved.

*
* @type {number}
*/
this.maxVelocityLength = 128;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This controls the scaling of motionFactor. I'm not fully sure it has to be measured in pixel unit, I'm following Intel's implementation in this regard.

const velocityTexel = velocityUV.mul( textureSize );
const phase = velocityTexel.fract().abs();
const weight = max( phase, phase.oneMinus() );
return weight.x.mul( weight.y ).oneMinus().div( 0.75 );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This differs from the subpixel correction I was referring to, but it should reflect the amount of subpixel more precisely.

Comment on lines +610 to +611
const textureSize = this.beautyNode.size(); // Assumes all the buffers share the same size.
const positionTexel = uvNode.mul( textureSize );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although I initially used screenCoordinate and screenSize, it didn't work and it seemed the screenSize uniform wasn't provided to the shader. I'll investigate this in a separate issue.

@shotamatsuda shotamatsuda marked this pull request as ready for review November 21, 2025 20:09
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