-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
TRAANode: Reduce smearing #32322
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
base: dev
Are you sure you want to change the base?
TRAANode: Reduce smearing #32322
Conversation
| * @type {number} | ||
| */ | ||
| this.depthThreshold = 0.0001; | ||
| this.depthThreshold = 0.0005; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 ); |
There was a problem hiding this comment.
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.
| const textureSize = this.beautyNode.size(); // Assumes all the buffers share the same size. | ||
| const positionTexel = uvNode.mul( textureSize ); |
There was a problem hiding this comment.
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.
Improve JSDoc.
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:
ssgi-r181.mov
ssgi-no-correction.mov
ssgi-correction.mov
And below shows the reduction of blurriness under motion:
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