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

ShadowNode: Introduce onBeforeUpdate and onAfterUpdate #30856

Closed

Conversation

RenaudRohlinger
Copy link
Collaborator

@RenaudRohlinger RenaudRohlinger commented Apr 4, 2025

Description
This PR adds two new hooks to shadow nodes: onBeforeUpdate and onAfterUpdate. These can be very useful for applying specific behavior to individual shadows in the scene.

For example, in the case of a static shadow and a dynamic shadow, a mesh can be hidden for one and visible only to the other:

const staticMeshes = [...]
const dynamicMeshes = [...]
const bakedShadow = new THREE.DirectionalLight(0xffffaa, 4.5);
bakedShadow.castShadow = true;
bakedShadow.shadow.needsUpdate = true;
bakedShadow.shadow.autoUpdate = false;

bakedShadow.shadow.onBeforeUpdate = () => {
   staticMesh.visible = true;
   dynamicMesh.visible = false;
};

bakedShadow.shadow.onAfterUpdate = () => {
   staticMesh.visible = false;
   dynamicMesh.visible = true;
};

const dynamicShadow = new THREE.DirectionalLight(0xffffaa, 4.5);
dynamicShadow.castShadow = true;

bakedShadow.shadow.onBeforeUpdate = () => {
   staticMesh.visible = false;
   dynamicMesh.visible = true;
};

bakedShadow.shadow.onAfterUpdate = () => {
   staticMesh.visible = true;
   dynamicMesh.visible = false;
};

This contribution is funded by Renaud Rohlinger @ Utsubo

@RenaudRohlinger RenaudRohlinger added this to the r176 milestone Apr 4, 2025
Copy link

github-actions bot commented Apr 4, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 336.39
78.35
336.39
78.35
+0 B
+0 B
WebGPU 540.78
149.8
540.86
149.82
+78 B
+22 B
WebGPU Nodes 540.25
149.7
540.32
149.72
+78 B
+21 B

🌳 Bundle size after tree-shaking

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

Before After Diff
WebGL 465.37
112.21
465.37
112.21
+0 B
+0 B
WebGPU 613.67
165.87
613.75
165.89
+78 B
+28 B
WebGPU Nodes 568.66
155.28
568.74
155.31
+78 B
+29 B

@sunag
Copy link
Collaborator

sunag commented Apr 4, 2025

Could staticMesh.onBeforeShadow() and .onAfterShadow() fulfill this need?

@RenaudRohlinger
Copy link
Collaborator Author

No because onBeforeShadow() and onAfterShadow() are applied at the object level, they affect all shadows in the scene uniformly. This means we can't separate the logic for different lights using these methods.

@sunag
Copy link
Collaborator

sunag commented Apr 4, 2025

I had noticed this but it seems that in the example you are dealing with meshes individually but it still makes sense. I still don't know if adding it to the Node is the best option, we should have events in the Nodes that manipulated the Node class (the scope) and events in the Scene/Object3D that manipulated the scene. I think that light.onBeforeShadow() or scene.onBeforeShadow() seems to make more sense in this context.

@RenaudRohlinger
Copy link
Collaborator Author

RenaudRohlinger commented Apr 4, 2025

Without even having to update the API, I think we could add in Renderer._renderScene camera.onBeforeRender and camera.onAfterRender which are already available since Camera extends Object3D, it would also work like this:

bakedShadow.shadow.camera.onBeforeRender = () => {
   staticMesh.visible = true;
   dynamicMesh.visible = false;
};
	_renderScene( scene, camera, useFrameBufferTarget = true ) {

		if ( this._isDeviceLost === true ) return;
		
		camera.onBeforeRender() // also object doesn't exist here, nor material ect...
		
		
		...
		
		
		sceneRef.onAfterRender( this, scene, camera, renderTarget );

		//
		
		camera.onAfterRender() // <-- after or before sceneRef.onAfterRender?


		return renderContext;

(Btw camera.onBeforeShadow() is also a thing, but then it's starting to get confusing)

@sunag
Copy link
Collaborator

sunag commented Apr 4, 2025

(Btw camera.onBeforeShadow() is also a thing, but then it's starting to get confusing)

Yes, light.onBeforeShadow() seems more appropriate so far.

@RenaudRohlinger
Copy link
Collaborator Author

Nevermind, I realized using camera.layers is actually the optimal solution to handle this particular case!

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.

2 participants