mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
101.99k stars 35.33k forks source link

Less shadowmap rendering #6814

Closed Astrak closed 9 years ago

Astrak commented 9 years ago

In different situations shadowmaps can be used for static objects that do not move often :

In those situations we need shadowmap rendering, but not each frame. For example, once a part of a procedural city has been created, the buildings don't move but the shadow rendering still uses a lot of computational power.

Here is the suggestion of a shadowMapNeedsUpdate = true parameter to the WebGLRenderer the way verticesNeedUpdate work.

Its value is checked in the ShadowMapPlugin and set to false just after :

if( _renderer.shadowMapEnabled === false || _renderer.shadowMapNeedsUpdate === false ) return;

_renderer.shadowMapNeedsUpdate = false;

This way the shadowmap is rendered once at the first render call, and if the scene requires the realtime shadowmap it has to be precised in the render loop. Otherwise it has to be precised in the functions that move/create/delete objects.

RemusMar commented 9 years ago

In different situations shadowmaps can be used for static objects that do not move often :

a procedural city (big shadow maps), objects that move only when dragged....

To use dynamic shadows for static objects is a VERY bad idea. In those cases you need static lightMaps Example: http://necromanthus.com/Test/html5/SMC.html

cheers

Astrak commented 9 years ago

Thanks.

I use that way as much as i can too, also for moving objects when possible (car shadow in helloracer..).

But sometimes you cannot pre-draw even pre-bake shadows. In a procedural city you can't draw the casted shadow of one building on another, because you don't know what position they will have. Other situations : a skinnedmesh that stops moving, an interactive scene where static objects can be dragged and need shadow casting....

I just hope it can help free computational power

RemusMar commented 9 years ago

But sometimes you cannot pre-draw even pre-bake shadows.

I think you're looking for something like mesh.shadowMapNeedsUpdate = true (or false); In other words, that particular shadowMap needs to be computed only once (after mesh creation or movement). And yes, that would be a very useful feature.

cheers

arodic commented 9 years ago

To use dynamic shadows for static objects is a VERY bad idea.

There are scenarios where you might want a static shadow on dynamic objects. In that case lighmaps are insufficient.

Astrak commented 9 years ago

I might have overestimated the gain : i just applied the idea to draggable cubes (a simple scene that gives headaches to my 2012/intel5 laptop ) but see no difference in CPU usage (integrated GPU intel HD Graphics).

mesh.shadowMapNeedsUpdate

@RemusMar Wouldn't that be equivalent to renderer.shadowMapNeedsUpdate since there is one shadowmap per light source ?

RemusMar commented 9 years ago

Wouldn't that be equivalent to renderer.shadowMapNeedsUpdate since there is one shadowmap per light source ?

You need to disable the shadows computing for all the static objects. So to spare processing power and to keep the best results (shadows) for the other dynamic objects, you need something enabled/disabled "per mesh" (per object).

Astrak commented 9 years ago

To me draggable cubes is a good example of what i need : i don't render the shadowmap unless a cube is dragged. It yet prevents the shadowmap rendering in a big ratio of time, depending the way the user interacts.

Computing shadows per mesh would ask the webglrendertarget to remember each mesh'projection on the shadowmap and update the new projection and the old one am i right ? Hey wouldn't that be easy to code ? ^^ However drawing only the modified fragments is something beyond my skills.

Also as i wrote i see no difference at all on my GPU-CPU, in my updated draggable cubes example and without dragging anything. I'm not really a pro of realtime rendering yet but given this point, would the per mesh solution be better ?

I test it elsewhere tonight and report if i see a real improvement.

RemusMar commented 9 years ago

You need "shadowMapNeedsUpdate = true (or false)" per mesh to make the difference between static and dynamic objects. On the other hand keep in mind: the shadows are STILL drawn for ALL the meshes! So you will see a performances boost only in case of hundreds/thousands of meshes. Otherwise the boost is close to zero.

cheers

Astrak commented 9 years ago

@RemusMar I did not precise the different situations i encounter are only scenes with static meshes, like draggablecubes (i quote this example again as it matches those different situations). I mean, when nothing move both ideas behave the same, so completely preventing the shadowmap rendering is yet a first step.

Anyway, if ever the result is really ok for static cases, i see no decrease in CPU-GPU usage in the examples i'm testing.

What is STLL ? no google result at all.

RemusMar commented 9 years ago

i see no decrease in CPU-GPU usage in the examples i'm testing.

You need to perform a test with hundreds of objects and using the "per mesh" approach.

What is STLL ?

You STILL cast/receive shadows for all the models. So that shadowMapNeedsUpdate is almost irrelevant for a few models. You won't gain anything.

I have to go now. cheers

mrdoob commented 9 years ago

Thanks for the suggestion! It was pretty easy to implement 😊

You can now do this:

renderer.shadowMap.autoUpdate = false;
renderer.shadowMap.needsUpdate = true; // when your scene changes
rohan-deshpande commented 6 years ago

Hey so happy I found this. This feature is totally undocumented! https://threejs.org/docs/#api/renderers/WebGLRenderer I was already doing something like this but it wasn't as performant as this and this has massively increased performance in my app. Will open PR to get it into the docs.

WestLangley commented 6 years ago

@rohan-deshpande Can you please explain your use case? Are you re-rendering even if your scene hasn't changed? How is renderer.shadowMap.autoUpdate = false "massively" improving performance in your use case?

mrdoob commented 6 years ago

@WestLangley It basically allows computing the shadowmap just once.

WestLangley commented 6 years ago

I understand that. But if the scene is static, I was wondering if the user was using an animation loop unnecessarily.

rohan-deshpande commented 6 years ago

Basically what @mrdoob said. It's a very similar situation to what @Astrak was describing

image

The building is static, but the camera can move, rotate etc., the game loop is constantly rendering and waiting for jobs from the animation queue. In this use case, I only need the shadows to be rendered once. After that, they never need to be updated. Except in the case of editing lights (which is what I'm doing here).

rohan-deshpande commented 6 years ago

PR for docs update is here https://github.com/mrdoob/three.js/pull/12417