Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions celestia.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -527,4 +527,11 @@ StarTextures
#------------------------------------------------------------------------
# SRGBRendering true

#------------------------------------------------------------------------
# ReverseZ replaces depth-buffer partitioning with reverse-Z + an infinite
# far plane. Requires ARB_clip_control / EXT_clip_control (or GL 4.5+);
# falls back to standard partitioning if unavailable.
# The default value is false.
#------------------------------------------------------------------------
# ReverseZ true
}
17 changes: 17 additions & 0 deletions src/celengine/fisheyeprojectionmode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// of the License, or (at your option) any later version.

#include "fisheyeprojectionmode.h"
#include <celengine/glsupport.h>
#include <celmath/frustum.h>
#include <celmath/geomutil.h>
#include <celengine/shadermanager.h>
Expand All @@ -25,6 +26,17 @@ FisheyeProjectionMode::FisheyeProjectionMode(float width, float height, int scre
Eigen::Matrix4f FisheyeProjectionMode::getProjectionMatrix(float nearZ, float farZ, float /*zoom*/) const
{
float aspectRatio = width / height;
if (gl::reverseZ)
{
// Reverse-Z, output z in [0, +1]. Only row 2 differs from standard
// Ortho — patch it in place.
Eigen::Matrix4f m = math::Ortho(-aspectRatio, aspectRatio,
-1.0f, 1.0f, nearZ, farZ);
float d = farZ - nearZ;
m(2, 2) = 1.0f / d;
m(2, 3) = farZ / d;
return m;
}
return math::Ortho(-aspectRatio, aspectRatio, -1.0f, 1.0f, nearZ, farZ);
}

Expand Down Expand Up @@ -80,6 +92,11 @@ float FisheyeProjectionMode::getNormalizedDeviceZ(float nearZ, float farZ, float
// just apply a linear transformation since fisheye uses
// orthographic projection already.
float d0 = farZ - nearZ;
if (gl::reverseZ)
{
// Mirrors the near/far swap in getProjectionMatrix.
return (z - nearZ) / d0 * 2.0f - 1.0f;
}
return 1.0f - (z - nearZ) / d0 * 2.0f;
}

Expand Down
10 changes: 10 additions & 0 deletions src/celengine/glsupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ CELAPI bool OES_geometry_shader = false; //NOSONAR
#else
CELAPI bool ARB_invalidate_subdata = false; //NOSONAR
#endif
CELAPI bool ARB_clip_control = false; //NOSONAR
CELAPI bool ARB_texture_compression_bptc = false; //NOSONAR
CELAPI bool EXT_clip_control = false; //NOSONAR
CELAPI bool EXT_texture_compression_s3tc = false; //NOSONAR
CELAPI bool EXT_texture_compression_s3tc_srgb = false; //NOSONAR
CELAPI bool EXT_texture_filter_anisotropic = false; //NOSONAR
Expand All @@ -25,6 +27,7 @@ CELAPI GLint maxTextureSize = 0; //NOSONAR
CELAPI GLfloat maxLineWidth = 0.0f; //NOSONAR
CELAPI GLint maxTextureAnisotropy = 0; //NOSONAR
CELAPI bool sRGBRendering = false; //NOSONAR
CELAPI bool reverseZ = false; //NOSONAR

namespace
{
Expand Down Expand Up @@ -102,6 +105,13 @@ bool init(util::array_view<std::string> ignore) noexcept
EXT_texture_filter_anisotropic = check_extension(ignore, "GL_EXT_texture_filter_anisotropic") || check_extension(ignore, "GL_ARB_texture_filter_anisotropic");
EXT_texture_sRGB_R8 = check_extension(ignore, "GL_EXT_texture_sRGB_R8");
MESA_pack_invert = check_extension(ignore, "GL_MESA_pack_invert");
#ifdef GL_ES
EXT_clip_control = check_extension(ignore, "GL_EXT_clip_control");
#else
ARB_clip_control = check_extension(ignore, "GL_ARB_clip_control")
|| checkVersion(celestia::gl::GL_4_5);
EXT_clip_control = check_extension(ignore, "GL_EXT_clip_control");
#endif

std::array<GLint, 2> pointSizeRange = { 0, 0 };
std::array<GLfloat, 2> lineWidthRange = { 0.0f, 0.0f };
Expand Down
3 changes: 3 additions & 0 deletions src/celengine/glsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ enum Version
#ifndef GL_ES
extern CELAPI bool ARB_invalidate_subdata; //NOSONAR
#endif
extern CELAPI bool ARB_clip_control; //NOSONAR
extern CELAPI bool ARB_texture_compression_bptc; //NOSONAR
extern CELAPI bool EXT_clip_control; //NOSONAR
extern CELAPI bool EXT_texture_compression_s3tc; //NOSONAR
extern CELAPI bool EXT_texture_compression_s3tc_srgb; //NOSONAR
extern CELAPI bool EXT_texture_filter_anisotropic; //NOSONAR
Expand All @@ -62,6 +64,7 @@ extern CELAPI GLint maxTextureSize; //NOSONAR
extern CELAPI GLfloat maxLineWidth; //NOSONAR
extern CELAPI GLint maxTextureAnisotropy; //NOSONAR
extern CELAPI bool sRGBRendering; //NOSONAR
extern CELAPI bool reverseZ; //NOSONAR

bool init(util::array_view<std::string> = {}) noexcept;
bool checkVersion(int) noexcept;
Expand Down
11 changes: 11 additions & 0 deletions src/celengine/perspectiveprojectionmode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// of the License, or (at your option) any later version.

#include "perspectiveprojectionmode.h"
#include <celengine/glsupport.h>
#include <celmath/frustum.h>
#include <celmath/geomutil.h>
#include <celengine/shadermanager.h>
Expand All @@ -22,6 +23,11 @@ PerspectiveProjectionMode::PerspectiveProjectionMode(float width, float height,

Eigen::Matrix4f PerspectiveProjectionMode::getProjectionMatrix(float nearZ, float farZ, float zoom) const
{
if (gl::reverseZ)
{
return math::PerspectiveReverseZInfiniteZeroToOne(
math::radToDeg(getFOV(zoom)), width / height, nearZ);
}
return math::Perspective(math::radToDeg(getFOV(zoom)), width / height, nearZ, farZ);
}

Expand Down Expand Up @@ -79,6 +85,11 @@ double PerspectiveProjectionMode::getViewConeAngleMax(float zoom) const

float PerspectiveProjectionMode::getNormalizedDeviceZ(float nearZ, float farZ, float z) const
{
if (gl::reverseZ)
{
// -1 at near, +1 at far; Ortho2D negates this for the annotation pass.
return 1.0f - 2.0f * nearZ / z;
}
float d0 = farZ - nearZ;
float d1 = -(farZ + nearZ) / d0;
float d2 = -2.0f * nearZ * farZ / d0;
Expand Down
61 changes: 54 additions & 7 deletions src/celengine/render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,25 @@
detailOptions.useMesaPackInvert = false;
#endif

// LEQUAL rather than LESS required for multipass rendering
glDepthFunc(GL_LEQUAL);
// Reverse-Z needs clip-control for ZERO_TO_ONE NDC; without it, fall back
// to standard partitioning rather than the precision-poor [-1, +1] variant.
if (gl::reverseZ && !(gl::ARB_clip_control || gl::EXT_clip_control))
{
gl::reverseZ = false;
GetLogger()->warn("ReverseZ requested but clip-control is unavailable; falling back to standard depth.\n");
}

if (gl::reverseZ)
{
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
glDepthFunc(GL_GEQUAL);
glClearDepthf(0.0f);
}
else
{
glDepthFunc(GL_LEQUAL);
glClearDepthf(1.0f);
}

resize(winWidth, winHeight);

Expand All @@ -454,6 +471,12 @@
viewportHeight = height;
projectionMode->setSize(static_cast<float>(viewportWidth), static_cast<float>(viewportHeight));
m_orthoProjMatrix = math::Ortho2D(0.0f, static_cast<float>(viewportWidth), 0.0f, static_cast<float>(viewportHeight));
if (gl::reverseZ)
{
// Remap Ortho2D z output from [-1, +1] to [0, +1] reverse-Z.
m_orthoProjMatrix(2, 2) = -0.5f;
m_orthoProjMatrix(2, 3) = 0.5f;
}
}

void Renderer::setFieldOfView(float _fov)
Expand Down Expand Up @@ -2548,14 +2571,13 @@

cloudTex->bind();

// Cloud layers can be trouble for the depth buffer, since they tend
// to be very close to the surface of a planet relative to the radius
// of the planet. We'll help out by offsetting the cloud layer toward
// the viewer.
// Pull clouds toward the viewer to avoid z-fighting with the surface
// (sign flipped under reverse-Z where closer = larger depth).
if (distance > radius * 1.1f)
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -1.0f);
float offset = gl::reverseZ ? 1.0f : -1.0f;
glPolygonOffset(offset, offset);
}

if (lit)
Expand Down Expand Up @@ -5417,7 +5439,7 @@
}

