working first bvh with odin and raylib
This commit is contained in:
parent
6603f27c90
commit
d68f740c10
@ -3,10 +3,15 @@ package main
|
||||
import rl "vendor:raylib"
|
||||
import "core:fmt"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WINDOW_WIDTH :: 1280
|
||||
WINDOW_HEIGHT :: 720
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
rl_window_loop :: proc() {
|
||||
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Rayt");
|
||||
defer rl.CloseWindow()
|
||||
|
||||
@ -4,36 +4,36 @@ import "core:fmt"
|
||||
import "core:math/rand"
|
||||
import "core:math/linalg"
|
||||
import "core:math"
|
||||
import "core:time"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Global defines
|
||||
RAND_SEED :: 1984
|
||||
|
||||
// Global program parameters
|
||||
IMAGE_WIDTH :: 1280
|
||||
ASPECT_RATIO :: 1.7778 // 16:9
|
||||
|
||||
MAX_NUM_ENTITIES :: 64
|
||||
|
||||
Vec3 :: distinct [3]f32
|
||||
|
||||
// Global colors
|
||||
COLOR_LIGHT_BLUE :: Vec3{0.5, 0.7, 1.0}
|
||||
COLOR_WHITE :: Vec3{1.0, 1.0, 1.0}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Global program parameters
|
||||
IMAGE_WIDTH :: 1280
|
||||
ASPECT_RATIO :: 1.7778 // 16:9
|
||||
MAX_NUM_ENTITIES :: 64
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Struct defs
|
||||
Image :: struct {
|
||||
width : u32,
|
||||
height : u32,
|
||||
aspect_ratio : f32
|
||||
}
|
||||
image: Image
|
||||
|
||||
|
||||
Camera :: struct {
|
||||
center : Vec3,
|
||||
up : Vec3,
|
||||
focal_length : f32,
|
||||
}
|
||||
camera: Camera
|
||||
|
||||
Viewport :: struct {
|
||||
width : f32,
|
||||
@ -46,7 +46,6 @@ Viewport :: struct {
|
||||
pixel_delta_u : Vec3,
|
||||
pixel_delta_v : Vec3,
|
||||
}
|
||||
viewport: Viewport
|
||||
|
||||
Ray :: struct {
|
||||
origin : Vec3,
|
||||
@ -72,11 +71,26 @@ Entity :: struct {
|
||||
v1: Vec3,
|
||||
v2: Vec3,
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Main global variables
|
||||
stopwatch : time.Stopwatch
|
||||
|
||||
image: Image
|
||||
camera: Camera
|
||||
viewport: Viewport
|
||||
|
||||
entities: [MAX_NUM_ENTITIES]Entity
|
||||
tri_indices: [MAX_NUM_ENTITIES]u32
|
||||
|
||||
|
||||
pixelbuffer: []Vec3
|
||||
pixelbuffer_rgb: []u8
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
vec3_rand_uniform :: proc() -> Vec3 {
|
||||
return Vec3{rand.float32(), rand.float32(), rand.float32()}
|
||||
}
|
||||
@ -117,6 +131,8 @@ rayt_cpu_main :: proc() {
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,8 +172,23 @@ rayt_cpu_main :: proc() {
|
||||
pixelbuffer = make([]Vec3, num_pixels);
|
||||
pixelbuffer_rgb = make([]u8, num_pixels * 3) // rgb values for each pixel
|
||||
|
||||
fmt.println("Starting CPU raytracing")
|
||||
// build bvh
|
||||
fmt.println("Building BVH")
|
||||
bvh_build()
|
||||
bvh_stats()
|
||||
|
||||
fmt.printf("Starting CPU raytracing")
|
||||
if bvh.num_leaf_nodes > 1 {
|
||||
fmt.printf(" - WITH BVH!")
|
||||
}
|
||||
fmt.printf("\n")
|
||||
|
||||
time.stopwatch_start(&stopwatch)
|
||||
cpu_raytracing()
|
||||
time.stopwatch_stop(&stopwatch)
|
||||
elapsed_ms := elapsed_time_ms()
|
||||
fmt.printf("Elapsed for CPU raytracing: %.4f ms \n", elapsed_ms)
|
||||
|
||||
|
||||
// Translate pixelbuffer with colors from 0 to 1, to rgb 0..255
|
||||
{
|
||||
@ -255,18 +286,23 @@ cpu_raytracing :: proc() {
|
||||
temp_hit_rec : HitRecord
|
||||
temp_hit_rec.hit = false
|
||||
temp_hit_rec.t = hit_rec.t
|
||||
for i in 0..<MAX_NUM_ENTITIES {
|
||||
tri_ref := &entities[i]
|
||||
triangle_intersection(&ray, &temp_hit_rec, tri_ref)
|
||||
if temp_hit_rec.hit {
|
||||
hit_rec.hit = true
|
||||
hit_rec.t = temp_hit_rec.t
|
||||
hit_rec.normal = temp_hit_rec.normal
|
||||
num_hits += 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
bvh_intersect(&ray, &hit_rec, bvh.root_index)
|
||||
}
|
||||
|
||||
//{
|
||||
// for i in 0..<MAX_NUM_ENTITIES {
|
||||
// tri_ref := &entities[i]
|
||||
// triangle_intersection(&ray, &temp_hit_rec, tri_ref)
|
||||
// if temp_hit_rec.hit {
|
||||
// hit_rec.hit = true
|
||||
// hit_rec.t = temp_hit_rec.t
|
||||
// hit_rec.normal = temp_hit_rec.normal
|
||||
// num_hits += 1
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
if hit_rec.hit {
|
||||
@ -292,4 +328,9 @@ cpu_raytracing :: proc() {
|
||||
}
|
||||
//fmt.printf("Num hits on triangles: %i \n", num_hits)
|
||||
|
||||
}
|
||||
|
||||
|
||||
elapsed_time_ms :: proc() -> f64 {
|
||||
return time.duration_milliseconds(time.stopwatch_duration(stopwatch))
|
||||
}
|
||||
193
src/rayt_bvh.odin
Normal file
193
src/rayt_bvh.odin
Normal file
@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:math/linalg"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BVHNode :: struct {
|
||||
aabb_min : Vec3,
|
||||
aabb_max : Vec3,
|
||||
left_first : u32,
|
||||
tri_count : u32
|
||||
}
|
||||
|
||||
BVH :: struct {
|
||||
nodes : []BVHNode,
|
||||
used_nodes : u32,
|
||||
root_index : u32,
|
||||
max_num_nodes : u32,
|
||||
num_leaf_nodes : u32,
|
||||
num_leaf_entities : u32
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bvh : BVH
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
intersect_aabb :: proc(ray : ^Ray, bmin : Vec3, bmax : Vec3, closest_so_far : f32) -> b32 {
|
||||
tx1 : f32 = (bmin.x - ray.origin.x) / ray.direction.x
|
||||
tx2 : f32 = (bmax.x - ray.origin.x) / ray.direction.x
|
||||
tmin : f32 = math.min(tx1, tx2)
|
||||
tmax : f32 = math.max(tx1, tx2)
|
||||
|
||||
ty1 : f32 = (bmin.y - ray.origin.y) / ray.direction.y
|
||||
ty2 : f32 = (bmax.y - ray.origin.y) / ray.direction.y
|
||||
tmin = math.max(tmin, math.min(ty1, ty2))
|
||||
tmax = math.min(tmax, math.max(ty1, ty2))
|
||||
|
||||
tz1 : f32 = (bmin.z - ray.origin.z) / ray.direction.z
|
||||
tz2 : f32 = (bmax.z - ray.origin.z) / ray.direction.z
|
||||
tmin = math.max(tmin, math.min(tz1, tz2))
|
||||
tmax = math.min(tmax, math.max(tz1, tz2))
|
||||
|
||||
out : b32 = tmax >= tmin && tmin < closest_so_far && tmax > 0.0
|
||||
return out
|
||||
}
|
||||
|
||||
bvh_update_bounds :: proc(node_idx : u32) {
|
||||
node : ^BVHNode = &bvh.nodes[node_idx]
|
||||
|
||||
node.aabb_min = Vec3{math.F32_MAX, math.F32_MAX, math.F32_MAX}
|
||||
node.aabb_max = Vec3{-math.F32_MAX, -math.F32_MAX, -math.F32_MAX}
|
||||
|
||||
first_tri_idx := node.left_first
|
||||
for i in 0..<node.tri_count {
|
||||
leaf_tri_idx := tri_indices[first_tri_idx + i]
|
||||
triangle : ^Entity = &entities[leaf_tri_idx]
|
||||
node.aabb_min = linalg.min(node.aabb_min, triangle.v0)
|
||||
node.aabb_min = linalg.min(node.aabb_min, triangle.v1)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
bvh_subdivide :: proc(node_idx : u32) {
|
||||
node : ^BVHNode = &bvh.nodes[node_idx]
|
||||
|
||||
if(node.tri_count <= bvh.num_leaf_entities) {
|
||||
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
|
||||
j : u32 = node.tri_count + i - 1
|
||||
|
||||
// Sort indices into partitions depending on split pos
|
||||
for i <= j {
|
||||
//fmt.printf("BVH node idx %i \n", node_idx)
|
||||
//fmt.printf("(i, j) = (%i, %i) \n", i, j)
|
||||
tri_idx : u32 = tri_indices[i]
|
||||
if entities[tri_idx].center[axis] < split_pos {
|
||||
i += 1
|
||||
} else {
|
||||
tri_indices[i] = tri_indices[j]
|
||||
tri_indices[j] = tri_idx
|
||||
j -= 1
|
||||
}
|
||||
}
|
||||
|
||||
left_count : u32 = i - node.left_first
|
||||
if left_count == 0 || left_count == node.tri_count {
|
||||
// One of the partitions is empty, stop subdividing
|
||||
return
|
||||
}
|
||||
|
||||
// Create child nodes and subdivide
|
||||
left_child_idx := bvh.used_nodes
|
||||
bvh.used_nodes += 1
|
||||
right_child_idx := bvh.used_nodes
|
||||
bvh.used_nodes += 1
|
||||
|
||||
bvh.nodes[left_child_idx].left_first = node.left_first
|
||||
bvh.nodes[left_child_idx].tri_count = left_count
|
||||
bvh.nodes[right_child_idx].left_first = i
|
||||
bvh.nodes[right_child_idx].tri_count = node.tri_count - left_count
|
||||
// Set the current node to not be a leaf node
|
||||
node.left_first = left_child_idx
|
||||
node.tri_count = 0
|
||||
|
||||
bvh_update_bounds(left_child_idx)
|
||||
bvh_update_bounds(right_child_idx)
|
||||
|
||||
bvh_subdivide(left_child_idx)
|
||||
bvh_subdivide(right_child_idx)
|
||||
|
||||
}
|
||||
|
||||
bvh_intersect :: proc(ray : ^Ray, rec : ^HitRecord, node_idx : u32) {
|
||||
node : ^BVHNode = &bvh.nodes[node_idx]
|
||||
|
||||
any_hit : b32 = false
|
||||
if intersect_aabb(ray, node.aabb_min, node.aabb_max, rec.t) {
|
||||
if node.tri_count > 0 {
|
||||
for i in 0..<node.tri_count {
|
||||
tri_idx := tri_indices[node.left_first + i]
|
||||
triangle : ^Entity = &entities[tri_idx]
|
||||
triangle_intersection(ray, rec, triangle)
|
||||
if rec.hit {
|
||||
any_hit = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bvh_intersect(ray, rec, node.left_first)
|
||||
bvh_intersect(ray, rec, node.left_first + 1)
|
||||
}
|
||||
}
|
||||
|
||||
if !rec.hit {
|
||||
rec.hit = any_hit
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bvh_build :: proc() {
|
||||
bvh.max_num_nodes = 2 * MAX_NUM_ENTITIES - 1
|
||||
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.num_leaf_entities = 8
|
||||
bvh.root_index = 0
|
||||
|
||||
// Init root node
|
||||
root : ^BVHNode = &bvh.nodes[bvh.root_index]
|
||||
root.left_first = 0
|
||||
root.tri_count = MAX_NUM_ENTITIES
|
||||
|
||||
bvh_update_bounds(bvh.root_index)
|
||||
|
||||
bvh_subdivide(bvh.root_index)
|
||||
|
||||
|
||||
}
|
||||
|
||||
bvh_stats :: proc() {
|
||||
do_print : b32 = false
|
||||
num_leaf_nodes : u32 = 0
|
||||
for i in 0..<bvh.max_num_nodes {
|
||||
node : ^BVHNode = &bvh.nodes[i]
|
||||
if node.tri_count > 0 {
|
||||
if do_print {
|
||||
fmt.printf("Node %i is leaf node with %i triangles \n", i, node.tri_count)
|
||||
}
|
||||
num_leaf_nodes += 1
|
||||
}
|
||||
}
|
||||
if do_print {
|
||||
fmt.printf("Total number of leaf nodes: %i \n", num_leaf_nodes)
|
||||
}
|
||||
bvh.num_leaf_nodes = num_leaf_nodes
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user