gkjohnson / three-gpu-pathtracer

Path tracing renderer and utilities for three.js built on top of three-mesh-bvh.
https://gkjohnson.github.io/three-gpu-pathtracer/example/bundle/index.html
MIT License
1.37k stars 133 forks source link

Add Area Lights, Multiple Importance Sampling #47

Closed gkjohnson closed 2 years ago

bhouston commented 2 years ago

I will put a bounty on a successfully merged area lights (compatible with Three.js) PR of $500 USD sponsored by @Threekit.

mrxz commented 2 years ago

I'm contemplating giving this a shot, but I would like to have clear what is expected. Adding the area lights seems pretty straightforward, but obviously add little benefit over just using emissive materials, unless the sampling is updated as well.

Looking at the code, it seems the following would be needed:

Anything missing from the above?

bhouston commented 2 years ago

This seems correct to me. @gkjohnson thoughts?

gkjohnson commented 2 years ago

Thanks @mrxz!

obviously add little benefit over just using emissive materials, unless the sampling is updated as well.

Yes sampling would need to be updated to use multiple importance sampling / next even estimation along side the environment map importance sampling. When enabling / disabling MIS the scenes should resolve to the same level of lighting intensity (though non MIS should take a lot longer)

Handle edge cases of perfect mirrors and directly hitting an area light (no bounce)

Can you explain this edge case a bit more?

Pack area light info into a texture for use in the shader (similar to how materials are handled)

Yup! I'm imagining something like LightsTexture that can eventually be expanded to support other light types like directional, spot, sphere, etc that can be updated similarly to the MaterialsTexture.

It would be great to have a simple example scene with an area light that can be adjusted and moved around with something like the TransformControls for testing, as well!

mrxz commented 2 years ago

I've created a PR with an initial attempt.

When enabling / disabling MIS the scenes should resolve to the same level of lighting intensity (though non MIS should take a lot longer)

This is indeed the case, though the largest chunk of the speed-up seems to stem from the next-event estimation and not so much the multiple importance sampling.

Can you explain this edge case a bit more?

When applying next-event estimation the direct and indirect light is sampled separately. The direct light should only come from sampled rays towards the light source. If a scattering ray happens to hit a light, ending the ray there without adding the light's emission prevents the light from being accounted for twice. The typical exceptions for this are rays without bounces (directly from camera to light) or rays that go through a perfect mirror and then hit the light.

But it seems that due to the way the multiple importance sampling is done using the sample of the last bounce (as was already done for the environment map, and in the PR now also for area lights) this second case doesn't seem to be an issue.

Yup! I'm imagining something like LightsTexture that can eventually be expanded to support other light types like directional, spot, sphere, etc that can be updated similarly to the MaterialsTexture.

I've implemented it that way indeed. There is currently no 'type' field, but that's easily added as soon as it's needed. The shader does have a Light struct and relevant methods that do all the 'magic' of create a random sample and computing the pdf. So when additional light types are introduced it shouldn't be too hard to do (though each one has its own quirks, of course).

I did contemplate making the environment map a light type as well, allowing some of the code paths/logic to be combined. On one hand it is effectively a source of direct light, but at the same time there can, and should, only be one. Didn't do it for this PR, as I think it would've made it harder to see/review the changes.

It would be great to have a simple example scene with an area light that can be adjusted and moved around with something like the TransformControls for testing, as well!

I've included an example in the PR that does have TransformControls to move around one of the area lights. It's pretty bare-bones and honestly quite lacklustre, but it does at least demonstrate the benefit of NEE/MIS with area lights. Suggestions or ideas are welcome.