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

Optimize Occlusion culling jitter #99974

Merged
merged 1 commit into from
Dec 9, 2024

Conversation

Flarkk
Copy link
Contributor

@Flarkk Flarkk commented Dec 3, 2024

This PR ensures the jittering is applied in a uniform manner across the whole viewport.
Previously, pixels closer to the center with farther Z used to get less jitter than those on the edge, as a side effect of perspective division.

This is achieved by jittering the near plane corners instead of the projection matrix, as only those are ultimately passed to embree in the end anyway.

Test project : occlusion_jitter.zip

This improvement supersedes the fix in #99941.

Before After
391694701-b5ee252d-38ba-4d0e-904b-fb8b7ee9ea80 output

Edit : Resolves #53288

@Flarkk Flarkk requested a review from a team as a code owner December 3, 2024 17:02
@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 3, 2024

@lawnjelly this is the improvement we touched base earlier on.

@lawnjelly
Copy link
Member

lawnjelly commented Dec 4, 2024

So this is effectively jittering the translation rather than the camera rotation, as discussed in #99941 (comment) ?

I did think of a snag with translation though, so sorry I may have led you on a wild goose chase here ... if my thinking is correct, rotation does have the advantage that it automatically compensates for distance:

The effective gap which will cause the problem described in #53288 does depend on distance, because it's the screen space distance that determines whether two adjacent walls will "merge" in the occlusion buffer. Rotation of the camera means the screen space change of jittered objects will be the same with distance I think which is what we want (the absolute jitter in view space will increase).

Translation will cause the opposite effect if I'm correct (with perspective projection):

  • Objects close to the camera will be shifted more than objects far away, rather than less

I suspect we need to make sure the test case works correctly at different distances.

If this is correct, then rotation may be better, even if it produced the undesirable effect that there is more jitter in the centre than the edges, due to perspective. I guess in that case we just calibrate to the centre, and live with slightly less occlusion than ideal at the edges.

Another alternative as I suggested in the original PR is to jitter each ray individually. Even with uniform jitter, you could adjust the magnitude to compensate for the edge effect. Although jittering or adjusting per pixel may be overkill, and an unnecessary expense on CPU for some possible benefit that may not be realised.

UPDATE:
This could be why the box appears to be jittering less than the sphere in your example after the PR. I've just checked and the box is further from the camera than the sphere.

@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 4, 2024

So this is the opposite : this PR jitters more like a rotation, while it's a translation right now.

You're right that translating moves closer objects more.
This is precisely why the sphere jitters more on the left : due to the spherical shape and the perspective, pixels on its left side have a closer Z than those on its right side.
The box has instead a uniform jitter because it's flat and laying in the XY plane.

The current implementation (with my last PR merged) translates the whole scene relatively to the rays (equivalently the rays relatively to the scene) in clip space.

What this PR does is actually the alternative you mention above.
It translates only the tips of the rays on the near plane (their origin - the camera - stays fixed).
This is not exactly a rotation but acts more like it, with the additional benefit of allowing pixel perfect jittering.
Of course the implementation doesn't jitter each ray tip one by one, but instead the near plane bottom-left corner. As the code then interpolates from this corner across X and Y across the whole near plane to determine the rays' tips, this effectively jitters each ray.

Will take a look at #53288 and see if this PR solves the problem.

@Flarkk Flarkk force-pushed the improve_occlusion_jitter branch from 8404d84 to d1192ab Compare December 4, 2024 11:48
@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 4, 2024

Rebased on top of #99941

@lawnjelly
Copy link
Member

So this is the opposite : this PR jitters more like a rotation, while it's a translation right now.

If this is the case, this should indeed be an improvement. Yup some decent testing over the distance range will be good.

If I remember right, ideally we should like to see objects jitter by around a pixel (just under in fact) no matter the distance from the camera.

@Flarkk Flarkk force-pushed the improve_occlusion_jitter branch from d1192ab to a61bd84 Compare December 4, 2024 13:46
@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 4, 2024

