godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add support for infinite projection matrices #10515

Open Flarkk opened 3 weeks ago

Flarkk commented 3 weeks ago

Describe the project you are working on

A space settled 3D game where both very far away celestial bodies and closer objects (terrains, spaceships ...) are rendered at the same time.

Describe the problem or limitation you are having in your project

I need both a normal near plane distance (say 5e-2) and a far plane very far away (say 1e18, or even INF). When near and far planes are apart from each other of an order of magnitude greater than ~1e7, an error is raised at culling and the scene doesn't render.

Worth noting that now Reverse z-buffer is a reality, it opens the door in theory to rendering such deep scenes without z-fighting (especially when objects are spaced and sized exponentially, like stars, planets and ground objects), and without having to rely on heavier solutions like rendering in multiple passes or multiple cameras.

In detail, when near and far places are set very far apart, the projection matrix generated rounds to an infinite projection matrix (see slide 7) due to numerical precision effects. At scene culling stage, the 6 clipping plans are extracted from the matrix following the popular Ggribb's method (see Projection::get_projection_planes(). This yields by construction a zero vector for the far plane normal when the projection matrix is infinite. This zero vector raises an error later during the light culling preparation.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Support infinite far planes on Camera3D in PROJECTION_PERSPECTIVE, PROJECTION_FRUSTUM and PROJECTION_ORTHOGONAL modes. This should be supported for both scene culling and geometry projection.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Because the near and far distances are used for both scene culling and geometry projection, they are 2 potential changes to consider :

  1. Default to infinite projection matrix for geometry projection There is some litterature on the fact that combining infinite projection with reverse-z doesn't degrade numerical error rates, even with scenes where geometry is not far away (see this one : https://developer.nvidia.com/content/depth-precision-visualized and that one : https://www.reedbeta.com/blog/depth-precision-visualized/). In practice, this would mean that for PROJECTION_PERSPECTIVE, PROJECTION_FRUSTUM and PROJECTION_ORTHOGONAL, the far distance value provided by the user is ignored and an infinite projection matrix is generated regardless.

  2. Allow ignoring far distance for scene culling There could be a new boolean attribute on Camera3D to allow the user to toggle it on/off. When toggled on, Projection::get_projection_planes() could branch and generate an adequate normal and distance-to-origin for the far plane. I tested this scrappy implementation and at first sight it seems to work :

    
    ///////--- Far Plane ---///////
    if (ignore_far_plane_for_culling) {
    /* Take the opposite of the near plane's normal.
        Assumption is made they're parallel.
        As godot currently doesn't allow the user to build the projection matrix from scratch,
        there shouldn't be any edge case */
    new_plane.normal = -planes[0].normal;
    new_plane.d = INFINITY; // distance is infinity
    } else {
    new_plane = Plane(matrix[3] - matrix[2],
        matrix[7] - matrix[6],
        matrix[11] - matrix[10],
        matrix[15] - matrix[14]);
    
    new_plane.normal = -new_plane.normal;
    new_plane.normalize();
    }

planes.write[1] = p_transform.xform(new_plane);



Alternatively, if we don't want to go down the route of defaulting to infinite projection, there could be additional projection modes added to `Camera3D`, say `PROJECTION_PERSPECTIVE_INFINITE`,  `PROJECTION_FRUSTUM_INFINITE` and  `PROJECTION_ORTHOGONAL_INFINITE`, that would trigger both changes above.

### If this enhancement will not be used often, can it be worked around with a few lines of script?

No, it's core

### Is there a reason why this should be core and not an add-on in the asset library?

Culling and projection are core.
Calinou commented 3 weeks ago

Alternatively, if we don't want to go down the route of defaulting to infinite projection, there could be an additional projection mode added to Camera3D, say PROJECTION_PERSPECTIVE_INFINITE, that would trigger both changes above.

What about the Orthogonal and Frustum modes? It seems these could support an infinite far distance too.

Flarkk commented 3 weeks ago

Correct. Just updated my proposal.