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

Enable rendering with unbounded far distance #99986

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Flarkk
Copy link
Contributor

@Flarkk Flarkk commented Dec 3, 2024

This PR allows rendering with unlimited camera zfar - or rather limited by the sole floating point range i.e. ~3.4e+38 in single precision.

This effectively untaps the potential of reverse-z depth buffer with single precision build, in all 3 renderers Forward+, Mobile and Compatibility. XR is not covered though (see notes below).

Outline

Currently zfar cannot be pushed further than barely ~1e6 times znear because of numerical precision limitations with some methods of struct Projection.
With the default znear = 0.05 this sets the maximum view distance to ~50 km.
While it's enough in most cases, it can be a hard limitation for open worlds, space games, and any very large scene.
Beyond this limit, scene rendering currently breaks and Godot starts spaming errors.

This PR removes this limitation with 3 key changes :

  • Stores the 6 frustum planes alongside the projection matrix in the rendering server's internals. Both are equivalent representations except that the former allows retrieving near/far distances and boundary points with much more accurate numerical precision
  • Restricts the use of the matrix representation to the sole cases where projections / un-projections are performed. Use the 6 planes in any other situation (typically to retrieve znear / zfar, get boundary points, etc...)
  • Bundles the 6 planes representation into a new struct Frustum with a few helper methods for convenience. This avoids relying of Vector<Plane> everywhere and improves readability

This PR is in a good enough state to get first reviews. There are still a few details to be ironed out though :

Demo : full-scale scene

large_zfar.zip

Vegetation is ~1m tall and ~10m from the observer
The terrain is 10 km large
The moon's radius is 1,700 km and is 400,000 km from the observer
The Andromeda galaxy is 152,000 light-years wide and is 2.5 million light years from the observer

image

Performance

This PR improves very slightly the frame process time of an empty scene by ~ 0.5%.
This is likely due to the significant number of planes retrieval operations avoided by the fact the 6 planes are already made available.

Before After
Capture d’écran du 2024-12-06 13-16-21 Capture d’écran du 2024-12-06 12-56-13

Notes

  • XR is left unchanged for now because this would require the XR APIs to return frustum planes in addition to the projection matrix. This may be the subject of another PR
  • I spent a considerable amount of time making sure all effects work with large zfar. Some require additional PRs to be merged.
    Test project for effects : effects.zip (make sure to preview the large znear camera in each scene)
Effects Works with large zfar Issue
Bokeh DOF ✔️ with #99755
SSR ✔️ with #99693
SSIL ✔️
SSAO ✔️
Sky ✔️ Sometimes hides far away objects in Compatibility and Mobile ?
Shadows ✔️
SDFGI Crash with zfar or znear > ~1e+20 : #99967
SSS ✔️ with #99755
Fog ✔️

Closes godotengine/godot-proposals#10515
Closes #55070
Supersedes #95944

@Flarkk Flarkk requested review from a team as code owners December 3, 2024 21:43
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/frustum.cpp Show resolved Hide resolved
core/math/projection.cpp Show resolved Hide resolved
@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 4, 2024

Rebased and fixed style issues to allow CI to run.

@Flarkk Flarkk force-pushed the large_zfar branch 3 times, most recently from 06693c1 to b8eda47 Compare December 4, 2024 09:09
@Mickeon Mickeon added this to the 4.x milestone Dec 4, 2024
@Flarkk Flarkk changed the title Unbound camera zfar for scene rendering Enable rendering with unbounded far distance Dec 5, 2024
@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 9, 2024

Rebased on top of #99974

@Calinou
Copy link
Member

Calinou commented Dec 23, 2024

Objects farther than 1e20 disappear when Occlusion culling is on

This can probably be worked around by assuming the object is visible when it's further away from the camera than this distance (and skipping any occlusion culling checks).

Embree doesn't support double precision, so precision=double builds can't do any better here.

@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 23, 2024

Haven't got back to this PR yet, but I think it's likely caused by an overflow with normalize() or length() somewhere.

1e19 ~ 1e20 is the threshold value that exceeds the maximum representable float value when squared (~1e38).

If this overflow happens in Embree then yes, we don't have any other choice but trying to work around it. Will take a look at your suggestion once I complete the work on another incoming related PR : uncapping zfar in the editor itself.

@Flarkk
Copy link
Contributor Author

Flarkk commented Dec 29, 2024

I got the culprit : indeed a call to length() that overflows on very far away geometry :

float min_depth = (closest_point - p_cam_position).length();

Just issued #100907 that solves the problem, and enables proper occlusion culling even on very far away objects.


Edit : turns out that this fix inadvertently reverts another fix. I'm figuring out alternatives

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants