Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix black spots appearing due to NANs when SSAO is enabled #8926

Merged
merged 4 commits into from
Jul 1, 2023

Conversation

Elabajaba
Copy link
Contributor

@Elabajaba Elabajaba commented Jun 22, 2023

Objective

Fixes #8925

Solution

Clamp the bad values.

Normalize the prepass normals when we get them in the prepass_normal() function.

More Info

The issue is that NdotV is sometimes very slightly greater than 1 (maybe FP rounding issues?), which caused F_Schlick() to return NANs in pow(1.0 - NdotV, 5.0) (call stack looked likepbr() -> directional_light() -> Fd_Burley() -> F_Schlick())

@Elabajaba
Copy link
Contributor Author

Another option that fixes it is to wrap prepass_normal() here

pbr_input.N = prepass_normal(in.frag_coord, 0u);
with normalize()

@Selene-Amanita Selene-Amanita added C-Bug An unexpected or incorrect behavior A-Rendering Drawing game state to the screen labels Jun 22, 2023
@JMS55 JMS55 added this to the 0.11 milestone Jun 22, 2023
@Elabajaba
Copy link
Contributor Author

I changed it to just normalize the prepass_normal() return value to avoid any potential loss of data from clamping.

We do this for apply_normal_mapping() when the prepass normals aren't used here

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jun 23, 2023
Copy link
Contributor

@superdump superdump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned on Discord, I think this solution is probably not the best choice.

The prepass normals are stored in an Rgb10a2Unorm texture. That means the values must be in the range [0,1] in the texture. I’m not certain but I think any values outside of that range would be clamped to that range.

Clamping is a problem because it changes the direction of the normal, which is undesirable for lighting. Normalising is probably a better choice.

Mikktspace says that when using normal mapping, you are not supposed to normalise the vertex normal, nor the interpolated vertex normal in order to obtain an exact inverse of the normal map baking process. You only normalise the normal-mapped normal which is the one used for lighting. This is done at the end of apply_normal_mapping.

So, the problem here is that mikktspace would potentially allow normals that don’t fit in Rgb10a2Unorm, and would be clamped, and then perhaps also that the world normal is not normalised.

I think then maybe the best thing to do is to go against the mikktspace recommendations and normalise the world normal before writing to the texture. I’m not certain of this but I’d say until we have clarity on what to do, this is possibly a bit controversial.

On the other hand, it is better than stuff doesn’t look broken so maybe we should add a FIXME comment to this, merge it, and continue figuring out how best to address it.

@alice-i-cecile alice-i-cecile removed the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jun 23, 2023
@Elabajaba
Copy link
Contributor Author

@superdump sorry, I forgot to update the PR description.

I removed all the clamps, and this just normalizes the prepass_normal() call.

@IceSentry
Copy link
Contributor

IceSentry commented Jun 26, 2023

We do this for apply_normal_mapping() when the prepass normals aren't used here

Unless something else changed, the prepass already calls apply_normal_mapping() so the results should already be normalized the exact same way. If they aren't it might mean there's a missing shader def in the prepass or something.

@IceSentry
Copy link
Contributor

IceSentry commented Jun 26, 2023

Also, I think this shouldn't be done in prepass_normal() but should be done at the callsite. The normal texture can be generated by a custom material and normalizing there might not be what user expects. So I'd just do it directly in pbr.wgsl.

@IceSentry
Copy link
Contributor

It was pointed out on discord that this happens because of precision issues even if the normals are normalized in the prepass. So it makes sense to do it like this. So you can ignore my comments.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jun 27, 2023
@alice-i-cecile
Copy link
Member

@superdump are you okay if we merge this?

@fintelia
Copy link
Contributor

fintelia commented Jul 1, 2023

The saturate method is defined as:

Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector.

So it doesn't seem to make much sense to do clamp(saturate(...), 0.0, 1.0)...

Edit: Seems that was removed in some intermediate commit

Copy link
Contributor

@superdump superdump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some more discussions on discord and here, I’m fine with this solution.

@superdump superdump added this pull request to the merge queue Jul 1, 2023
Merged via the queue into bevyengine:main with commit 94291cf Jul 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Black spots when using SSAO
7 participants