@@ -439,8 +439,25 @@ bool Renderer::init(int winWidth, int winHeight,
439439 detailOptions.useMesaPackInvert = false ;
440440#endif
441441
442- // LEQUAL rather than LESS required for multipass rendering
443- glDepthFunc (GL_LEQUAL );
442+ // Reverse-Z needs clip-control for ZERO_TO_ONE NDC; without it, fall back
443+ // to standard partitioning rather than the precision-poor [-1, +1] variant.
444+ if (gl::reverseZ && !(gl::ARB_clip_control || gl::EXT_clip_control))
445+ {
446+ gl::reverseZ = false ;
447+ GetLogger ()->warn (" ReverseZ requested but clip-control is unavailable; falling back to standard depth.\n " );
448+ }
449+
450+ if (gl::reverseZ)
451+ {
452+ glClipControl (GL_LOWER_LEFT , GL_ZERO_TO_ONE );
453+ glDepthFunc (GL_GEQUAL );
454+ glClearDepthf (0 .0f );
455+ }
456+ else
457+ {
458+ glDepthFunc (GL_LEQUAL );
459+ glClearDepthf (1 .0f );
460+ }
444461
445462 resize (winWidth, winHeight);
446463
@@ -454,6 +471,12 @@ void Renderer::resize(int width, int height)
454471 viewportHeight = height;
455472 projectionMode->setSize (static_cast <float >(viewportWidth), static_cast <float >(viewportHeight));
456473 m_orthoProjMatrix = math::Ortho2D (0 .0f , static_cast <float >(viewportWidth), 0 .0f , static_cast <float >(viewportHeight));
474+ if (gl::reverseZ)
475+ {
476+ // Remap Ortho2D z output from [-1, +1] to [0, +1] reverse-Z.
477+ m_orthoProjMatrix (2 , 2 ) = -0 .5f ;
478+ m_orthoProjMatrix (2 , 3 ) = 0 .5f ;
479+ }
457480}
458481
459482void Renderer::setFieldOfView (float _fov)
@@ -2555,14 +2578,13 @@ void Renderer::renderObject(const Vector3f& pos,
25552578
25562579 cloudTex->bind ();
25572580
2558- // Cloud layers can be trouble for the depth buffer, since they tend
2559- // to be very close to the surface of a planet relative to the radius
2560- // of the planet. We'll help out by offsetting the cloud layer toward
2561- // the viewer.
2581+ // Pull clouds toward the viewer to avoid z-fighting with the surface
2582+ // (sign flipped under reverse-Z where closer = larger depth).
25622583 if (distance > radius * 1 .1f )
25632584 {
25642585 glEnable (GL_POLYGON_OFFSET_FILL );
2565- glPolygonOffset (-1 .0f , -1 .0f );
2586+ float offset = gl::reverseZ ? 1 .0f : -1 .0f ;
2587+ glPolygonOffset (offset, offset);
25662588 }
25672589
25682590 if (lit)
@@ -5447,6 +5469,31 @@ Renderer::buildDepthPartitions()
54475469 // We want to avoid overpartitioning the depth buffer. In this stage, we
54485470 // coalesce partitions that have small spans in the depth buffer.
54495471 // TODO: Implement this step!
5472+
5473+ // Collapse partitions: reverse-Z gives uniform precision, so the per-
5474+ // interval depth-range squeeze is pure waste.
5475+ if (gl::reverseZ && nIntervals > 1 )
5476+ {
5477+ float collapsedNearZ = depthPartitions[0 ].nearZ ;
5478+ float collapsedFarZ = depthPartitions[0 ].farZ ;
5479+ for (int j = 1 ; j < nIntervals; j++)
5480+ {
5481+ collapsedNearZ = std::max (collapsedNearZ, depthPartitions[j].nearZ );
5482+ collapsedFarZ = std::min (collapsedFarZ, depthPartitions[j].farZ );
5483+ }
5484+ // Floor at -MinNearPlaneDistance: fp32 noise around 0 in the closest
5485+ // partition can flip nearZ positive, which makes the reverse-Z near
5486+ // negative and surface depths jitter near vs far frame to frame.
5487+ collapsedNearZ = std::min (collapsedNearZ, -MinNearPlaneDistance);
5488+ depthPartitions.clear ();
5489+ DepthBufferPartition collapsed;
5490+ collapsed.index = 0 ;
5491+ collapsed.nearZ = collapsedNearZ;
5492+ collapsed.farZ = collapsedFarZ;
5493+ depthPartitions.push_back (collapsed);
5494+ nIntervals = 1 ;
5495+ }
5496+
54505497 return nIntervals;
54515498}
54525499
0 commit comments