fastish build of SAH BVH
This commit is contained in:
parent
d875d1130b
commit
738c765557
@ -51,7 +51,8 @@ Viewport :: struct {
|
|||||||
|
|
||||||
Ray :: struct {
|
Ray :: struct {
|
||||||
origin : Vec3,
|
origin : Vec3,
|
||||||
direction : Vec3
|
direction : Vec3,
|
||||||
|
inv_dir : Vec3
|
||||||
}
|
}
|
||||||
|
|
||||||
HitRecord :: struct {
|
HitRecord :: struct {
|
||||||
@ -108,6 +109,7 @@ ray_get :: proc(x : f32, y : f32) -> Ray {
|
|||||||
|
|
||||||
out.direction = ray_direction
|
out.direction = ray_direction
|
||||||
out.origin = camera.center
|
out.origin = camera.center
|
||||||
|
out.inv_dir = Vec3{1.0/out.direction.x, 1.0/out.direction.y, 1.0/out.direction.z}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@ -116,29 +118,6 @@ rayt_cpu_main :: proc() {
|
|||||||
rand.reset(RAND_SEED)
|
rand.reset(RAND_SEED)
|
||||||
|
|
||||||
load_triangles()
|
load_triangles()
|
||||||
|
|
||||||
// Random triangles
|
|
||||||
//{
|
|
||||||
// center_shift := Vec3{5.0, 5.0, 5.0}
|
|
||||||
// // Generate triangles inside a box
|
|
||||||
// for i in 0..<MAX_NUM_ENTITIES {
|
|
||||||
// r0 := vec3_rand_uniform();
|
|
||||||
// r1 := vec3_rand_uniform();
|
|
||||||
// r2 := vec3_rand_uniform();
|
|
||||||
//
|
|
||||||
// // Put the first vertex within a 10x10x10 cube centered at the origin
|
|
||||||
// v0 := 9.0*r0
|
|
||||||
// v0 = v0 - center_shift
|
|
||||||
//
|
|
||||||
// entities[i].kind = EntityKind.Tri
|
|
||||||
// entities[i].v0 = v0
|
|
||||||
// entities[i].v1 = v0 + r1
|
|
||||||
// entities[i].v2 = v0 + r2
|
|
||||||
// entities[i].center = 0.3333*(entities[i].v0 + entities[i].v1 + entities[i].v2)
|
|
||||||
// tri_indices[i] = cast(u32)i
|
|
||||||
// //fmt.printf("Triangle idx %i \n", tri_indices[i])
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Set up scene globals
|
// Set up scene globals
|
||||||
{
|
{
|
||||||
@ -178,7 +157,11 @@ rayt_cpu_main :: proc() {
|
|||||||
|
|
||||||
// build bvh
|
// build bvh
|
||||||
fmt.println("Building BVH")
|
fmt.println("Building BVH")
|
||||||
|
time.stopwatch_start(&stopwatch)
|
||||||
bvh_build()
|
bvh_build()
|
||||||
|
time.stopwatch_stop(&stopwatch)
|
||||||
|
elapsed_bvh_ms := elapsed_time_ms()
|
||||||
|
fmt.printf("Build BVH in %.4f ms \n", elapsed_bvh_ms)
|
||||||
bvh_stats()
|
bvh_stats()
|
||||||
|
|
||||||
fmt.printf("Starting CPU raytracing")
|
fmt.printf("Starting CPU raytracing")
|
||||||
@ -251,8 +234,7 @@ triangle_intersection :: proc(ray : ^Ray, rec : ^HitRecord, triangle : ^Entity)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have an intersection closer than the last, we fill out the hit record
|
||||||
|
|
||||||
if rec.t < closest_so_far {
|
if rec.t < closest_so_far {
|
||||||
intersection_point := ray_point(rec.t, ray)
|
intersection_point := ray_point(rec.t, ray)
|
||||||
v0_normal := linalg.cross(edge1, edge2)
|
v0_normal := linalg.cross(edge1, edge2)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import "core:math"
|
|||||||
import "core:math/linalg"
|
import "core:math/linalg"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Struct definitions
|
||||||
BVHNode :: struct {
|
BVHNode :: struct {
|
||||||
aabb_min : Vec3,
|
aabb_min : Vec3,
|
||||||
aabb_max : Vec3,
|
aabb_max : Vec3,
|
||||||
@ -13,37 +13,52 @@ BVHNode :: struct {
|
|||||||
tri_count : u32
|
tri_count : u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper structure used to define a number of primtiives inside a given non-leaf BVH node,
|
||||||
|
// and their bounding box. This is used to compute the cost of a given split of that node.
|
||||||
|
BVHBin :: struct {
|
||||||
|
aabb_min : Vec3,
|
||||||
|
aabb_max : Vec3,
|
||||||
|
tri_count : u32,
|
||||||
|
}
|
||||||
|
|
||||||
BVH :: struct {
|
BVH :: struct {
|
||||||
nodes : []BVHNode,
|
nodes : []BVHNode,
|
||||||
used_nodes : u32,
|
used_nodes : u32,
|
||||||
root_index : u32,
|
root_index : u32,
|
||||||
max_num_nodes : u32,
|
max_num_nodes : u32,
|
||||||
num_leaf_nodes : u32,
|
num_leaf_nodes : u32,
|
||||||
num_leaf_entities : u32
|
num_leaf_entities : u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Globals
|
||||||
bvh : BVH
|
bvh : BVH
|
||||||
|
BVH_NUM_BINS :: 8
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bvh_bins_init :: proc(bins : []BVHBin) {
|
||||||
|
for i in 0..<BVH_NUM_BINS {
|
||||||
|
bins[i].aabb_min = Vec3{math.F32_MAX, math.F32_MAX, math.F32_MAX}
|
||||||
|
bins[i].aabb_max = Vec3{-math.F32_MAX, -math.F32_MAX, -math.F32_MAX}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
intersect_aabb :: proc(ray : ^Ray, bmin : Vec3, bmax : Vec3, closest_so_far : f32) -> b32 {
|
intersect_aabb :: proc(ray : ^Ray, bmin : Vec3, bmax : Vec3, closest_so_far : f32) -> b32 {
|
||||||
tx1 : f32 = (bmin.x - ray.origin.x) / ray.direction.x
|
tx1 : f32 = (bmin.x - ray.origin.x) * ray.inv_dir.x
|
||||||
tx2 : f32 = (bmax.x - ray.origin.x) / ray.direction.x
|
tx2 : f32 = (bmax.x - ray.origin.x) * ray.inv_dir.x
|
||||||
tmin : f32 = math.min(tx1, tx2)
|
tmin : f32 = math.min(tx1, tx2)
|
||||||
tmax : f32 = math.max(tx1, tx2)
|
tmax : f32 = math.max(tx1, tx2)
|
||||||
|
|
||||||
ty1 : f32 = (bmin.y - ray.origin.y) / ray.direction.y
|
ty1 : f32 = (bmin.y - ray.origin.y) * ray.inv_dir.y
|
||||||
ty2 : f32 = (bmax.y - ray.origin.y) / ray.direction.y
|
ty2 : f32 = (bmax.y - ray.origin.y) * ray.inv_dir.y
|
||||||
tmin = math.max(tmin, math.min(ty1, ty2))
|
tmin = math.max(tmin, math.min(ty1, ty2))
|
||||||
tmax = math.min(tmax, math.max(ty1, ty2))
|
tmax = math.min(tmax, math.max(ty1, ty2))
|
||||||
|
|
||||||
tz1 : f32 = (bmin.z - ray.origin.z) / ray.direction.z
|
tz1 : f32 = (bmin.z - ray.origin.z) * ray.inv_dir.z
|
||||||
tz2 : f32 = (bmax.z - ray.origin.z) / ray.direction.z
|
tz2 : f32 = (bmax.z - ray.origin.z) * ray.inv_dir.z
|
||||||
tmin = math.max(tmin, math.min(tz1, tz2))
|
tmin = math.max(tmin, math.min(tz1, tz2))
|
||||||
tmax = math.min(tmax, math.max(tz1, tz2))
|
tmax = math.min(tmax, math.max(tz1, tz2))
|
||||||
|
|
||||||
@ -51,6 +66,40 @@ intersect_aabb :: proc(ray : ^Ray, bmin : Vec3, bmax : Vec3, closest_so_far : f3
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
aabb_area :: proc(aabb_min : Vec3, aabb_max : Vec3) -> f32 {
|
||||||
|
e := aabb_max - aabb_min
|
||||||
|
return e.x * e.y + e.y * e.z + e.z * e.x
|
||||||
|
}
|
||||||
|
|
||||||
|
aabb_grow :: proc(aabb_min : Vec3, aabb_max : Vec3, p_min : Vec3, p_max : Vec3) -> (Vec3, Vec3) {
|
||||||
|
out_aabb_min := aabb_min
|
||||||
|
out_aabb_max := aabb_max
|
||||||
|
if(p_min.x < math.F32_MAX) {
|
||||||
|
out_aabb_min = linalg.min(out_aabb_min, p_min)
|
||||||
|
out_aabb_max = linalg.max(out_aabb_max, p_min)
|
||||||
|
out_aabb_min = linalg.min(out_aabb_min, p_max)
|
||||||
|
out_aabb_max = linalg.max(out_aabb_max, p_max)
|
||||||
|
}
|
||||||
|
return out_aabb_min, out_aabb_max
|
||||||
|
}
|
||||||
|
|
||||||
|
aabb_min_triangle :: proc(aabb_min : Vec3, tri : ^Entity) -> Vec3 {
|
||||||
|
out_aabb_min := aabb_min
|
||||||
|
out_aabb_min = linalg.min(out_aabb_min, tri.v0)
|
||||||
|
out_aabb_min = linalg.min(out_aabb_min, tri.v1)
|
||||||
|
out_aabb_min = linalg.min(out_aabb_min, tri.v2)
|
||||||
|
return out_aabb_min
|
||||||
|
}
|
||||||
|
|
||||||
|
aabb_max_triangle :: proc(aabb_max : Vec3, tri : ^Entity) -> Vec3 {
|
||||||
|
out_aabb_max := aabb_max
|
||||||
|
out_aabb_max = linalg.max(out_aabb_max, tri.v0)
|
||||||
|
out_aabb_max = linalg.max(out_aabb_max, tri.v1)
|
||||||
|
out_aabb_max = linalg.max(out_aabb_max, tri.v2)
|
||||||
|
return out_aabb_max
|
||||||
|
}
|
||||||
|
|
||||||
bvh_update_bounds :: proc(node_idx : u32) {
|
bvh_update_bounds :: proc(node_idx : u32) {
|
||||||
node : ^BVHNode = &bvh.nodes[node_idx]
|
node : ^BVHNode = &bvh.nodes[node_idx]
|
||||||
|
|
||||||
@ -61,28 +110,113 @@ bvh_update_bounds :: proc(node_idx : u32) {
|
|||||||
for i in 0..<node.tri_count {
|
for i in 0..<node.tri_count {
|
||||||
leaf_tri_idx := tri_indices[first_tri_idx + i]
|
leaf_tri_idx := tri_indices[first_tri_idx + i]
|
||||||
triangle : ^Entity = &entities[leaf_tri_idx]
|
triangle : ^Entity = &entities[leaf_tri_idx]
|
||||||
node.aabb_min = linalg.min(node.aabb_min, triangle.v0)
|
node.aabb_min = aabb_min_triangle(node.aabb_min, triangle)
|
||||||
node.aabb_min = linalg.min(node.aabb_min, triangle.v1)
|
node.aabb_max = aabb_max_triangle(node.aabb_max, triangle)
|
||||||
node.aabb_min = linalg.min(node.aabb_min, triangle.v2)
|
|
||||||
node.aabb_max = linalg.max(node.aabb_max, triangle.v0)
|
|
||||||
node.aabb_max = linalg.max(node.aabb_max, triangle.v1)
|
|
||||||
node.aabb_max = linalg.max(node.aabb_max, triangle.v2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find_best_split_plane :: proc(node : ^BVHNode, out_axis : ^u32, out_split_pos : ^f32) -> f32 {
|
||||||
|
best_cost : f32 = math.F32_MAX
|
||||||
|
|
||||||
|
// Loop over each axis
|
||||||
|
for axis in 0..<3 {
|
||||||
|
bounds_min : f32 = math.F32_MAX
|
||||||
|
bounds_max : f32 = -math.F32_MAX
|
||||||
|
// Find the bounds of all the primitive centers in the node
|
||||||
|
for i in 0..<node.tri_count {
|
||||||
|
tri : ^Entity = &entities[tri_indices[node.left_first + i]]
|
||||||
|
bounds_min = math.min(bounds_min, tri.center[axis])
|
||||||
|
bounds_max = math.max(bounds_max, tri.center[axis])
|
||||||
|
}
|
||||||
|
|
||||||
|
if bounds_min == bounds_max { continue }
|
||||||
|
|
||||||
|
bins : [BVH_NUM_BINS]BVHBin
|
||||||
|
bvh_bins_init(bins[:])
|
||||||
|
bin_scale : f32 = cast(f32)BVH_NUM_BINS / (bounds_max - bounds_min)
|
||||||
|
|
||||||
|
// We put all the primitives in the node in any one of the binds,
|
||||||
|
// depending on the primitive's centroid pos. What we are doing is really
|
||||||
|
// just splitting the node with BVH_NUM_BINS-1 number of planes.
|
||||||
|
|
||||||
|
for i in 0..<node.tri_count {
|
||||||
|
tri : ^Entity = &entities[tri_indices[node.left_first + i]]
|
||||||
|
primitive_index : u32 = cast(u32)((tri.center[axis] - bounds_min) * bin_scale)
|
||||||
|
bin_idx : u32 = math.min(BVH_NUM_BINS - 1, primitive_index)
|
||||||
|
bins[bin_idx].tri_count += 1
|
||||||
|
bins[bin_idx].aabb_min = aabb_min_triangle(bins[bin_idx].aabb_min, tri)
|
||||||
|
bins[bin_idx].aabb_max = aabb_max_triangle(bins[bin_idx].aabb_min, tri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather data for all BVH_NUM_BINS-1 planes
|
||||||
|
left_area : [BVH_NUM_BINS - 1]f32
|
||||||
|
right_area : [BVH_NUM_BINS - 1]f32
|
||||||
|
left_count : [BVH_NUM_BINS - 1]u32
|
||||||
|
right_count : [BVH_NUM_BINS - 1]u32
|
||||||
|
left_box_aabb_min : Vec3 = Vec3{math.F32_MAX, math.F32_MAX, math.F32_MAX}
|
||||||
|
left_box_aabb_max : Vec3 = Vec3{-math.F32_MAX, -math.F32_MAX, -math.F32_MAX}
|
||||||
|
right_box_aabb_min : Vec3 = Vec3{math.F32_MAX, math.F32_MAX, math.F32_MAX}
|
||||||
|
right_box_aabb_max : Vec3 = Vec3{-math.F32_MAX, -math.F32_MAX, -math.F32_MAX}
|
||||||
|
left_sum : u32 = 0
|
||||||
|
right_sum : u32 = 0
|
||||||
|
// Loop from both sides simultaneously
|
||||||
|
for i in 0..<BVH_NUM_BINS-1 {
|
||||||
|
left_sum += bins[i].tri_count
|
||||||
|
left_count[i] = left_sum
|
||||||
|
left_box_aabb_min, left_box_aabb_max = aabb_grow(left_box_aabb_min, left_box_aabb_max,
|
||||||
|
bins[i].aabb_min, bins[i].aabb_max)
|
||||||
|
left_area[i] = aabb_area(left_box_aabb_min, left_box_aabb_max)
|
||||||
|
right_idx := BVH_NUM_BINS - 1 - i
|
||||||
|
right_sum += bins[right_idx].tri_count
|
||||||
|
right_count[right_idx-1] = right_sum
|
||||||
|
right_box_aabb_min, right_box_aabb_max = aabb_grow(right_box_aabb_min,
|
||||||
|
right_box_aabb_max,
|
||||||
|
bins[right_idx].aabb_min,
|
||||||
|
bins[right_idx].aabb_max)
|
||||||
|
|
||||||
|
right_area[right_idx-1] = aabb_area(right_box_aabb_min, right_box_aabb_max)
|
||||||
|
}
|
||||||
|
|
||||||
|
plane_scale : f32 = (bounds_max - bounds_min) / cast(f32)BVH_NUM_BINS
|
||||||
|
// Compute the Surface area heuristic (SAH) cost for each plane
|
||||||
|
for i in 0..<BVH_NUM_BINS-1 {
|
||||||
|
plane_cost : f32 = 0.0
|
||||||
|
plane_cost += cast(f32)left_count[i] * left_area[i]
|
||||||
|
plane_cost += cast(f32)right_count[i] * right_area[i]
|
||||||
|
|
||||||
|
if(plane_cost < best_cost) {
|
||||||
|
out_axis^ = cast(u32)axis
|
||||||
|
out_split_pos^ = bounds_min + plane_scale * cast(f32)(i + 1)
|
||||||
|
best_cost = plane_cost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_cost
|
||||||
|
}
|
||||||
|
|
||||||
bvh_subdivide :: proc(node_idx : u32) {
|
bvh_subdivide :: proc(node_idx : u32) {
|
||||||
node : ^BVHNode = &bvh.nodes[node_idx]
|
node : ^BVHNode = &bvh.nodes[node_idx]
|
||||||
|
if node.tri_count < bvh.num_leaf_entities {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if(node.tri_count <= bvh.num_leaf_entities) {
|
axis : u32 = 0
|
||||||
|
split_pos : f32
|
||||||
|
split_cost := find_best_split_plane(node, &axis, &split_pos)
|
||||||
|
node_split_cost : f32 = 0.0
|
||||||
|
{
|
||||||
|
e : Vec3 = node.aabb_max - node.aabb_min
|
||||||
|
surface_area := e.x * e.y + e.y * e.z + e.z * e.x
|
||||||
|
node_split_cost = cast(f32)node.tri_count * surface_area
|
||||||
|
}
|
||||||
|
|
||||||
|
if(split_cost >= node_split_cost) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
extent := node.aabb_max - node.aabb_min
|
|
||||||
axis : u32 = 0
|
|
||||||
if(extent.y > extent.x) {axis = 1}
|
|
||||||
if(extent.z > extent[axis]) {axis = 2}
|
|
||||||
split_pos : f32 = node.aabb_min[axis] + extent[axis] * 0.5
|
|
||||||
|
|
||||||
i : u32 = node.left_first
|
i : u32 = node.left_first
|
||||||
j : u32 = node.tri_count + i - 1
|
j : u32 = node.tri_count + i - 1
|
||||||
|
|
||||||
@ -150,8 +284,9 @@ bvh_build :: proc() {
|
|||||||
num_triangles : u32 = cast(u32)len(tri_indices)
|
num_triangles : u32 = cast(u32)len(tri_indices)
|
||||||
bvh.max_num_nodes = 2 * num_triangles - 1
|
bvh.max_num_nodes = 2 * num_triangles - 1
|
||||||
bvh.nodes = make([]BVHNode, bvh.max_num_nodes)
|
bvh.nodes = make([]BVHNode, bvh.max_num_nodes)
|
||||||
bvh.used_nodes = 2 // We skip first two nodes, for some reason. TODO comment this, read the tutorial
|
bvh.used_nodes = 2 // We skip first two nodes, for some reason.
|
||||||
bvh.num_leaf_entities = 2
|
//TODO comment this, read the tutorial
|
||||||
|
bvh.num_leaf_entities = 4
|
||||||
bvh.root_index = 0
|
bvh.root_index = 0
|
||||||
|
|
||||||
// Init root node
|
// Init root node
|
||||||
@ -184,5 +319,6 @@ bvh_stats :: proc() {
|
|||||||
fmt.printf("Total number of leaf nodes: %i \n", num_leaf_nodes)
|
fmt.printf("Total number of leaf nodes: %i \n", num_leaf_nodes)
|
||||||
fmt.printf("Total number of triangles in BVH: %i \n", total_triangles_in_bvh)
|
fmt.printf("Total number of triangles in BVH: %i \n", total_triangles_in_bvh)
|
||||||
}
|
}
|
||||||
|
assert(cast(int)total_triangles_in_bvh == len(tri_indices))
|
||||||
bvh.num_leaf_nodes = num_leaf_nodes
|
bvh.num_leaf_nodes = num_leaf_nodes
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user