Forward+: Replace the current BRDF approximation with a DFG LUT and add multiscattering energy compensation - #103934
Conversation
There was a problem hiding this comment.
Tested locally, it works as expected.
Testing project (from godotengine/godot-proposals#4508): test_multiscatter.zip
I'm not sure if the final visual appearance is intended though. I'd have expected the spheres in the bottom-left corner (which are rough and metallic) to have the same color as the background.
Edit: Reading the PR description again, it seems multiscattering isn't implemented yet in the engine by this PR. This PR only does the groundwork that would make it possible, so I guess the visual appearance is intended.
Spheres at the top are smooth, while spheres at the bottom are rough. Spheres at the right are dielectric, while spheres at the left are metallic.
With DirectionalLight3D
| Before | After |
|---|---|
![]() |
![]() |
Without DirectionalLight3D
| Before | After |
|---|---|
![]() |
![]() |
@Calinou Yes, multiscattering will come in a separate PR to keep this PR simple. |
|
How long does the LUT generation take? |
Startup/shutdown time on the MRP I linked seems to be largely unaffected (it's within margin of error on an average of 100 runs for each test). PC specifications
Using the GPUUsing lavapipe (CPU rendering) |
b435208 to
52c9598
Compare
|
@clayjohn If the time it takes Godot to generate this DFG seems like a problem to you... |
|
@LiveTrower how long does the LUT take to generate? Having hard numbers would be helpful. A few ms would be totally fine |
|
Uncached, it takes 10-35ms. |
clayjohn
left a comment
There was a problem hiding this comment.
What is the issue with implementing multiscattering?
We discussed this in the rendering meeting this week and agreed that we should just go straight for the multiscattering LUT unless there is a technical reason not to
I don't understand your question. Could you explain more? |
|
His question is, why not implement multiscattering with this all in one go instead of after this gets merged. |
Okay, that's clearer. The reason was that I wanted to keep the PR simple since it's my first PR, and I wasn't sure if this approach was appropriate because I didn’t know if you'd agree with the time it takes for Godot to generate the DFG LUT, and thus make more changes. My plan was as follows:
These are the main issues:
Fixing these issues would also involve solving the clear coat problems, so I’d rather wait for proper solutions to make it easier to fully implement sheen shading later. So, if you prefer, I can combine the first two PRs into one, but the third one will remain in limbo. |
|
I'd say merging the first two PRs into one makes sense. As far as the blocker PRs mentioned go for sheen shading, I don't know what their statuses are... If they need more work or are in the massive PR review backlog (seems like the latter at a glance) |
Agreed. For context, we discussed this in the rendering meeting today and the consensus was that it looks slightly better (presumably more accurate which is good), but was really very noticeable. Given that we should move to a multiscattering model anyway, we figured it would make more sense to do both together.
They need more work. What they need is someone willing to champion the PRs and help the contributor get them into a mergable state (i.e. improve code quality, test, ensure compatibility is maintained). I helped on the other big overhaul PRs, but I don't have the bandwidth right now to provide that much assistance. |
Calinou
left a comment
There was a problem hiding this comment.
Tested locally, it works as expected.
However, the SPECULAR_GGX constant should be renamed back to SPECULAR_SCHLICK_GGX as this breaks compatibility otherwise. While the Schlick GGX name will be technically incorrect when using Forward+, we favor keeping compatibility with existing projects as much as possible.
Forward+
master |
This PR |
|---|---|
![]() |
![]() |
Mobile
master |
This PR |
|---|---|
![]() |
![]() |
Compatibility
master |
This PR |
|---|---|
![]() |
![]() |
@Calinou Why? Isn't the numerical value used for serialization? If so, how would changing the string name change anything? https://chat.godotengine.org/channel/general/thread/29LjmAoEafWQ8Xwri |
In GDScript if I have a line of code that uses |
Calinou
left a comment
There was a problem hiding this comment.
Code looks good to me.
Please squash the commits together into a single commit. See PR workflow for instructions 🙂
Done. |
clayjohn
left a comment
There was a problem hiding this comment.
Looks great! Thank you for being so responsive to all the requested changes
Requested changes were made
|
Thanks! Congratulations on your first merged contribution! 🎉 |
Forward+: Replace the current BRDF approximation with a DFG LUT and add multiscattering energy compensation
Forward+: Replace the current BRDF approximation with a DFG LUT and add multiscattering energy compensation
















DFG LUT
This PR replaces the approximation described here with an analytical approximation described in real shading, but only for Forward+ rendering.
This implementation takes the approach of generating a 16-bit 128x128 DFG LUT with 1024 samples using a compute shader, and it also includes a modification described by google filament.
Finally, this DFG LUT also has the DG term of the cloth BRDF included in the blue channel for future implementation of cloth model.

Here are some images to compare before and after:
Note: This PR only replaces the approximation in Forward+ rendering, as the current approximation is more suitable for renders like Mobile and Compatibility.
MULTISCATTERING
This PR also implements multiscattering by integrating Filament’s Energy preservation.
Here is a comparison between single scattering and multiscattering:
closes: godotengine/godot-proposals#4508