three-gpu-pathtracer icon indicating copy to clipboard operation
three-gpu-pathtracer copied to clipboard

[WIP] Webgpu pathtracer prototype

Open TheBlek opened this issue 2 months ago • 1 comments

Hi @gkjohnson! Here's a simple lambertian pathtracer. Both megakernel and wavefront, as discussed in #692. See webgpu_primitives demo. There is still a lot of work to be done here. I will try to keep an up-to-date todo list here so you could follow the progress.

At what point do you propose measuring performance?

I think it is not so difficult to have both megakernel and wavefront pathtracers as they share the essential code (for now at least). And it could be useful to have a point of reference when implementing additional features. And I'm not sure how relative performance will scale with features.

In webgpu by default we can bind maximum 128Mb buffers and there is no way to request bigger limits in three.js. So that leaves us with max tile resolution of 1920x1080 (which I hardcoded in the example for now). I'm wondering whether requesting bigger webgpu limits or features for that matter is a worthwhile feature for three.js. What do you think?

This buffer limit also limits the scene size at least until the TLAS is deployed and tracing is per-mesh instead of on the whole scene.

Also by default we can bind a maximum of 8 buffers for use which is a bottleneck for traceRay kernel. It needs a lot of buffers so I had to move reading material index into the next kernel (bsdf eval phase). This could be fixed if three.js had support for array types in structs and top-level storage buffer backed structs. At least I could not get it to work, so I had to put queue sizes into a separate buffer and pass separately. I also wonder to write an issue about the limitation in three.js but maybe I just didn't find it.

~~What's your policy on three.js version for webgpu release here? Ideally we would use r181 as it includes support for indirect compute (which is essential for wavefront pathtracing to not read-back values from GPU between calls). But it released like 5 days ago :) For now I copy-pasted compute methods from WebGpuRenderer and WebGpuBackend with little tweaks in order to compute indirectly. see computeIndirect and computeBackendIndirect functions in PathTracerCore.~~

Just saw that the library recently upgraded to r181 - perfect!

Would love any feedback!

  • [x] Update code to use r181
  • [x] Investigate haloing under spheres in wavefront pathtracing
  • [x] Implement tiled rendering to support resolutions greater than 1920x1080
  • [x] Derive performance numbers from three.js'es metrics
  • [ ] Turn back on dynamic resolution
  • [ ] Implement physically-based materials (MeshStandardMaterial)
  • [ ] PERFORMANCE: before putting items into a storage queue, collect them on a workgroup-local queue for better parallelism
  • [ ] PERFORMANCE: Investigate a different split into kernels (from the paper)
  • [ ] PERFORMANCE: Investigate SoA memory layout For later:
  • [ ] Randomize generated rays for each pixel
  • [ ] Implement environment map sampling
  • [ ] Implement russian roulette
  • [ ] Investigate more webgl features to be ported

TheBlek avatar Nov 15 '25 09:11 TheBlek

Amazing work! Thanks for putting the time in. In terms of getting an initial version merged I think these are some good goals. I think this should help get the general architecture defined and answer some of the open questions. I think your other TODOs can be added in subsequent PRs which will make things easier to review:

  • Demo with full resolution support and window resizing.
  • Lambert support only.
  • Support for megakernal & wavefront path tracing to compare performance.
  • Organize the files

In terms of making this easier to understand - do you mind splitting some of the code out into separate files? The "PathTracerCore" file is pretty monolithic and difficult to follow at the moment. I think these changes will make it easier to give some feedback:

  • I think we can move the compute nodes to a webgpu/nodes folder.
  • Lets remove commented code that's not being used
  • Can we document a bit more of what's happening especially in the wavefront path tracer step?

And some other comments:

So that leaves us with max tile resolution of 1920x1080

It would be helpful to know what data is needed at each step of the path tracer. But organizing things from the beginning so that tiled rendering is supported I think is our best bet. Once three.js supports requesting larger buffers users can use that but we'll still need a graceful fallback either way.

At what point do you propose measuring performance?

Let's get to a point where we have a full resolution scene rendering and we can take a look at the pros / cons of the megakernal and wavefront approach. The wavefront approach may also wind up having more of a noticeable improvement later on when we can terminate rays early and / or sort them and therefore avoid running large blocks of work.

Also just a note that the "megakernel" path seems to not be working for me. I see this error:

'TRI_INTERSECT_EPSILON'
    if ( abs( det ) < TRI_INTERSECT_EPSILON ) {
                      ^^^^^^^^^^^^^^^^^^^^^


 - While calling [Device].CreateShaderModule([ShaderModuleDescriptor ""compute""]).

gkjohnson avatar Nov 17 '25 02:11 gkjohnson