diff --git a/src/main.odin b/src/main.odin index a4ffa89..1a30bad 100644 --- a/src/main.odin +++ b/src/main.odin @@ -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() diff --git a/src/rayt_base.odin b/src/rayt_base.odin index 5472061..f9aecfd 100644 --- a/src/rayt_base.odin +++ b/src/rayt_base.odin @@ -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.. f64 { + return time.duration_milliseconds(time.stopwatch_duration(stopwatch)) } \ No newline at end of file diff --git a/src/rayt_bvh.odin b/src/rayt_bvh.odin new file mode 100644 index 0000000..f3ce404 --- /dev/null +++ b/src/rayt_bvh.odin @@ -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.. 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.. 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 +} \ No newline at end of file