From 9763abcdc1e9cec5c8a14e03374125de29bd3e74 Mon Sep 17 00:00:00 2001 From: Altagos Date: Thu, 29 Feb 2024 16:16:33 +0100 Subject: [PATCH] added world --- build.zig | 1 + src/camera.zig | 4 +++ src/hittable.zig | 74 +++++++++++++++++++++++++++++++++++++++++ src/hittable/sphere.zig | 38 +++++++++++++++++++++ src/main.zig | 12 +++++-- src/ray.zig | 11 +++--- src/rayray.zig | 16 +++++---- 7 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 src/hittable.zig create mode 100644 src/hittable/sphere.zig diff --git a/build.zig b/build.zig index 713502e..657e216 100644 --- a/build.zig +++ b/build.zig @@ -24,6 +24,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + addDeps(b, &exe.root_module); exe.root_module.addImport("spall", spall.module("spall")); exe.root_module.addImport("rayray", rayray); diff --git a/src/camera.zig b/src/camera.zig index 0849914..60a251c 100644 --- a/src/camera.zig +++ b/src/camera.zig @@ -72,6 +72,10 @@ pub fn init(allocator: std.mem.Allocator, image_width: usize, aspect_ratio: f32) }; } +pub fn deinit(self: *Camera) void { + self.image.deinit(); +} + pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void { if (x >= self.image_width or y >= self.image_height) return error.OutOfBounds; const i = x + self.image_width * y; diff --git a/src/hittable.zig b/src/hittable.zig new file mode 100644 index 0000000..ae6bf49 --- /dev/null +++ b/src/hittable.zig @@ -0,0 +1,74 @@ +const std = @import("std"); + +const zm = @import("zmath"); + +const Ray = @import("ray.zig"); +pub const Sphere = @import("hittable/sphere.zig"); + +pub const HitRecord = struct { + p: zm.Vec, + normal: zm.Vec = zm.f32x4s(1.0), + t: f32, + front_face: bool = true, + + pub fn setFaceNormal(self: *HitRecord, r: *Ray, outward_normal: zm.Vec) void { + self.front_face = zm.dot3(r.dir, outward_normal)[0] < 0.0; + self.normal = if (self.front_face) outward_normal else -outward_normal; + } +}; + +pub const HittableType = enum { + sphere, +}; + +pub const Hittable = union(HittableType) { + sphere: Sphere, + + pub fn initSphere(sphere: Sphere) Hittable { + return .{ .sphere = sphere }; + } + + pub fn hit(self: *Hittable, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord { + switch (self.*) { + .sphere => |*sphere| { + return sphere.hit(r, ray_tmin, ray_tmax); + }, + } + + return null; + } +}; + +pub const HittableList = struct { + list: std.ArrayList(Hittable), + + pub fn init(allocator: std.mem.Allocator) HittableList { + const list = std.ArrayList(Hittable).init(allocator); + + return .{ .list = list }; + } + + pub fn deinit(self: *HittableList) void { + self.list.deinit(); + } + + pub fn add(self: *HittableList, item: Hittable) !void { + try self.list.append(item); + } + + pub fn hit(self: *HittableList, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord { + var rec: ?HitRecord = null; + var hit_anything = false; + var closest_so_far = ray_tmax; + + for (self.list.items) |*object| { + if (object.hit(r, ray_tmin, closest_so_far)) |new_rec| { + rec = new_rec; + hit_anything = true; + closest_so_far = new_rec.t; + } + } + + return rec; + } +}; diff --git a/src/hittable/sphere.zig b/src/hittable/sphere.zig new file mode 100644 index 0000000..da791b3 --- /dev/null +++ b/src/hittable/sphere.zig @@ -0,0 +1,38 @@ +const zm = @import("zmath"); + +const Ray = @import("../ray.zig"); +const HitRecord = @import("../hittable.zig").HitRecord; + +const Sphere = @This(); + +center: zm.Vec, +radius: f32, + +pub fn hit(self: *Sphere, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord { + const oc = r.orig - self.center; + const a = zm.lengthSq3(r.dir)[0]; + const half_b = zm.dot3(oc, r.dir)[0]; + const c = zm.dot3(oc, oc)[0] - self.radius * self.radius; + + const discriminant = half_b * half_b - a * c; + if (discriminant < 0) return null; + + const sqrtd = @sqrt(discriminant); + + // Find the nearest root that lies in the acceptable range + var root = (-half_b - sqrtd) / a; + if (root <= ray_tmin or ray_tmax <= root) { + root = (-half_b + sqrtd) / a; + if (root <= ray_tmin or ray_tmax <= root) return null; + } + + var rec = HitRecord{ + .t = root, + .p = r.at(root), + }; + + const outward_normal = (rec.p - self.center) / zm.f32x4s(self.radius); + rec.setFaceNormal(r, outward_normal); + + return rec; +} diff --git a/src/main.zig b/src/main.zig index c6c6dd1..7a2fbae 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,8 +2,12 @@ const std = @import("std"); const a = @import("a"); const spall = @import("spall"); +const zm = @import("zmath"); -const Raytracer = @import("rayray").Raytracer; +const rayray = @import("rayray"); +const Hittable = rayray.hittable.Hittable; +const HittableList = rayray.hittable.HittableList; +const Sphere = rayray.hittable.Sphere; pub const std_options = .{ .log_level = .debug, @@ -21,9 +25,13 @@ pub fn main() !void { spall.init_thread(); defer spall.deinit_thread(); + var world = HittableList.init(allocator); + try world.add(Hittable.initSphere(Sphere{ .center = zm.f32x4(0, 0, -1, 0), .radius = 0.5 })); + try world.add(Hittable.initSphere(Sphere{ .center = zm.f32x4(0, -100.5, -1, 0), .radius = 100 })); + const s = spall.trace(@src(), "Main", .{}); - var raytracer = try Raytracer.init(allocator); + var raytracer = try rayray.Raytracer.init(allocator, world); defer raytracer.deinit(); const img = try raytracer.render(); diff --git a/src/ray.zig b/src/ray.zig index 91b39a4..493607c 100644 --- a/src/ray.zig +++ b/src/ray.zig @@ -1,5 +1,8 @@ +const std = @import("std"); + const zm = @import("zmath"); +const hittable = @import("hittable.zig"); const Ray = @This(); orig: zm.Vec, @@ -16,11 +19,9 @@ pub fn at(self: *Ray, t: f32) zm.Vec { return self.orig + zm.f32x4s(t) * self.dir; } -pub fn color(r: *Ray) zm.Vec { - const t = hitSphere(zm.f32x4(0, 0, -1, 0), 0.5, r); - if (t > 0.0) { - const N = zm.normalize3(r.at(t) - zm.f32x4(0, 0, -1, 0)); - return zm.f32x4s(0.5) * zm.f32x4(N[0] + 1, N[1] + 1, N[2] + 1, 1); +pub fn color(r: *Ray, world: *hittable.HittableList) zm.Vec { + if (world.hit(r, 0, std.math.inf(f32))) |rec| { + return zm.f32x4s(0.5) * (rec.normal + zm.f32x4(1, 1, 1, 1)); } const unit_direction = zm.normalize3(r.dir); diff --git a/src/rayray.zig b/src/rayray.zig index 0e93828..d08b530 100644 --- a/src/rayray.zig +++ b/src/rayray.zig @@ -6,6 +6,7 @@ const color = zigimg.color; const zm = @import("zmath"); const Camera = @import("camera.zig"); +pub const hittable = @import("hittable.zig"); const Ray = @import("ray.zig"); const log = std.log.scoped(.rayray); @@ -16,16 +17,19 @@ pub const Raytracer = struct { allocator: std.mem.Allocator, camera: Camera, + world: hittable.HittableList, - pub fn init(allocator: std.mem.Allocator) !Self { + pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList) !Self { return .{ .allocator = allocator, .camera = try Camera.init(allocator, 400, 16.0 / 9.0), + .world = world, }; } - pub fn deinit(self: *const Self) void { - _ = self; + pub fn deinit(self: *Self) void { + self.camera.deinit(); + self.world.deinit(); } // TODO: Render in cubes not in rows @@ -50,7 +54,7 @@ pub const Raytracer = struct { const finished_threads = try self.allocator.alloc(bool, num_threads); for (0..num_threads) |row| { - const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, row, row_height, &threads[row].done }); + const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, &self.world, row, row_height, &threads[row].done }); threads[row].thread = t; } @@ -85,7 +89,7 @@ pub const Raytracer = struct { return self.camera.image; } - fn render_thread(cam: *Camera, row: usize, height: usize, done: *bool) void { + fn render_thread(cam: *Camera, world: *hittable.HittableList, row: usize, height: usize, done: *bool) void { spall.init_thread(); defer spall.deinit_thread(); @@ -102,7 +106,7 @@ pub const Raytracer = struct { const pixel_center = cam.pixel00_loc + (zm.f32x4s(@as(f32, @floatFromInt(i))) * cam.pixel_delta_u) + (zm.f32x4s(@as(f32, @floatFromInt(j))) * cam.pixel_delta_v); const ray_direction = pixel_center - cam.camera_center; var ray = Ray.init(cam.camera_center, ray_direction); - const col = vecToRgba(ray.color()); + const col = vecToRgba(ray.color(world)); cam.setPixel(i, j, col) catch break; } -- 2.51.2