three.js icon indicating copy to clipboard operation
three.js copied to clipboard

WebGLRenderer: Add .renderShadowMap().

Open Oletus opened this issue 2 years ago • 10 comments

This can be used to manually update shadow maps, which can be convenient in case the user wants to set up the scene differently for shadow map rendering. This can be useful for example when showing a cutout view.

Fixed #23461

This contribution is funded by Higharc.

Oletus avatar Feb 24 '22 20:02 Oletus

The example needs to be added to files.json. Otherwise it does not appear in the side menu.

And please add the respective E2E screenshot via:

npm run make-screenshot webgl_shadowmap_manual_update

The PR checks should then be successful.

Mugen87 avatar Mar 01 '22 11:03 Mugen87

@Mugen87 Added the file to the examples listing and added the screenshot, PR checks passed now!

Oletus avatar Mar 03 '22 17:03 Oletus

I had to diff the example in this PR to find the 3 lines which differ from the original example upon which it is based.

I think this will be confusing to users.

This can be useful for example when showing a cutout view.

Why not create an example of that, instead?

WestLangley avatar Mar 03 '22 19:03 WestLangley

I did give a try to this feature. It works like a charm. I was able to bake a static shadow on the whole scene in 4k and then render a small dynamic shadow in 1k that calls renderShadowMap only on a few meshes:

(In this video the giant shadow camera helper is the shadow camera of the scene, and the small shadow camera helper is the dynamic that only renders the characters through renderShadowMap)

https://user-images.githubusercontent.com/15867665/159166252-c066c3dd-53a5-42e6-82ba-2e05135f3e5d.mov

Also, to make full usage of it I would recommend a 4th parameter forceUpdate

Like so:

this.renderShadowMap = function ( lightsWithShadows, scene, camera, forceUpdate )

That will allow being able to manually force to render an object even though autoUpdate and needsUpdate are set to false. We need to disable both these values if with use renderShadowMap otherwise it would clash with the natural state of how shadowmap.render works and call both methods per render.

In that case, the render method of WebGLShadowMap would have a third condition in order to prevent the render of the shadow: if ( shadow.autoUpdate === false && shadow.needsUpdate === false && forceUpdate === undefined ) continue;

RenaudRohlinger avatar Mar 20 '22 14:03 RenaudRohlinger

Why not create an example of that, instead?

Agreed. This needs a simple example.

mrdoob avatar Mar 21 '22 20:03 mrdoob

With that new feature it would be very interesting to also introduce Array usage on top of Mesh.Group for the second parameter of WebGLShadowMap.render.

It will allow to prevent a complex filtering of the meshes that we want to apply shadow to. That way, instead of doing scene.traverse(), we can just renderShadowMap(lights, [meshA, meshB], camera, true)

It could look like this:


function WebGLShadowMap( _renderer, _objects, _capabilities ) {

	this.render = function ( lights, scene, camera, forceUpdate ) {
              
              // bypass if we want to manually update the shadowmap for specific elements
              if ( scope.autoUpdate === false && scope.needsUpdate === false && forceUpdate === undefined ) return;
              
              // allow arrays
              const elements = Array.isArray( scene ) ? scene : [ scene ];
             
              for ( let vp = 0; vp < viewportCount; vp ++ ) {
                    //viewport
                    _frustum = shadow.getFrustum();
              
                    for ( let j = 0; j < elements.length; j ++ ) {
                    
	                    const element = elements[ j ];
	                    renderObject( element, camera, shadow.camera, light, this.type );
                    
                    }
              
              }
              
              //....

RenaudRohlinger avatar Mar 22 '22 06:03 RenaudRohlinger

@mrdoob @Mugen87 Hey! Sorry that it took such a long time for me to get around to updating the example, but it should be much better now! The manually updated shadow map now includes an object that's not visible in normal rendering. DeepScan is complaining about an unrelated issue since this code was forked off of dev a while back - do I need to rebase this branch or merge dev into here?

Oletus avatar May 06 '22 12:05 Oletus

Rebased this to pass the tests and made sure that the patch still works, @mrdoob time to merge this? 😁 I'd be really happy if we got this change in, hope you're not too busy! 🙏

Oletus avatar Jun 08 '22 14:06 Oletus

@mrdoob Wanted to check the status of this PR once again, it's been in the approved state for a long time! It seems like the code is still valid for the latest versions of three.js, but I could rebase the branch again if needed. We're still using this feature at Higharc, it would be great if we could get some of our customizations upstream.

Oletus avatar Feb 22 '23 13:02 Oletus

Sorry for the wait!

Would be great if you could rebase the PR yes.

Also, would it be possible to make it so WebGLRenderer's render() also uses renderShadowMap()?

mrdoob avatar Mar 24 '23 03:03 mrdoob