BabylonJS / Babylon.js

Babylon.js is a powerful, beautiful, simple, and open game and rendering engine packed into a friendly JavaScript framework.
http://www.babylonjs.com
Apache License 2.0
22.78k stars 3.39k forks source link

IBL shadowing #15106

Open MiiBond opened 1 month ago

MiiBond commented 1 month ago

This PR adds an IblShadowsRenderPipeline that adds shadowing for image-based lighting (IBL). It's applied as a post process after rendering your scene. You must assign an HDR environment with setIblTexture to enable the shadowing. Both cubemaps and equirectangular projected IBL's work.

This pipeline builds a voxel grid for all the meshes in the scene that you want to cast shadows. You can exclude meshes (e.g. the skybox, ground plane, etc.) by calling addExcludedMesh on the pipeline. The voxel grid needs to be re-generated every time something in the scene moves and this can be an expensive operation (depending on the voxel resolution) so it's most suitable for static scenes in its current form. Note that the camera can move and the IBL environment is free to rotate or change completely without having to rebuild the voxel grid.

Using this voxel grid, shadows are generated by sampling the IBL and then applied to the scene as a greyscale approximation. In combination with this, we can also calculate screen-space shadows to handle shadowing of small details.

Some important exposed settings:

Updating the voxel grid:

Things that still need work:

bjsplat commented 1 month ago

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). To prevent this PR from going to the changelog marked it with the "skip changelog" label.

bjsplat commented 1 month ago

Snapshot stored with reference name: refs/pull/15106/merge

Test environment: https://babylonsnapshots.z22.web.core.windows.net/refs/pull/15106/merge/index.html

To test a playground add it to the URL, for example:

https://babylonsnapshots.z22.web.core.windows.net/refs/pull/15106/merge/index.html#WGZLGJ#4600

Links to test babylon tools with this snapshot:

https://playground.babylonjs.com/?snapshot=refs/pull/15106/merge https://sandbox.babylonjs.com/?snapshot=refs/pull/15106/merge https://gui.babylonjs.com/?snapshot=refs/pull/15106/merge https://nme.babylonjs.com/?snapshot=refs/pull/15106/merge

To test the snapshot in the playground with a playground ID add it after the snapshot query string:

https://playground.babylonjs.com/?snapshot=refs/pull/15106/merge#BCU1XR#0

bjsplat commented 1 month ago

Visualization tests for WebGPU (Experimental) Important - these might fail sporadically. This is an optional test.

https://babylonsnapshots.z22.web.core.windows.net/refs/pull/15106/merge/testResults/webgpuplaywright/index.html

bjsplat commented 1 month ago

WebGL2 visualization test reporter:

https://babylonsnapshots.z22.web.core.windows.net/refs/pull/15106/merge/testResults/webgl2playwright/index.html

bjsplat commented 1 month ago

Visualization tests for WebGPU (Experimental) Important - these might fail sporadically. This is an optional test.

https://babylonsnapshots.z22.web.core.windows.net/refs/pull/15106/merge/testResults/webgpuplaywright/index.html

MiiBond commented 1 month ago

Would it be possible to get some general feedback about whether this is the right approach for implementing something like this in Babylon? I've modeled this after the DepthPeelingRenderer, which is a scene component, but maybe this should be a pipeline? The effect has many passes and parts so I'm curious whether I'm structuring them in a reasonable way inside Babylon.

Popov72 commented 1 month ago

I'll take a look at it early next week.

Popov72 commented 1 month ago

Here are some quick comments, but not a full review, as I probably won't be able to follow up because I'll be away for a few months in a couple of days, and it's only in draft mode:

As for your question about implementation, I think a rendering pipeline would indeed be better suited, as it would better isolate the code from the rest of the engine. We're going to try and move away from the existing architecture with scene components, so it's best not to rely on them...

I also wonder about performance... With the default values of 256 for the voxel resolution, and assuming 8 draw buffers are supported, it means (256/8)*3=96 rendering to regenerate the voxelized scene. Even with a resolution of 64, it's still 24 passes to generate the voxelized scene. Have you been able to run tests on different scenes of varying complexity and assess the impact on performance?

MiiBond commented 1 month ago

Excellent, feedback, @Popov72! Thank you.

Regarding performance, yes, it's a lot of draw calls to generate the voxel grid and it isn't really suited to dynamic scenes for that reason (at least in it's current form). There are optimizations yet to be made but, to be honest, most of the work I have ahead of me is for improving the quality rather than the performance.

Enjoy your time off!

MiiBond commented 1 month ago

I turned this into a pipeline but I'm not yet using the addEffect functionality and adding the pipeline to the scene.postProcessRenderPipelineManager. I think most of the per-camera stuff that I have can be converted to this system (many are ProceduralTextures right now) but I'm unsure of my AccumulationPass. Right now, it relies on a couple of buffers from the previous frame, including the previous result of the accumulation pass. Is there a way to store off RT's to then be used for the next frame for each camera? It doesn't look like the PostProcessRenderPipeline was designed with this in mind.