Skip to content

Conversation

@sunag
Copy link
Collaborator

@sunag sunag commented Nov 15, 2025

Related issue: #28863,

Pre-Pass

I made some refinements to the API to support Pre-Pass with MRT; this is usually necessary and its need becomes particularly evident when using velocity.

const prePass = pass( scene, camera );
prePass.setMRT( mrt( {
	output: normalView,
	velocity: velocity
} ) );
prePass.transparent = false;

AO Pre-Pass - there is bug when resize the window yet

AO No Pre-Pass

The benefits are quite evident, as a demonstration shows.

Pre-pass

prepass

Current

current

Process

image

@sunag sunag mentioned this pull request Nov 15, 2025
3 tasks
@sunag sunag added this to the r182 milestone Nov 15, 2025
@github-actions
Copy link

github-actions bot commented Nov 15, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 356.97
86.61
356.97
86.61
+0 B
+0 B
WebGPU 618.37
173.6
618.8
173.75
+438 B
+141 B
WebGPU Nodes 616.97
173.35
617.41
173.49
+438 B
+140 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 488.7
121.39
488.7
121.39
+0 B
+0 B
WebGPU 690.41
189.47
690.85
189.59
+438 B
+121 B
WebGPU Nodes 631.82
172.64
632.26
172.76
+438 B
+121 B

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 15, 2025

Very exciting stuff!

@mrdoob
Copy link
Owner

mrdoob commented Nov 16, 2025

It's interesting that the temporal filtering isn't really needed when the AO is used properly.

@mrdoob
Copy link
Owner

mrdoob commented Nov 16, 2025

Will be nice to see how will webgpu_postprocessing_sss looks after adopting this.

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 16, 2025

You still see quality difference with and without temporal filtering in the Pre-Pass demo:

Without temporal filtering:

image

With temporal filtering:

image

The latter one looks much better like a higher quality setting with more sampling directions and a higher sample count.

The pre-pass approach does not influence the quality of the computed AO just how it's blended with the scene. Since the AO is less pronounced with the pre-pass approach (only indirect diffuse is modulated), the noisy pattern that appears without temporal filtering isn't that visible. But it is still there and hence temporal filtering will improve the quality.

@mrdoob
Copy link
Owner

mrdoob commented Nov 16, 2025

I guess the reason the GTAONode has a boolean for it is because it makes the pass slower?
Otherwise it would be always enabled and we wouldn't need a boolean for it.

But seeing how subtle it is when the AO is used directly in the PBR shader makes me think it's not worth it.
Definitely not in this example.

@mrdoob
Copy link
Owner

mrdoob commented Nov 16, 2025

On that note, after this lands...

Maybe we could consider implementing something like this:

scene.aoNode = new GTAONode();

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 16, 2025

I guess the reason the GTAONode has a boolean for it is because it makes the pass slower?

It's because not every app wants to use TAA. TAA is a requirement though for temporal filtering/accumulation.

@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 16, 2025

Even if you use TAA it's good to have a toggle for temporal filtering. Depending on the effect (e.g. SSGI) ghosting can be amplified and apps might want to use a denoiser instead. However, you loose quality that way so it really depends on the app which options works best. I've seen that similar effects in Unity also provides such toggles to accommodate the user requirements.

But I agree the bedroom scene is not ideal. Hopefully, we can replace it at some point with a better asset.

@mrdoob
Copy link
Owner

mrdoob commented Nov 16, 2025

It's because not every app wants to use TAA. TAA is a requirement though for temporal filtering.

Are nodes aware of other nodes in the graph?

Would be nice if the GTAO node was able to detect if the TAA node is going to be applied later and then run this code.
That would make this boolean redundant.

@sunag sunag changed the title TSL: Pre-pass using global context - WIP TSL: Pre-pass using global context Nov 16, 2025
@sunag sunag marked this pull request as ready for review November 21, 2025 16:30
@sunag
Copy link
Collaborator Author

sunag commented Nov 21, 2025

scene.aoNode = new GTAONode();

I'm not sure if this fits in scene, because the pre-pass shares the same scene.

We could have it in pass() if necessary:

scenePass.aoNode = aoPassOutput.sample( screenUV ).r;

@sunag sunag merged commit 6e78ad5 into mrdoob:dev Nov 21, 2025
10 checks passed
@sunag sunag deleted the dev-prepass branch November 21, 2025 23:58
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.

4 participants