int
Renderer::buildDepthPartitions()

Check failure on line 5442 in src/celengine/render.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 28 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=CelestiaProject_Celestia&issues=AZ6pJobffOtpRbRK2gb_&open=AZ6pJobffOtpRbRK2gb_&pullRequest=2574
{
// Since we're rendering objects of a huge range of sizes spread over
// vast distances, we can't just rely on the hardware depth buffer to
Expand Down Expand Up @@ -5503,7 +5525,7 @@
// Factor of 0.999 makes sure ensures that the near plane does not fall
// exactly at the marker's z coordinate (in which case the marker
// would be susceptible to getting clipped.)
if (-depthSortedAnnotations[0].position.z() > zNearest)

Check warning on line 5528 in src/celengine/render.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge this "if" statement with the enclosing one.

See more on https://sonarcloud.io/project/issues?id=CelestiaProject_Celestia&issues=AZ7cgo808_KnkF1PICof&open=AZ7cgo808_KnkF1PICof&pullRequest=2574
zNearest = -depthSortedAnnotations[0].position.z() * 0.999f;
}

Expand Down Expand Up @@ -5556,6 +5578,31 @@
// We want to avoid overpartitioning the depth buffer. In this stage, we
// coalesce partitions that have small spans in the depth buffer.
// TODO: Implement this step!

