Skip to content

Commit

Permalink
Merge pull request #786 from zeux/nanite-errpx
Browse files Browse the repository at this point in the history
demo: Improve boundsError to take camera parameters into account
  • Loading branch information
zeux authored Oct 9, 2024
2 parents afeea3d + c664ea2 commit 1e48e96
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 12 deletions.
4 changes: 2 additions & 2 deletions demo/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void dumpObj(const std::vector<Vertex>& vertices, const std::vector<unsigned int
{
unsigned int a = indices[i], b = indices[i + 1], c = indices[i + 2];

fprintf(stderr, "f %d %d %d\n", a + 1, b + 1, c + 1);
fprintf(stderr, "f %d//%d %d//%d %d//%d\n", a + 1, a + 1, b + 1, b + 1, c + 1, c + 1);
}
}

Expand All @@ -209,7 +209,7 @@ void dumpObj(const char* section, const std::vector<unsigned int>& indices)
{
unsigned int a = indices[j], b = indices[j + 1], c = indices[j + 2];

fprintf(stderr, "f %d %d %d\n", a + 1, b + 1, c + 1);
fprintf(stderr, "f %d//%d %d//%d %d//%d\n", a + 1, a + 1, b + 1, b + 1, c + 1, c + 1);
}
}

Expand Down
32 changes: 22 additions & 10 deletions demo/nanite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct Cluster
const size_t kClusterSize = 128;
const size_t kGroupSize = 8;
const bool kUseLocks = true;
const bool kUseNormals = true;

static LODBounds bounds(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices, float error)
{
Expand Down Expand Up @@ -107,11 +108,15 @@ static LODBounds boundsMerge(const std::vector<Cluster>& clusters, const std::ve
return result;
}

static float boundsError(const LODBounds& bounds, float x, float y, float z)
// computes approximate (perspective) projection error of a cluster in screen space (0..1; multiply by screen height to get pixels)
// camera_proj is projection[1][1], or cot(fovy/2); camera_znear is *positive* near plane distance
// for DAG cut to be valid, boundsError must be monotonic: it must return a larger error for parent cluster
// for simplicity, we ignore perspective distortion and use rotationally invariant projection size estimation
static float boundsError(const LODBounds& bounds, float camera_x, float camera_y, float camera_z, float camera_proj, float camera_znear)
{
float dx = bounds.center[0] - x, dy = bounds.center[1] - y, dz = bounds.center[2] - z;
float dx = bounds.center[0] - camera_x, dy = bounds.center[1] - camera_y, dz = bounds.center[2] - camera_z;
float d = sqrtf(dx * dx + dy * dy + dz * dz) - bounds.radius;
return d <= 0 ? FLT_MAX : bounds.error / d;
return bounds.error / (d > camera_znear ? d : camera_znear) * (camera_proj * 0.5f);
}

#ifdef METIS
Expand Down Expand Up @@ -426,7 +431,10 @@ static std::vector<unsigned int> simplify(const std::vector<Vertex>& vertices, c

std::vector<unsigned int> lod(indices.size());
unsigned int options = meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute;
if (locks)
float normal_weights[3] = {0.5f, 0.5f, 0.5f};
if (kUseNormals)
lod.resize(meshopt_simplifyWithAttributes(&lod[0], &indices[0], indices.size(), &vertices[0].px, vertices.size(), sizeof(Vertex), &vertices[0].nx, sizeof(Vertex), normal_weights, 3, locks ? &(*locks)[0] : NULL, target_count, FLT_MAX, options, error));
else if (locks)
lod.resize(meshopt_simplifyWithAttributes(&lod[0], &indices[0], indices.size(), &vertices[0].px, vertices.size(), sizeof(Vertex), NULL, 0, NULL, 0, &(*locks)[0], target_count, FLT_MAX, options, error));
else
lod.resize(meshopt_simplify(&lod[0], &indices[0], indices.size(), &vertices[0].px, vertices.size(), sizeof(Vertex), target_count, FLT_MAX, options | meshopt_SimplifyLockBorder, error));
Expand Down Expand Up @@ -607,20 +615,24 @@ void nanite(const std::vector<Vertex>& vertices, const std::vector<unsigned int>
maxy = std::max(maxy, vertices[i].py * 2);
maxz = std::max(maxz, vertices[i].pz * 2);
}
float threshold = 3e-3f;

float threshold = 2e-3f; // 2 pixels at 1080p
float fovy = 60.f;
float znear = 1e-2f;
float proj = 1.f / tanf(fovy * 3.1415926f / 180.f * 0.5f);

std::vector<unsigned int> cut;
for (size_t i = 0; i < clusters.size(); ++i)
if (boundsError(clusters[i].self, maxx, maxy, maxz) <= threshold && boundsError(clusters[i].parent, maxx, maxy, maxz) > threshold)
if (boundsError(clusters[i].self, maxx, maxy, maxz, proj, znear) <= threshold && boundsError(clusters[i].parent, maxx, maxy, maxz, proj, znear) > threshold)
cut.insert(cut.end(), clusters[i].indices.begin(), clusters[i].indices.end());

#ifndef NDEBUG
for (size_t i = 0; i < dag_debug.size(); ++i)
{
int j = dag_debug[i].first, k = dag_debug[i].second;
float ej = boundsError(clusters[j].self, maxx, maxy, maxz);
float ejp = boundsError(clusters[j].parent, maxx, maxy, maxz);
float ek = boundsError(clusters[k].self, maxx, maxy, maxz);
float ej = boundsError(clusters[j].self, maxx, maxy, maxz, proj, znear);
float ejp = boundsError(clusters[j].parent, maxx, maxy, maxz, proj, znear);
float ek = boundsError(clusters[k].self, maxx, maxy, maxz, proj, znear);

assert(ej <= ek);
assert(ejp >= ej);
Expand All @@ -634,7 +646,7 @@ void nanite(const std::vector<Vertex>& vertices, const std::vector<unsigned int>
dumpObj(vertices, cut);

for (size_t i = 0; i < clusters.size(); ++i)
if (boundsError(clusters[i].self, maxx, maxy, maxz) <= threshold && boundsError(clusters[i].parent, maxx, maxy, maxz) > threshold)
if (boundsError(clusters[i].self, maxx, maxy, maxz, proj, znear) <= threshold && boundsError(clusters[i].parent, maxx, maxy, maxz, proj, znear) > threshold)
dumpObj("cluster", clusters[i].indices);
}
}

0 comments on commit 1e48e96

Please sign in to comment.