Skip to content

Commit

Permalink
Experiment sub-centroid patch culling
Browse files Browse the repository at this point in the history
Define 5-9 smaller spheres to use in horizon culling. Idea being that you can get tighter culling without a single large sphere visible over the horizon giving false positive visibility
  • Loading branch information
fluffyfreak committed Nov 19, 2024
1 parent 824143d commit f85f62c
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 3 deletions.
40 changes: 37 additions & 3 deletions src/GeoPatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "MathUtil.h"
#include "Pi.h"
#include "RefCounted.h"
#include "math/Sphere.h"
#include "galaxy/SystemBody.h"
#include "graphics/Frustum.h"
#include "graphics/Graphics.h"
Expand Down Expand Up @@ -43,7 +42,23 @@ namespace PatchMaths{
}

// tri edge lengths
static const double GEOPATCH_SUBDIVIDE_AT_CAMDIST = 5.0;
static constexpr double GEOPATCH_SUBDIVIDE_AT_CAMDIST = 5.0;

#if USE_SUB_CENTROID_CLIPPING
#if NUM_HORIZON_POINTS == 9
static const vector2d sample[NUM_HORIZON_POINTS] = {
{ 0.2, 0.2 }, { 0.5, 0.2 }, { 0.8, 0.2 },
{ 0.2, 0.5 }, { 0.5, 0.5 }, { 0.8, 0.8 },
{ 0.2, 0.8 }, { 0.5, 0.8 }, { 0.8, 0.8 }
};
#elif NUM_HORIZON_POINTS == 5
static const vector2d sample[NUM_HORIZON_POINTS] = {
{ 0.2, 0.2 }, { 0.8, 0.2 },
{ 0.5, 0.5 },
{ 0.2, 0.8 }, { 0.8, 0.8 }
};
#endif
#endif // #if USE_SUB_CENTROID_CLIPPING

GeoPatch::GeoPatch(const RefCountedPtr<GeoPatchContext> &ctx_, GeoSphere *gs,
const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_,
Expand All @@ -70,7 +85,12 @@ GeoPatch::GeoPatch(const RefCountedPtr<GeoPatchContext> &ctx_, GeoSphere *gs,
distMult = 5.0 / Clamp(m_depth, 1, 5);
}
m_splitLength = GEOPATCH_SUBDIVIDE_AT_CAMDIST / pow(2.0, m_depth) * distMult;


#if USE_SUB_CENTROID_CLIPPING
for (size_t i = 0; i < NUM_HORIZON_POINTS; i++) {
m_clipHorizon[i] = SSphere(PatchMaths::GetSpherePoint(v0_, v1_, v2_, v3_, sample[i].x, sample[i].y), m_clipRadius * CLIP_RADIUS_MULTIPLIER);
}
#endif // #if USE_SUB_CENTROID_CLIPPING
}

GeoPatch::~GeoPatch()
Expand Down Expand Up @@ -287,6 +307,7 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons

void GeoPatch::RenderImmediate(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView) const
{
PROFILE_SCOPED()
if (m_patchVBOData->m_heights) {
const vector3d relpos = m_clipCentroid - campos;
renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(relpos)));
Expand Down Expand Up @@ -336,6 +357,7 @@ void GeoPatch::GatherRenderablePatches(std::vector<GeoPatch *> &visiblePatches,

void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustum)
{
PROFILE_SCOPED()
// there should be no LOD update when we have active split requests
if (HasJobRequest())
return;
Expand Down Expand Up @@ -465,6 +487,7 @@ void GeoPatch::ReceiveJobHandle(Job::Handle job)

bool GeoPatch::IsPatchVisible(const Graphics::Frustum &frustum, const vector3d &camPos) const
{
PROFILE_SCOPED()
// Test if this patch is visible
if (!frustum.TestPoint(m_clipCentroid, m_clipRadius))
return false; // nothing below this patch is visible
Expand All @@ -480,6 +503,7 @@ bool GeoPatch::IsPatchVisible(const Graphics::Frustum &frustum, const vector3d &

bool GeoPatch::IsOverHorizon(const vector3d &camPos) const
{
PROFILE_SCOPED()
const vector3d camDir(camPos - m_clipCentroid);
const vector3d camDirNorm(camDir.Normalized());
const vector3d cenDir(m_clipCentroid.Normalized());
Expand All @@ -488,7 +512,17 @@ bool GeoPatch::IsOverHorizon(const vector3d &camPos) const
if (dotProd < 0.25 && (camDir.LengthSqr() > (m_clipRadius * m_clipRadius))) {
// return the result of the Horizon Culling test, inverted to match naming semantic
// eg: HC returns true==visible, but this method returns true==hidden
#if USE_SUB_CENTROID_CLIPPING
bool gather = false;
for (size_t i = 0; i < NUM_HORIZON_POINTS; i++) {
gather |= s_sph.HorizonCulling(camPos, m_clipHorizon[i]);
if (gather)
return false; // early out if any test is visible
}
return !gather; // if true then it's visible, return semantic is true == over the horizon and thus invisible so invert
#else
return !s_sph.HorizonCulling(camPos, SSphere(m_clipCentroid, m_clipRadius));
#endif // #if USE_SUB_CENTROID_CLIPPING
}

// not over the horizon
Expand Down
15 changes: 15 additions & 0 deletions src/GeoPatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "matrix4x4.h"
#include "vector3.h"
#include "graphics/Frustum.h"
#include "math/Sphere.h"
#include <deque>
#include <memory>

Expand All @@ -37,6 +38,17 @@ class SQuadSplitResult;
class SSingleSplitResult;
struct SSplitResultData;

// Experiment:
// Use smaller spheres than the (m_clipCentroid, m_clipRadius) to test against the horizon.
// This can eliminate more patches due to not poking a giant clipping sphere over the horizon
// but on terrains with extreme feature heights there is over-culling of GeoPatches
// eg: Vesta in the Sol system has massive craters which are obviously culled as they go over the horizon.
#define USE_SUB_CENTROID_CLIPPING 1
#if USE_SUB_CENTROID_CLIPPING
#define NUM_HORIZON_POINTS 5 // 9 // 9 points is more expensive
static constexpr double CLIP_RADIUS_MULTIPLIER = 0.1;
#endif // USE_SUB_CENTROID_CLIPPING

class GeoPatch {
public:
GeoPatch(const RefCountedPtr<GeoPatchContext> &_ctx, GeoSphere *gs,
Expand Down Expand Up @@ -161,6 +173,9 @@ class GeoPatch {
std::unique_ptr<GeoPatch> m_kids[NUM_KIDS];

vector3d m_clipCentroid;
#if USE_SUB_CENTROID_CLIPPING
SSphere m_clipHorizon[NUM_HORIZON_POINTS];
#endif // #if USE_SUB_CENTROID_CLIPPING
GeoSphere *m_geosphere;
double m_splitLength; // rough length, is how near to the camera the m_clipCentroid should be before it must split
double m_clipRadius;
Expand Down

0 comments on commit f85f62c

Please sign in to comment.