From d52f728a57d6d1d27bf2ace80f49999209732b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Fri, 5 Apr 2024 13:58:01 +0200 Subject: [PATCH] Improved instance bounding box computation --- src/lib/accelerators/bvh.c | 38 +++++++++++++++++++++++++++++++++++++ src/lib/accelerators/bvh.h | 4 ++++ src/lib/renderer/instance.c | 3 +-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/lib/accelerators/bvh.c b/src/lib/accelerators/bvh.c index 59123540..c69dca79 100644 --- a/src/lib/accelerators/bvh.c +++ b/src/lib/accelerators/bvh.c @@ -614,6 +614,44 @@ struct boundingBox get_root_bbox(const struct bvh *bvh) { return load_bbox_from_node(&bvh->nodes[0]); } +struct boundingBox get_transformed_root_bbox(const struct bvh *bvh, const struct matrix4x4 *mat) { + // This algorithm recursively traverses the BVH in order to get a better approximation of the + // bounding box of the transformed BVH. An area threshold, expressed as a fraction of the area + // of the root node, is used to stop recursion early: For nodes that have a smaller area, the + // bounding box of the node is used as a proxy for the bounding box of the elements contained + // in the subtree. Thus, the smaller the threshold, the more precise the algorithm is, at the + // cost of more iterations. + const float area_threshold = 0.1f * bboxHalfArea((struct boundingBox[]) { get_root_bbox(bvh) }); + + index_t stack[MAX_BVH_DEPTH + 1]; + index_t top = 0; + size_t stack_size = 0; + struct boundingBox bbox = emptyBBox; + + while (true) { + struct bvh_node* node = &bvh->nodes[top]; + struct boundingBox node_bbox = load_bbox_from_node(node); + + // Stop recursing at a fixed depth, when we hit a leaf, or when the surface area of the bounding + // box is lower than the given threshold. + if (stack_size >= MAX_BVH_DEPTH + 1 || + node->index.prim_count > 0 || + bboxHalfArea(&node_bbox) < area_threshold) + { + tform_bbox(&node_bbox, *mat); + extendBBox(&bbox, &node_bbox); + if (stack_size <= 0) + break; + top = stack[--stack_size]; + } else { + stack[stack_size++] = node->index.first_child_or_prim + 0; + top = node->index.first_child_or_prim + 1; + } + } + + return bbox; +} + struct bvh *build_mesh_bvh(const struct mesh *mesh) { return build_bvh_generic(mesh, get_poly_bbox_and_center, mesh->polygons.count); } diff --git a/src/lib/accelerators/bvh.h b/src/lib/accelerators/bvh.h index 9d563e90..c635adeb 100644 --- a/src/lib/accelerators/bvh.h +++ b/src/lib/accelerators/bvh.h @@ -25,6 +25,10 @@ struct bvh; /// Returns the bounding box of the root of the given BVH struct boundingBox get_root_bbox(const struct bvh *bvh); +/// Returns the transformed bounding box of the BVH, using a better approximation than transforming +/// the root bounding box. +struct boundingBox get_transformed_root_bbox(const struct bvh *bvh, const struct matrix4x4 *); + /// Builds a BVH for a given mesh /// @param mesh Mesh containing polygons to process /// @param count Amount of polygons given diff --git a/src/lib/renderer/instance.c b/src/lib/renderer/instance.c index 7039ff0c..0733e9e6 100644 --- a/src/lib/renderer/instance.c +++ b/src/lib/renderer/instance.c @@ -214,8 +214,7 @@ static void getMeshBBoxAndCenter(const struct instance *instance, struct boundin mesh->rayOffset = 0.0f; return; } - *bbox = get_root_bbox(mesh->bvh); - tform_bbox(bbox, instance->composite.A); + *bbox = get_transformed_root_bbox(mesh->bvh, &instance->composite.A); *center = bboxCenter(bbox); mesh->rayOffset = rayOffset(*bbox); }