working bvh raytrace of the unity model
This commit is contained in:
parent
d68f740c10
commit
d875d1130b
12582
assets/unity.tri
Normal file
12582
assets/unity.tri
Normal file
File diff suppressed because it is too large
Load Diff
112
src/main.odin
112
src/main.odin
@ -2,11 +2,12 @@ package main
|
||||
|
||||
import rl "vendor:raylib"
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WINDOW_WIDTH :: 1280
|
||||
WINDOW_HEIGHT :: 720
|
||||
WINDOW_HEIGHT : i32
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -16,24 +17,38 @@ rl_window_loop :: proc() {
|
||||
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Rayt");
|
||||
defer rl.CloseWindow()
|
||||
|
||||
do_debug_elements := false
|
||||
do_debug_model := false
|
||||
|
||||
rl_image := rl.Image {
|
||||
data = raw_data(pixelbuffer_rgb),
|
||||
width = cast(i32)image.width,
|
||||
height = cast(i32)image.height,
|
||||
mipmaps = 1,
|
||||
format = .UNCOMPRESSED_R8G8B8
|
||||
}
|
||||
defer rl.UnloadImage(rl_image)
|
||||
fmt.println("Created raylib image from rgb data")
|
||||
|
||||
rl_image := rl.Image {
|
||||
data = raw_data(pixelbuffer_rgb),
|
||||
width = cast(i32)image.width,
|
||||
height = cast(i32)image.height,
|
||||
mipmaps = 1,
|
||||
format = .UNCOMPRESSED_R8G8B8
|
||||
}
|
||||
defer rl.UnloadImage(rl_image)
|
||||
fmt.println("Created raylib image from rgb data")
|
||||
|
||||
texture := rl.LoadTextureFromImage(rl_image)
|
||||
defer rl.UnloadTexture(texture)
|
||||
fmt.println("Loaded texture from image")
|
||||
//rl_window_loop(texture)
|
||||
|
||||
|
||||
texture := rl.LoadTextureFromImage(rl_image)
|
||||
defer rl.UnloadTexture(texture)
|
||||
fmt.println("Loaded texture from image")
|
||||
|
||||
rl_camera := rl.Camera3D {
|
||||
position = {-2.0, 0.0, 6.0},
|
||||
target = {0.0, 0.0, 0.0},
|
||||
up = {0.0, 1.0, 0.0},
|
||||
fovy = 45,
|
||||
projection = .PERSPECTIVE
|
||||
}
|
||||
|
||||
mesh : rl.Mesh
|
||||
model : rl.Model
|
||||
if do_debug_model {
|
||||
mesh = create_mesh_from_triangles()
|
||||
model = rl.LoadModelFromMesh(mesh)
|
||||
}
|
||||
|
||||
|
||||
for !rl.WindowShouldClose() {
|
||||
@ -42,20 +57,33 @@ rl_window_loop :: proc() {
|
||||
rl.BeginDrawing()
|
||||
rl.ClearBackground(rl.BLUE)
|
||||
|
||||
// Display raytraced image
|
||||
rl.DrawTexture(texture, 0, 0, rl.WHITE)
|
||||
|
||||
rl.DrawCircle(400, 300, 50, rl.GREEN)
|
||||
rl.DrawLine(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, rl.BLUE)
|
||||
// Debug draw model
|
||||
if do_debug_model {
|
||||
rl.BeginMode3D(rl_camera)
|
||||
rl.DrawModel(model, {0.0, 0.0, 0.0}, 1, rl.RED)
|
||||
rl.DrawGrid(10, 1.0)
|
||||
rl.EndMode3D()
|
||||
}
|
||||
|
||||
if do_debug_elements {
|
||||
rl.DrawCircle(400, 300, 50, rl.GREEN)
|
||||
rl.DrawLine(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, rl.BLUE)
|
||||
|
||||
|
||||
rl.DrawCircle(100, 100, 120, rl.RED)
|
||||
|
||||
rl.DrawCircle(100, 100, 120, rl.RED)
|
||||
}
|
||||
|
||||
rl.EndDrawing()
|
||||
|
||||
}
|
||||
|
||||
|
||||
if do_debug_model {
|
||||
rl.UnloadMesh(mesh)
|
||||
rl.UnloadModel(model)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -63,7 +91,10 @@ rl_window_loop :: proc() {
|
||||
main :: proc() {
|
||||
rl.SetTraceLogLevel(rl.TraceLogLevel.ERROR)
|
||||
|
||||
// Fill pixelbuffer with raytraced image.
|
||||
WINDOW_HEIGHT = cast(i32)math.ceil((cast(f32)WINDOW_WIDTH/1.7778))
|
||||
fmt.printf("Window dimensions %i x %i \n", WINDOW_WIDTH, WINDOW_HEIGHT)
|
||||
|
||||
// Fill pixelbuffer with raytraced image.
|
||||
|
||||
rayt_cpu_main()
|
||||
|
||||
@ -71,3 +102,40 @@ main :: proc() {
|
||||
|
||||
rl_window_loop()
|
||||
}
|
||||
|
||||
create_mesh_from_triangles :: proc() -> rl.Mesh {
|
||||
vertex_count := len(tri_indices) * 3
|
||||
vertices := make([]f32, vertex_count * 3)
|
||||
indices := make([]u16, vertex_count)
|
||||
|
||||
for tri, i in entities {
|
||||
base_idx := i * 3
|
||||
// Vertex 0
|
||||
vertices[base_idx * 3 + 0] = tri.v0.x
|
||||
vertices[base_idx * 3 + 1] = tri.v0.y
|
||||
vertices[base_idx * 3 + 2] = tri.v0.z
|
||||
// Vertex 1
|
||||
vertices[base_idx * 3 + 3] = tri.v1.x
|
||||
vertices[base_idx * 3 + 4] = tri.v1.y
|
||||
vertices[base_idx * 3 + 5] = tri.v1.z
|
||||
// Vertex 2
|
||||
vertices[base_idx * 3 + 6] = tri.v2.x
|
||||
vertices[base_idx * 3 + 7] = tri.v2.y
|
||||
vertices[base_idx * 3 + 8] = tri.v2.z
|
||||
// Indices (simple sequential indices since each triangle is independent)
|
||||
indices[base_idx + 0] = cast(u16)(base_idx + 0)
|
||||
indices[base_idx + 1] = cast(u16)(base_idx + 1)
|
||||
indices[base_idx + 2] = cast(u16)(base_idx + 2)
|
||||
|
||||
}
|
||||
|
||||
mesh: rl.Mesh
|
||||
mesh.vertexCount = cast(i32)vertex_count
|
||||
mesh.triangleCount = cast(i32)len(tri_indices)
|
||||
mesh.vertices = &vertices[0]
|
||||
mesh.indices = &indices[0]
|
||||
|
||||
rl.UploadMesh(&mesh, false)
|
||||
|
||||
return mesh
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
package main
|
||||
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:math/rand"
|
||||
import "core:math/linalg"
|
||||
import "core:math"
|
||||
@ -17,9 +20,8 @@ COLOR_WHITE :: Vec3{1.0, 1.0, 1.0}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Global program parameters
|
||||
IMAGE_WIDTH :: 1280
|
||||
IMAGE_WIDTH :: WINDOW_WIDTH
|
||||
ASPECT_RATIO :: 1.7778 // 16:9
|
||||
MAX_NUM_ENTITIES :: 64
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Struct defs
|
||||
@ -56,7 +58,6 @@ HitRecord :: struct {
|
||||
point : Vec3,
|
||||
normal : Vec3,
|
||||
t : f32,
|
||||
hit : b32,
|
||||
front_face : b32,
|
||||
}
|
||||
|
||||
@ -75,13 +76,14 @@ Entity :: struct {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Main global variables
|
||||
stopwatch : time.Stopwatch
|
||||
use_bvh := true
|
||||
|
||||
image: Image
|
||||
camera: Camera
|
||||
viewport: Viewport
|
||||
|
||||
entities: [MAX_NUM_ENTITIES]Entity
|
||||
tri_indices: [MAX_NUM_ENTITIES]u32
|
||||
entities: []Entity
|
||||
tri_indices: []u32
|
||||
|
||||
|
||||
pixelbuffer: []Vec3
|
||||
@ -112,29 +114,31 @@ ray_get :: proc(x : f32, y : f32) -> Ray {
|
||||
|
||||
rayt_cpu_main :: proc() {
|
||||
rand.reset(RAND_SEED)
|
||||
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
//{
|
||||
// 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
|
||||
{
|
||||
@ -145,7 +149,7 @@ rayt_cpu_main :: proc() {
|
||||
image.width, image.height, image.aspect_ratio)
|
||||
|
||||
camera.focal_length = 3.0
|
||||
camera.center = Vec3{0.0, 0.0, 18.0}
|
||||
camera.center = Vec3{-2.0, 0.0, 4.0}
|
||||
|
||||
viewport.height = 2.0
|
||||
viewport.width = viewport.height * cast(f32)(image.width)/cast(f32)(image.height)
|
||||
@ -178,11 +182,11 @@ rayt_cpu_main :: proc() {
|
||||
bvh_stats()
|
||||
|
||||
fmt.printf("Starting CPU raytracing")
|
||||
if bvh.num_leaf_nodes > 1 {
|
||||
fmt.printf(" - WITH BVH!")
|
||||
if !use_bvh {
|
||||
fmt.printf(" - NB NB NB! NO BVH! WITHOUT BVH!")
|
||||
}
|
||||
fmt.printf("\n")
|
||||
|
||||
|
||||
time.stopwatch_start(&stopwatch)
|
||||
cpu_raytracing()
|
||||
time.stopwatch_stop(&stopwatch)
|
||||
@ -222,15 +226,15 @@ ray_point :: proc(t : f32, ray : ^Ray) -> Vec3 {
|
||||
triangle_intersection :: proc(ray : ^Ray, rec : ^HitRecord, triangle : ^Entity) {
|
||||
edge1 := triangle.v1-triangle.v0
|
||||
edge2 := triangle.v2-triangle.v0
|
||||
rec.hit = false
|
||||
|
||||
// Moller-Trumbore intersection algorithm
|
||||
closest_so_far : f32 = rec.t
|
||||
{
|
||||
h := linalg.cross(ray.direction, edge2)
|
||||
closest_so_far : f32 = rec.t
|
||||
a := linalg.dot(edge1, h)
|
||||
|
||||
if a <= -0.001 || a >= 0.001 {
|
||||
if a <= -0.0001 || a >= 0.0001 {
|
||||
f := 1.0/a
|
||||
s := ray.origin-triangle.v0
|
||||
u := f * linalg.dot(s, h)
|
||||
@ -239,17 +243,17 @@ triangle_intersection :: proc(ray : ^Ray, rec : ^HitRecord, triangle : ^Entity)
|
||||
v := f * linalg.dot(ray.direction, q)
|
||||
if v >= 0.0 && (u + v) <= 1.0 {
|
||||
t := f * linalg.dot(edge2, q)
|
||||
if t > 0.0001 && t <= closest_so_far {
|
||||
rec.hit = true
|
||||
rec.t = t
|
||||
if t > 0.0001 {
|
||||
rec.t = math.min(t, rec.t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if rec.hit {
|
||||
if rec.t < closest_so_far {
|
||||
intersection_point := ray_point(rec.t, ray)
|
||||
v0_normal := linalg.cross(edge1, edge2)
|
||||
v0_normal = linalg.normalize(v0_normal)
|
||||
@ -266,7 +270,7 @@ triangle_intersection :: proc(ray : ^Ray, rec : ^HitRecord, triangle : ^Entity)
|
||||
}
|
||||
|
||||
cpu_raytracing :: proc() {
|
||||
|
||||
do_trace_without_bvh := false
|
||||
num_hits : u32 = 0
|
||||
last_ray : Ray
|
||||
// Temp fill pixels
|
||||
@ -281,42 +285,28 @@ cpu_raytracing :: proc() {
|
||||
ray := ray_get(cast(f32)x, cast(f32)y)
|
||||
|
||||
|
||||
hit_rec.hit = false
|
||||
|
||||
hit_rec.t = math.F32_MAX
|
||||
temp_hit_rec : HitRecord
|
||||
temp_hit_rec.hit = false
|
||||
temp_hit_rec.t = hit_rec.t
|
||||
|
||||
{
|
||||
if use_bvh {
|
||||
bvh_intersect(&ray, &hit_rec, bvh.root_index)
|
||||
} else if do_trace_without_bvh {
|
||||
for i in 0..<len(entities) {
|
||||
tri_ref := &entities[i]
|
||||
triangle_intersection(&ray, &hit_rec, tri_ref)
|
||||
}
|
||||
}
|
||||
|
||||
//{
|
||||
// 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 {
|
||||
if hit_rec.t < math.F32_MAX {
|
||||
// Color triangle
|
||||
sample_pixel_color = 0.5*(hit_rec.normal + COLOR_WHITE)
|
||||
//sample_pixel_color = Vec3{0.7, 0.2, 0.2}
|
||||
} else {
|
||||
// Background gradient
|
||||
unit_dir := linalg.normalize(ray.direction)
|
||||
|
||||
blend : f32 = 0.5*(unit_dir.y + 1.0)
|
||||
|
||||
sample_pixel_color = vec3_lerp(blend, COLOR_WHITE, COLOR_LIGHT_BLUE)
|
||||
|
||||
|
||||
//sample_pixel_color = Vec3{0.0, 0.0, 0.0}
|
||||
|
||||
}
|
||||
|
||||
@ -330,7 +320,81 @@ cpu_raytracing :: proc() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
elapsed_time_ms :: proc() -> f64 {
|
||||
return time.duration_milliseconds(time.stopwatch_duration(stopwatch))
|
||||
}
|
||||
|
||||
load_triangles :: proc() {
|
||||
|
||||
do_debug_print := false
|
||||
|
||||
file_path := "W:/rayt/assets/unity.tri"
|
||||
data, ok := os.read_entire_file(file_path)
|
||||
if !ok {
|
||||
fmt.println("Error reading file: ", file_path)
|
||||
os.exit(1);
|
||||
}
|
||||
defer delete(data)
|
||||
|
||||
|
||||
content := string(data)
|
||||
lines := strings.split(content, "\n")
|
||||
num_triangles := len(lines)
|
||||
entities = make([]Entity, num_triangles)
|
||||
tri_indices = make([]u32, num_triangles)
|
||||
|
||||
entity_idx : u32 = 0
|
||||
for line in lines {
|
||||
trimmed := strings.trim_space(line)
|
||||
if len(trimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.split(trimmed, " ")
|
||||
defer delete(fields)
|
||||
|
||||
if len(fields) != 9 {
|
||||
fmt.printf("Warning, line '%s' does not contain 9 values \n", trimmed)
|
||||
continue
|
||||
}
|
||||
|
||||
values: [9]f32
|
||||
valid := true
|
||||
for field, i in fields {
|
||||
if num, ok := strconv.parse_f32(field); ok {
|
||||
values[i] = num
|
||||
} else {
|
||||
fmt.printf("Error: could not prase '%s' as f32 in line '%s' \n", field, trimmed)
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
os.exit(1)
|
||||
} else {
|
||||
if do_debug_print {
|
||||
fmt.printf("Creating triangle %i, ", entity_idx)
|
||||
}
|
||||
entities[entity_idx].v0 = Vec3{values[0], values[1], values[2]}
|
||||
entities[entity_idx].v1 = Vec3{values[3], values[4], values[5]}
|
||||
entities[entity_idx].v2 = Vec3{values[6], values[7], values[8]}
|
||||
entities[entity_idx].center = 0.3333*
|
||||
(entities[entity_idx].v0
|
||||
+ entities[entity_idx].v1 + entities[entity_idx].v2)
|
||||
tri_indices[entity_idx] = entity_idx
|
||||
if do_debug_print {
|
||||
fmt.printf("added to tri_indices[%i] = %i", entity_idx, tri_indices[entity_idx])
|
||||
}
|
||||
entity_idx += 1
|
||||
if do_debug_print {
|
||||
fmt.printf("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.printf("Parsed %i triangles from file %s \n", len(tri_indices), file_path)
|
||||
assert(num_triangles == len(tri_indices))
|
||||
assert(num_triangles == len(entities))
|
||||
assert(num_triangles == int(entity_idx))
|
||||
|
||||
}
|
||||
@ -121,26 +121,22 @@ bvh_subdivide :: proc(node_idx : u32) {
|
||||
node.tri_count = 0
|
||||
|
||||
bvh_update_bounds(left_child_idx)
|
||||
bvh_update_bounds(right_child_idx)
|
||||
|
||||
bvh_subdivide(left_child_idx)
|
||||
|
||||
bvh_update_bounds(right_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)
|
||||
@ -148,24 +144,20 @@ bvh_intersect :: proc(ray : ^Ray, rec : ^HitRecord, node_idx : u32) {
|
||||
}
|
||||
}
|
||||
|
||||
if !rec.hit {
|
||||
rec.hit = any_hit
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bvh_build :: proc() {
|
||||
bvh.max_num_nodes = 2 * MAX_NUM_ENTITIES - 1
|
||||
num_triangles : u32 = cast(u32)len(tri_indices)
|
||||
bvh.max_num_nodes = 2 * num_triangles - 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.num_leaf_entities = 2
|
||||
bvh.root_index = 0
|
||||
|
||||
// Init root node
|
||||
root : ^BVHNode = &bvh.nodes[bvh.root_index]
|
||||
root.left_first = 0
|
||||
root.tri_count = MAX_NUM_ENTITIES
|
||||
root.tri_count = num_triangles
|
||||
|
||||
bvh_update_bounds(bvh.root_index)
|
||||
|
||||
@ -175,19 +167,22 @@ bvh_build :: proc() {
|
||||
}
|
||||
|
||||
bvh_stats :: proc() {
|
||||
do_print : b32 = false
|
||||
do_print : b32 = true
|
||||
num_leaf_nodes : u32 = 0
|
||||
total_triangles_in_bvh : 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)
|
||||
//fmt.printf("Node %i is leaf node with %i triangles \n", i, node.tri_count)
|
||||
}
|
||||
num_leaf_nodes += 1
|
||||
total_triangles_in_bvh += node.tri_count
|
||||
}
|
||||
}
|
||||
if do_print {
|
||||
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)
|
||||
}
|
||||
bvh.num_leaf_nodes = num_leaf_nodes
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user