// Collapse partitions: reverse-Z gives uniform precision, so the per-
// interval depth-range squeeze is pure waste.
if (gl::reverseZ && nIntervals > 1)
{
float collapsedNearZ = depthPartitions[0].nearZ;
float collapsedFarZ = depthPartitions[0].farZ;
for (int j = 1; j < nIntervals; j++)
{
collapsedNearZ = std::max(collapsedNearZ, depthPartitions[j].nearZ);
collapsedFarZ = std::min(collapsedFarZ, depthPartitions[j].farZ);
}
// Floor at -MinNearPlaneDistance: fp32 noise around 0 in the closest
// partition can flip nearZ positive, which makes the reverse-Z near
// negative and surface depths jitter near vs far frame to frame.
collapsedNearZ = std::min(collapsedNearZ, -MinNearPlaneDistance);
depthPartitions.clear();
DepthBufferPartition collapsed;
collapsed.index = 0;
collapsed.nearZ = collapsedNearZ;
collapsed.farZ = collapsedFarZ;
depthPartitions.push_back(collapsed);
nIntervals = 1;
}

return nIntervals;
}

Expand Down
18 changes: 18 additions & 0 deletions src/celengine/renderglsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ void renderGeometryShadow_GLSL(RenderGeometry* geometry,
shadowFbo->bind();
glViewport(0, 0, shadowFbo->width(), shadowFbo->height());

// The shadow pass is forward-Z throughout (Ortho [-1,+1] NDC, clear=1,
// LEQUAL, positive polygon offset, sampler does `myDepth > pcfDepth`).
// Isolate it from the global reverseZ state.
if (gl::reverseZ)
{
glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
glDepthFunc(GL_LEQUAL);
glClearDepthf(1.0f);
}

// Write only to the depth buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glClear(GL_DEPTH_BUFFER_BIT);
Expand Down Expand Up @@ -115,6 +125,14 @@ void renderGeometryShadow_GLSL(RenderGeometry* geometry,
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glCullFace(GL_BACK);
shadowFbo->unbind(oldFboId);

// Restore reverseZ globals.
if (gl::reverseZ)
{
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
glDepthFunc(GL_GEQUAL);
glClearDepthf(0.0f);
}
}

} // end unnamed namespace
Expand Down
2 changes: 2 additions & 0 deletions src/celestia/celestiacore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2741,9 +2741,11 @@ LoadFontHelper(const Renderer* renderer,

bool CelestiaCore::initRenderer(engine::TextureResolution resolution,
std::optional<bool> sRGBRendering,
std::optional<bool> reverseZ,
[[maybe_unused]] bool useMesaPackInvert)
{
gl::sRGBRendering = sRGBRendering.value_or(config->renderDetails.sRGBRendering);
gl::reverseZ = reverseZ.value_or(config->renderDetails.reverseZ);

if (gl::sRGBRendering)
{
Expand Down
1 change: 1 addition & 0 deletions src/celestia/celestiacore.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class CelestiaCore // : public Watchable<CelestiaCore>
ProgressNotifier* progressNotifier = nullptr);
bool initRenderer(celestia::engine::TextureResolution,
std::optional<bool> sRGBRendering = std::nullopt,
std::optional<bool> reverseZ = std::nullopt,
bool useMesaPackInvert = true);
void start(double t);
void start();
Expand Down
1 change: 1 addition & 0 deletions src/celestia/configfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ applyRenderDetails(CelestiaConfig::RenderDetails& renderDetails, const Associati
applyNumber(renderDetails.ShadowMapSize, hash, "ShadowMapSize"sv);
applyStringArray(renderDetails.ignoreGLExtensions, hash, "IgnoreGLExtensions"sv);
applyBoolean(renderDetails.sRGBRendering, hash, "SRGBRendering"sv);
applyBoolean(renderDetails.reverseZ, hash, "ReverseZ"sv);
applyNumber(renderDetails.stars.pointRadius, hash, "StarPointRadius"sv);
renderDetails.stars.pointRadius = std::clamp(renderDetails.stars.pointRadius, 1.0f, 10.0f);
applyNumber(renderDetails.stars.optimization, hash, "StarOptimization"sv);
Expand Down
1 change: 1 addition & 0 deletions src/celestia/configfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct CelestiaConfig
unsigned int ShadowMapSize{ 0 };
std::vector<std::string> ignoreGLExtensions{ };
bool sRGBRendering{ false };
bool reverseZ{ false };
struct StarRendering
{
float pointRadius{ 1.5f };
Expand Down
7 changes: 6 additions & 1 deletion src/celestia/qt/qtglwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ CelestiaGlWidget::initializeGL()
if (settings.contains("sRGBRendering"))
sRGBOverride = settings.value("sRGBRendering").toBool();

if (!appCore->initRenderer(static_cast<engine::TextureResolution>(textureResolution), sRGBOverride, false))
std::optional<bool> reverseZOverride;
if (settings.contains("ReverseZ"))
reverseZOverride = settings.value("ReverseZ").toBool();

if (!appCore->initRenderer(static_cast<engine::TextureResolution>(textureResolution),
sRGBOverride, reverseZOverride, false))
{
// cerr << "Failed to initialize renderer.\n";
exit(1);
Expand Down
24 changes: 24 additions & 0 deletions src/celmath/geomutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,30 @@ Perspective(T fovy, T aspect, T nearZ, T farZ)
return m;
}

/*! Reverse-Z infinite-far perspective for [0, +1] NDC; requires glClipControl(ZERO_TO_ONE). */
template<class T> Eigen::Matrix<T, 4, 4>
PerspectiveReverseZInfiniteZeroToOne(T fovy, T aspect, T nearZ)
{
using std::cos, std::sin;

if (aspect == static_cast<T>(0))
return Eigen::Matrix<T, 4, 4>::Identity();

T angle = degToRad(fovy / static_cast<T>(2));
T sine = sin(angle);
if (sine == static_cast<T>(0))
return Eigen::Matrix<T, 4, 4>::Identity();
T ctg = cos(angle) / sine;

Eigen::Matrix<T, 4, 4> m = Eigen::Matrix<T, 4, 4>::Zero();
m(0, 0) = ctg / aspect;
m(1, 1) = ctg;
m(2, 2) = static_cast<T>(0);
m(2, 3) = nearZ;
m(3, 2) = static_cast<T>(-1);
return m;
}

/*! Return an orthographic projection matrix
*/
template<class T> Eigen::Matrix<T, 4, 4>
Expand Down
Loading