Skip to content

Commit

Permalink
Custom intersectors via callbacks.
Browse files Browse the repository at this point in the history
  • Loading branch information
jbikker committed Jan 9, 2025
1 parent dd5e4f1 commit 51a9d6c
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 13 deletions.
40 changes: 34 additions & 6 deletions tiny_bvh.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ THE SOFTWARE.

// Features
#define DOUBLE_PRECISION_SUPPORT
//#define TINYBVH_USE_CUSTOM_VECTOR_TYPES
// #define TINYBVH_USE_CUSTOM_VECTOR_TYPES
// #define TINYBVH_NO_SIMD
#define ENABLE_INDEXED_GEOMETRY
#define ENABLE_CUSTOM_GEOMETRY

// CWBVH triangle format: doesn't seem to help on GPU?
// #define CWBVH_COMPRESSED_TRIS
Expand All @@ -110,18 +113,20 @@ THE SOFTWARE.
#define FALLBACK_SHADOW_QUERY( s ) { Ray r = s; float d = s.hit.t; Intersect( r ); return r.hit.t < d; }

// include fast AVX BVH builder
#ifndef TINYBVH_NO_SIMD
#if defined(__x86_64__) || defined(_M_X64) || defined(__wasm_simd128__) || defined(__wasm_relaxed_simd__)
#define BVH_USEAVX
#include "immintrin.h" // for __m128 and __m256
#elif defined(__aarch64__) || defined(_M_ARM64)
#define BVH_USENEON
#include "arm_neon.h"
#endif
#endif // TINYBVH_NO_SIMD

// library version
#define TINY_BVH_VERSION_MAJOR 1
#define TINY_BVH_VERSION_MINOR 2
#define TINY_BVH_VERSION_SUB 1
#define TINY_BVH_VERSION_SUB 2

// ============================================================================
//
Expand Down Expand Up @@ -624,6 +629,9 @@ class BVH : public BVHBase
BVHNode* bvhNode = 0; // BVH node pool, Wald 32-byte format. Root is always in node 0.
uint32_t newNodePtr = 0; // used during build to keep track of next free node in pool.
Fragment* fragment = 0; // input primitive bounding boxes.
// Custom geometry intersection callback
void (*customIntersect)(Ray&, unsigned) = 0;
bool (*customIsOccluded)(const Ray&, unsigned) = 0;
};

#ifdef DOUBLE_PRECISION_SUPPORT
Expand Down Expand Up @@ -998,6 +1006,19 @@ class QBVH6
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif

// Some constexpr stuff to produce nice-looking branches in
// *::Intersect with proper dead code elinimation.
#ifdef ENABLE_INDEXED_GEOMETRY
static constexpr bool indexedEnabled = true;
#else
static constexpr bool indexedEnabled = false;
#endif
#ifdef ENABLE_CUSTOM_GEOMETRY
static constexpr bool customEnabled = true;
#else
static constexpr bool customEnabled = false;
#endif