I updated the PR to raise the jitter magnitude to 0.99 pixel.

With these settings I can confirm that :

  • jitter is 1 pixel wide no matter the occluder's Z position
  • no occluder does not jitter (when the amplitude is set too low below 1, it can happen that an occluder's edge is jittered within the same pixel)

occlusion_jitter_2.zip

The more to the right, the farther :

Scene top view Result
Capture d’écran du 2024-12-04 14-40-04 output1

I can also confirm that this seems to greatly improve the situation described in #53288.
The gap causing the red box to be culled is now much smaller and coincides with the moment jitter fills the gap over all 9 frames.

Not culled Culled
Capture d’écran du 2024-12-04 14-45-17 Capture d’écran du 2024-12-04 14-43-41
output output2

@lawnjelly
Copy link
Member

lawnjelly commented Dec 4, 2024

That looks great.

From memory I suspect the ideal value (less than 1) for the current pattern may be 0.66, so this is worth testing too.

The jitter pattern I think is one in the centre, a medium square and a distance square (9 in total). At 1, I suspect it would be mimicking the situation where the whole scene was shifted to the left by a whole pixel, so I'm not absolutely sure we gain as much in terms of resolution.

Effectively by jittering we are aiming to increase the resolution of the occlusion buffer, in this case by 9x (with 3 x the resolution on each axis), therefore the fractions should be 0.0 (centre), 0.333 (mid square), 0.666 (far square). The multiplier we are changing here is for the far square as the mid square is multiplied by 0.5.

Essentially the way I was thinking is that by regularizing this, we are maximizing the chance of a gap occurring in the occlusion buffer for these gaps.

The alternative of a more random pattern is potentially even better, but suffers from difficulty in repeatability for the timeout (of course you could use a fixed random pattern as an alternative).

We can increase the resolution higher with more samples, but that increases the timeout required for stability and makes it more liable to pop in.

A priori I'm not absolutely sure what the best value would be here as we are in uncharted territory - I'm not sure I've seen anyone else using this technique with raster occlusion culling, so it's just a combination of some theory with some empirical testing. We could equally well say that say, 0.5 would deal with even smaller gaps (at the risk of missing some larger ones maybe).

@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 4, 2024

With 0.66.
Looks like the gap is even smaller.
Will change the PR right away.

Not culled Culled
image Capture d’écran du 2024-12-04 17-04-01
output2 output

@Flarkk Flarkk force-pushed the improve_occlusion_jitter branch from a61bd84 to 1dc3aaf Compare December 4, 2024 16:16
@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 4, 2024

Done. You can review now.
Shall we consider this closes #53288 ?
I'm unsure we will achieve anything better with a ray tracing approach.

@lawnjelly
Copy link
Member

Sounds good.

I suppose if the occlusion buffer was a certain fixed integer multiple proportion of the screen resolution it should be possible to remove the problem entirely by this super sampling, but the choice of occlusion buffer size is currently pretty complex to do with how many cores. I think we'd need there to be people reporting problems for us to do things like start altering the occlusion buffer sizes.

e.g. at 1920x1080 you would use e.g. 480x270, and use 3 extra temporal jittered samples on each axis.

Might be worth doing on e.g. a console, but probably not for general use at the moment.

@Flarkk Flarkk force-pushed the improve_occlusion_jitter branch from 1dc3aaf to c7895ca Compare December 4, 2024 16:40
@lawnjelly
Copy link
Member

I'm not super familiar with the 4.x release process now, but will bump to 4.4 and add to 4.4 merge queue as this should be a good addition.

@lawnjelly lawnjelly modified the milestones: 4.x, 4.4 Dec 5, 2024
@Repiteo Repiteo merged commit 533091a into godotengine:master Dec 9, 2024
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Dec 9, 2024

Thanks!

@Flarkk Flarkk deleted the improve_occlusion_jitter branch December 9, 2024 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Raster occlusion incorrectly culls objects through gaps
4 participants