@@ -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)
@@ -2548,14 +2571,13 @@ void Renderer::renderObject(const Vector3f& pos,
25482571
25492572 cloudTex->bind ();
25502573
2551- // Cloud layers can be trouble for the depth buffer, since they tend
2552- // to be very close to the surface of a planet relative to the radius
2553- // of the planet. We'll help out by offsetting the cloud layer toward
2554- // the viewer.
2574+ // Pull clouds toward the viewer to avoid z-fighting with the surface
2575+ // (sign flipped under reverse-Z where closer = larger depth).
25552576 if (distance > radius * 1 .1f )
25562577 {
25572578 glEnable (GL_POLYGON_OFFSET_FILL );
2558- glPolygonOffset (-1 .0f , -1 .0f );
2579+ float offset = gl::reverseZ ? 1 .0f : -1 .0f ;
2580+ glPolygonOffset (offset, offset);
25592581 }
25602582
25612583 if (lit)
@@ -5556,6 +5578,31 @@ Renderer::buildDepthPartitions()
55565578 // We want to avoid overpartitioning the depth buffer. In this stage, we
55575579 // coalesce partitions that have small spans in the depth buffer.
55585580 // TODO: Implement this step!
5581+
5582+ // Collapse partitions: reverse-Z gives uniform precision, so the per-
5583+ // interval depth-range squeeze is pure waste.
5584+ if (gl::reverseZ && nIntervals > 1 )
5585+ {
5586+ float collapsedNearZ = depthPartitions[0 ].nearZ ;
5587+ float collapsedFarZ = depthPartitions[0 ].farZ ;
5588+ for (int j = 1 ; j < nIntervals; j++)
5589+ {
5590+ collapsedNearZ = std::max (collapsedNearZ, depthPartitions[j].nearZ );
5591+ collapsedFarZ = std::min (collapsedFarZ, depthPartitions[j].farZ );
5592+ }
5593+ // Floor at -MinNearPlaneDistance: fp32 noise around 0 in the closest
5594+ // partition can flip nearZ positive, which makes the reverse-Z near
5595+ // negative and surface depths jitter near vs far frame to frame.
5596+ collapsedNearZ = std::min (collapsedNearZ, -MinNearPlaneDistance);
5597+ depthPartitions.clear ();
5598+ DepthBufferPartition collapsed;
5599+ collapsed.index = 0 ;
5600+ collapsed.nearZ = collapsedNearZ;
5601+ collapsed.farZ = collapsedFarZ;
5602+ depthPartitions.push_back (collapsed);
5603+ nIntervals = 1 ;
5604+ }
5605+
55595606 return nIntervals;
55605607}
55615608
0 commit comments