namespace tinybvh {
#if defined BVH_USEAVX || defined BVH_USENEON

Expand Down Expand Up @@ -1891,8 +1912,10 @@ int32_t BVH::Intersect( Ray& ray ) const
cost += C_TRAV;
if (node->isLeaf())
{
if (vertIdx) for (uint32_t i = 0; i < node->triCount; i++, cost += C_INT)
if (indexedEnabled && vertIdx != 0) for (uint32_t i = 0; i < node->triCount; i++, cost += C_INT)
IntersectTriIndexed( ray, verts, vertIdx, triIdx[node->leftFirst + i] );
else if (customEnabled && customIntersect != 0) for (uint32_t i = 0; i < node->triCount; i++, cost += C_INT)
(*customIntersect)(ray, triIdx[node->leftFirst + i]);
else for (uint32_t i = 0; i < node->triCount; i++, cost += C_INT)
IntersectTri( ray, verts, triIdx[node->leftFirst + i] );
if (stackPtr == 0) break; else node = stack[--stackPtr];
Expand Down Expand Up @@ -1923,11 +1946,16 @@ bool BVH::IsOccluded( const Ray& ray ) const
{
if (node->isLeaf())
{
if (vertIdx)
if (indexedEnabled && vertIdx != 0)
{
for (uint32_t i = 0; i < node->triCount; i++)
if (IndexedTriOccludes( ray, verts, vertIdx, triIdx[node->leftFirst + i] )) return true;
}
else if (customEnabled && customIsOccluded != 0)
{
for (uint32_t i = 0; i < node->triCount; i++)
if ((*customIsOccluded)(ray, triIdx[node->leftFirst + i])) return true;
}
else
{
for (uint32_t i = 0; i < node->triCount; i++)
Expand Down Expand Up @@ -2765,10 +2793,10 @@ template<int M> void MBVH<M>::Refit( const uint32_t nodeIdx )
}
else
{
for( unsigned i = 0; i < node.childCount; i++ ) Refit( node.child[i] );
for (unsigned i = 0; i < node.childCount; i++) Refit( node.child[i] );
MBVHNode& firstChild = mbvhNode[node.child[0]];
bvhvec3 bmin = firstChild.aabbMin, bmax = firstChild.aabbMax;
for( unsigned i = 1; i < node.childCount; i++ )
for (unsigned i = 1; i < node.childCount; i++)
{
MBVHNode& child = mbvhNode[node.child[i]];
bmin = tinybvh_min( bmin, child.aabbMin );
Expand Down
155 changes: 155 additions & 0 deletions tiny_bvh_custom.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#define FENSTER_APP_IMPLEMENTATION
#define SCRWIDTH 800
#define SCRHEIGHT 600
#include "external/fenster.h" // https://github.com/zserge/fenster

#define TINYBVH_IMPLEMENTATION
#include "tiny_bvh.h"
#include <fstream>

using namespace tinybvh;

struct Sphere
{
bvhvec3 pos;
float r;
};

BVH bvh;
int frameIdx = 0;
bvhvec4* triangles = 0;
Sphere* spheres = 0;
int verts = 0;

// setup view pyramid for a pinhole camera
static bvhvec3 eye( -15.24f, 21.5f, 2.54f ), p1, p2, p3;
static bvhvec3 view = normalize( bvhvec3( 0.826f, -0.438f, -0.356f ) );

// callback for custom geometry: ray/sphere intersection
void sphereIntersect( tinybvh::Ray& ray, unsigned primID )
{
bvhvec3 oc = ray.O - spheres[primID].pos;
float b = dot( oc, ray.D );
float r = spheres[primID].r;
float c = dot( oc, oc ) - r * r;
float t, d = b * b - c;
if (d <= 0) return;
d = sqrtf( d ), t = -b - d;
bool hit = t < ray.hit.t && t > 0;
if (hit) ray.hit.t = t, ray.hit.prim = primID;
}

bool sphereIsOccluded( const tinybvh::Ray& ray, unsigned primID )
{
bvhvec3 oc = ray.O - spheres[primID].pos;
float b = dot( oc, ray.D );
float r = spheres[primID].r;
float c = dot( oc, oc ) - r * r;
float t, d = b * b - c;
if (d <= 0) return false;
d = sqrtf( d ), t = -b - d;
return t < ray.hit.t && t > 0;
}

void Init()
{
// load raw vertex data for Crytek's Sponza
std::fstream s{ "./testdata/cryteksponza.bin", s.binary | s.in };
s.seekp( 0 );
s.read( (char*)&verts, 4 );
printf( "Loading triangle data (%i tris).\n", verts );
verts *= 3, triangles = (bvhvec4*)malloc64( verts * 16 );
s.read( (char*)triangles, verts * 16 );
s.close();

// turn the array of triangles into an array of spheres
spheres = new Sphere[verts / 3];
for (int i = 0; i < verts / 3; i++)
{
bvhvec3 v0 = triangles[i * 3 + 0];
bvhvec3 v1 = triangles[i * 3 + 1];
bvhvec3 v2 = triangles[i * 3 + 2];
spheres[i].r = min( 0.05f, tinybvh_min( length( v1 - v0 ), length( v2 - v0 ) ) );
spheres[i].pos = (v0 + v1 + v2) * 0.33333f;
}

// abuse the triangle array to hold sphere bounding boxes
for (int i = 0; i < verts / 3; i++)
{
bvhvec3 aabbMin = spheres[i].pos - bvhvec3( spheres[i].r );
bvhvec3 aabbMax = spheres[i].pos + bvhvec3( spheres[i].r );
triangles[i * 3 + 0] = aabbMin;
triangles[i * 3 + 1] = (aabbMax + aabbMin) * 0.5f;
triangles[i * 3 + 2] = aabbMax; // so, a degenerate tri: just a diagonal line.
}

// build the BVH over the aabbs
bvh.Build( triangles, verts / 3 );

// set custom intersection callbacks
bvh.customIntersect = &sphereIntersect;
bvh.customIsOccluded = &sphereIsOccluded;
}

bool UpdateCamera( float delta_time_s, fenster& f )
{
bvhvec3 right = normalize( cross( bvhvec3( 0, 1, 0 ), view ) );
bvhvec3 up = 0.8f * cross( view, right );

// get camera controls.
bool moved = false;
if (f.keys['A']) eye += right * -1.0f * delta_time_s * 10, moved = true;
if (f.keys['D']) eye += right * delta_time_s * 10, moved = true;
if (f.keys['W']) eye += view * delta_time_s * 10, moved = true;
if (f.keys['S']) eye += view * -1.0f * delta_time_s * 10, moved = true;
if (f.keys['R']) eye += up * delta_time_s * 10, moved = true;
if (f.keys['F']) eye += up * -1.0f * delta_time_s * 10, moved = true;
if (f.keys[20]) view = normalize( view + right * -1.0f * delta_time_s ), moved = true;
if (f.keys[19]) view = normalize( view + right * delta_time_s ), moved = true;
if (f.keys[17]) view = normalize( view + up * -1.0f * delta_time_s ), moved = true;
if (f.keys[18]) view = normalize( view + up * delta_time_s ), moved = true;

// recalculate right, up
right = normalize( cross( bvhvec3( 0, 1, 0 ), view ) );
up = 0.8f * cross( view, right );
bvhvec3 C = eye + 2 * view;
p1 = C - right + up, p2 = C + right + up, p3 = C - right - up;
return moved;
}

void Tick( float delta_time_s, fenster& f, uint32_t* buf )
{
// handle user input and update camera
bool moved = UpdateCamera( delta_time_s, f ) || frameIdx++ == 0;

// clear the screen with a debug-friendly color
for (int i = 0; i < SCRWIDTH * SCRHEIGHT; i++) buf[i] = 0xaaaaff;

// trace rays
const bvhvec3 L = normalize( bvhvec3( 1, 2, 3 ) );
for (int ty = 0; ty < SCRHEIGHT / 4; ty++) for (int tx = 0; tx < SCRWIDTH / 4; tx++)
{
for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++)
{
float u = (float)(tx * 4 + x) / SCRWIDTH, v = (float)(ty * 4 + y) / SCRHEIGHT;
bvhvec3 D = normalize( p1 + u * (p2 - p1) + v * (p3 - p1) - eye );
Ray ray( eye, D, 1e30f );
bvh.Intersect( ray );
if (ray.hit.t < 10000)
{
int pixel_x = tx * 4 + x, pixel_y = ty * 4 + y, primIdx = ray.hit.prim;
bvhvec3 v0 = triangles[primIdx * 3];
bvhvec3 v1 = triangles[primIdx * 3 + 1];
bvhvec3 v2 = triangles[primIdx * 3 + 2];
bvhvec3 N = normalize( cross( v1 - v0, v2 - v0 ) );
int c = (int)(255.9f * fabs( dot( N, L ) ));
buf[pixel_x + pixel_y * SCRWIDTH] = c + (c << 8) + (c << 16);
}
}
}
}

void Shutdown()
{
// nothing here.
}
12 changes: 5 additions & 7 deletions tiny_bvh_fenster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#define SCRHEIGHT 600
#include "external/fenster.h" // https://github.com/zserge/fenster

// #define LOADSCENE
#define LOADSCENE

#define TINYBVH_IMPLEMENTATION
#include "tiny_bvh.h"
Expand All @@ -17,7 +17,8 @@ Ray* rays = 0;
int* depths = 0;

#ifdef LOADSCENE
bvhvec4* triangles = 0;
bvhvec4* vertices = 0;
uint32_t* indices = 0;
const char scene[] = "cryteksponza.bin";
#else
ALIGNED( 16 ) bvhvec4 vertices[259 /* level 3 */ * 6 * 2 * 49 * 3]{};
Expand Down Expand Up @@ -90,8 +91,8 @@ void Init()
s.seekp( 0 );
s.read( (char*)&verts, 4 );
printf( "Loading triangle data (%i tris).\n", verts );
verts *= 3, triangles = (bvhvec4*)malloc64( verts * 16 );
s.read( (char*)triangles, verts * 16 );
verts *= 3, vertices = (bvhvec4*)malloc64( verts * 16 );
s.read( (char*)vertices, verts * 16 );
s.close();
#else
// generate a sphere flake scene
Expand Down Expand Up @@ -192,9 +193,6 @@ void Tick( float delta_time_s, fenster& f, uint32_t* buf )
// buf[pixel_x + pixel_y * SCRWIDTH] = depths[i] << 17; // render depth as red
}
}

tinybvh::free64( rays );
tinybvh::free64( depths );
}

void Shutdown()
Expand Down
10 changes: 10 additions & 0 deletions tiny_bvh_test.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiny_bvh_pt", "vcproj\tiny_
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiny_bvh_gpu", "vcproj\tiny_bvh_gpu.vcxproj", "{4B0A219D-68D0-41EA-A0C4-8EB9E6171218}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiny_bvh_custom", "vcproj\tiny_bvh_custom.vcxproj", "{05E87951-A43C-49D9-BC3B-5F6387285D2E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -71,6 +73,14 @@ Global
{4B0A219D-68D0-41EA-A0C4-8EB9E6171218}.Release|x64.Build.0 = Release|x64
{4B0A219D-68D0-41EA-A0C4-8EB9E6171218}.Release|x86.ActiveCfg = Release|Win32
{4B0A219D-68D0-41EA-A0C4-8EB9E6171218}.Release|x86.Build.0 = Release|Win32
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Debug|x64.ActiveCfg = Debug|x64
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Debug|x64.Build.0 = Debug|x64
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Debug|x86.ActiveCfg = Debug|Win32
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Debug|x86.Build.0 = Debug|Win32
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Release|x64.ActiveCfg = Release|x64
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Release|x64.Build.0 = Release|x64
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Release|x86.ActiveCfg = Release|Win32
{05E87951-A43C-49D9-BC3B-5F6387285D2E}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading

0 comments on commit 51a9d6c

Please sign in to comment.