Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make bvh construction truly generic to limit code duplication #78

Merged
merged 2 commits into from
Jun 16, 2020
Merged
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
99 changes: 0 additions & 99 deletions src/accelerators/bbox.c

This file was deleted.

39 changes: 0 additions & 39 deletions src/accelerators/bbox.h

This file was deleted.

119 changes: 43 additions & 76 deletions src/accelerators/bvh.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "../datatypes/vertexbuffer.h"
#include "../datatypes/poly.h"
#include "../datatypes/vector.h"
#include "../datatypes/bbox.h"
#include "../datatypes/lightRay.h"
#include "../datatypes/mesh.h"

Expand All @@ -31,20 +32,14 @@
#define TRAVERSAL_COST 1.5f // Ratio (cost of traversing a node / cost of intersecting a primitive)
#define BIN_COUNT 32 // Number of bins to use to approximate the SAH

// TODO: Move this to its own file if need be
// NOTE: The existing `boundingBox` type is too big
typedef struct bBox {
vector min, max;
} bBox;

// Bin used to approximate the SAH.
typedef struct Bin {
bBox bbox;
struct boundingBox bbox;
unsigned count;
float cost;
} Bin;

static inline void storeBBoxInNode(struct bvhNode *node, const bBox *bbox) {
static inline void storeBBoxInNode(struct bvhNode *node, const struct boundingBox *bbox) {
node->bounds[0] = bbox->min.x;
node->bounds[1] = bbox->max.x;
node->bounds[2] = bbox->min.y;
Expand All @@ -53,7 +48,7 @@ static inline void storeBBoxInNode(struct bvhNode *node, const bBox *bbox) {
node->bounds[5] = bbox->max.z;
}

static inline void loadBBoxFromNode(bBox *bbox, const struct bvhNode *node) {
static inline void loadBBoxFromNode(struct boundingBox *bbox, const struct bvhNode *node) {
bbox->min.x = node->bounds[0];
bbox->max.x = node->bounds[1];
bbox->min.y = node->bounds[2];
Expand All @@ -62,25 +57,10 @@ static inline void loadBBoxFromNode(bBox *bbox, const struct bvhNode *node) {
bbox->max.z = node->bounds[5];
}

static const bBox emptyBBox = {
.min = { FLT_MAX, FLT_MAX, FLT_MAX },
.max = { -FLT_MAX, -FLT_MAX, -FLT_MAX }
};

static inline float halfArea(const bBox *bbox) {
vector extent = vecSub(bbox->max, bbox->min);
return extent.x * (extent.y + extent.z) + extent.y * extent.z;
}

static inline void extendBBox(bBox *dst, const bBox *src) {
dst->min = vecMin(dst->min, src->min);
dst->max = vecMax(dst->max, src->max);
}

static inline float nodeArea(const struct bvhNode *node) {
bBox bbox;
struct boundingBox bbox;
loadBBoxFromNode(&bbox, node);
return halfArea(&bbox);
return bboxHalfArea(&bbox);
}

static inline void makeLeaf(struct bvhNode* node, unsigned begin, unsigned primCount) {
Expand Down Expand Up @@ -137,7 +117,7 @@ static inline unsigned partitionPrimitiveIndices(
static void buildBvhRecursive(
unsigned nodeId,
struct bvh *bvh,
const bBox *bboxes,
const struct boundingBox *bboxes,
const vector *centers,
unsigned begin, unsigned end,
unsigned depth)
Expand Down Expand Up @@ -172,13 +152,13 @@ static void buildBvhRecursive(
// Sweep from the right to the left to compute the partial SAH cost.
// Recall that the SAH is the sum of two parts: SA(left) * N(left) + SA(right) * N(right).
// This loop computes SA(right) * N(right) alone.
bBox curBBox = emptyBBox;
struct boundingBox curBBox = emptyBBox;
unsigned curCount = 0;
for (unsigned i = BIN_COUNT; i > 1; --i) {
Bin *bin = &bins[axis][i - 1];
curCount += bin->count;
extendBBox(&curBBox, &bin->bbox);
bin->cost = curCount * halfArea(&curBBox);
bin->cost = curCount * bboxHalfArea(&curBBox);
}

// Sweep from the left to the right to compute the full cost and find the minimum.
Expand All @@ -188,7 +168,7 @@ static void buildBvhRecursive(
Bin *bin = &bins[axis][i];
curCount += bin->count;
extendBBox(&curBBox, &bin->bbox);
float cost = curCount * halfArea(&curBBox) + bins[axis][i + 1].cost;
float cost = curCount * bboxHalfArea(&curBBox) + bins[axis][i + 1].cost;
if (cost < minCost[axis]) {
minBin[axis] = i + 1;
minCost[axis] = cost;
Expand Down Expand Up @@ -228,8 +208,8 @@ static void buildBvhRecursive(
bvh->nodeCount += 2;

// Compute the bounding box of the children
bBox leftBBox = emptyBBox;
bBox rightBBox = emptyBBox;
struct boundingBox leftBBox = emptyBBox;
struct boundingBox rightBBox = emptyBBox;
for (unsigned i = 0; i < minBin[minAxis]; ++i)
extendBBox(&leftBBox, &bins[minAxis][i].bbox);
for (unsigned i = minBin[minAxis]; i < BIN_COUNT; ++i)
Expand All @@ -246,21 +226,21 @@ static void buildBvhRecursive(
}
}

struct bvh *buildBvh(int *polys, unsigned count) {
// Builds a BVH using the provided callback to obtain bounding boxes and centers for each primitive
static inline struct bvh *buildBvhGeneric(
void* userData,
void (*getBBoxAndCenter)(void *, unsigned, struct boundingBox *, vector *),
unsigned count)
{
vector *centers = malloc(sizeof(vector) * count);
bBox *bboxes = malloc(sizeof(bBox) * count);
struct boundingBox *bboxes = malloc(sizeof(struct boundingBox) * count);
int *primIndices = malloc(sizeof(int) * count);

bBox rootBBox = emptyBBox;
struct boundingBox rootBBox = emptyBBox;

// Precompute bboxes and centers
for (unsigned i = 0; i < count; ++i) {
vector v0 = vertexArray[polygonArray[polys[i]].vertexIndex[0]];
vector v1 = vertexArray[polygonArray[polys[i]].vertexIndex[1]];
vector v2 = vertexArray[polygonArray[polys[i]].vertexIndex[2]];
centers[i] = getMidPoint(v0, v1, v2);
bboxes[i].min = vecMin(v0, vecMin(v1, v2));
bboxes[i].max = vecMax(v0, vecMax(v1, v2));
getBBoxAndCenter(userData, i, &bboxes[i], &centers[i]);
primIndices[i] = i;
rootBBox.min = vecMin(rootBBox.min, bboxes[i].min);
rootBBox.max = vecMax(rootBBox.max, bboxes[i].max);
Expand All @@ -279,51 +259,38 @@ struct bvh *buildBvh(int *polys, unsigned count) {

// Shrink array of nodes (since some leaves may contain more than 1 primitive)
bvh->nodes = realloc(bvh->nodes, sizeof(struct bvhNode) * bvh->nodeCount);
for (unsigned i = 0; i < count; ++i)
primIndices[i] = polys[primIndices[i]];
free(centers);
free(bboxes);
return bvh;
}

vector centerFromBBox(struct bBox box) {
return (vector){
box.min.x + (0.5f * (box.max.x - box.min.x)),
box.min.y + (0.5f * (box.max.y - box.min.y)),
box.min.z + (0.5f * (box.max.z - box.min.z))
};
static void getPolyBBoxAndCenter(void* userData, unsigned i, struct boundingBox *bbox, vector *center) {
int* polys = userData;
vector v0 = vertexArray[polygonArray[polys[i]].vertexIndex[0]];
vector v1 = vertexArray[polygonArray[polys[i]].vertexIndex[1]];
vector v2 = vertexArray[polygonArray[polys[i]].vertexIndex[2]];
*center = getMidPoint(v0, v1, v2);
bbox->min = vecMin(v0, vecMin(v1, v2));
bbox->max = vecMax(v0, vecMax(v1, v2));
}

struct bvh *topLevelBvh(struct mesh *meshes, unsigned meshCount) {
vector *centers = malloc(sizeof(vector) * meshCount);
bBox *bboxes = malloc(sizeof(bBox) * meshCount);
int *primIndices = malloc(sizeof(int) * meshCount);

bBox rootBBox = emptyBBox;

//Gather up the bboxes and centres
for (unsigned i = 0; i < meshCount; ++i) {
loadBBoxFromNode(&bboxes[i], &meshes[i].bvh->nodes[0]);
centers[i] = centerFromBBox(bboxes[i]);
rootBBox.min = vecMin(rootBBox.min, bboxes[i].min);
rootBBox.max = vecMax(rootBBox.max, bboxes[i].max);
primIndices[i] = i;
}

unsigned maxNodes = 2 * meshCount - 1;
struct bvh *bvh = malloc(sizeof(struct bvh));
bvh->nodeCount = 1;
bvh->nodes = malloc(sizeof(struct bvhNode) * maxNodes);
bvh->primIndices = primIndices;
storeBBoxInNode(&bvh->nodes[0], &rootBBox);

buildBvhRecursive(0, bvh, bboxes, centers, 0, meshCount, 0);
bvh->nodes = realloc(bvh->nodes, sizeof(struct bvhNode) * bvh->nodeCount);
free(centers);
free(bboxes);
struct bvh *buildBvh(int *polys, unsigned count) {
struct bvh *bvh = buildBvhGeneric(polys, getPolyBBoxAndCenter, count);
for (unsigned i = 0; i < count; ++i)
bvh->primIndices[i] = polys[bvh->primIndices[i]];
return bvh;
}

static void getMeshBBoxAndCenter(void* userData, unsigned i, struct boundingBox *bbox, vector *center) {
struct mesh *meshes = userData;
loadBBoxFromNode(bbox, &meshes[i].bvh->nodes[0]);
*center = bboxCenter(bbox);
}

struct bvh *buildTopLevelBvh(struct mesh *meshes, unsigned meshCount) {
return buildBvhGeneric(meshes, getMeshBBoxAndCenter, meshCount);
}

static inline bool rayIntersectsWithBvhLeaf(const struct bvh *bvh, const struct bvhNode *leaf, const struct lightRay *ray, struct hitRecord *isect) {
bool found = false;
for (int i = 0; i < leaf->primCount; ++i) {
Expand Down
2 changes: 1 addition & 1 deletion src/accelerators/bvh.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct bvh {
/// @param count Amount of polygons given
struct bvh *buildBvh(int *polygons, unsigned count);

struct bvh *topLevelBvh(struct mesh *meshes, unsigned meshCount);
struct bvh *buildTopLevelBvh(struct mesh *meshes, unsigned meshCount);

/// Intersects a ray with the given BVH
bool rayIntersectsWithBvh(const struct bvh *bvh, const struct lightRay *ray, struct hitRecord *isect);
Expand Down
Loading