this repo has no description

added world

+143 -13
+1
build.zig
··· 24 24 .target = target, 25 25 .optimize = optimize, 26 26 }); 27 + addDeps(b, &exe.root_module); 27 28 exe.root_module.addImport("spall", spall.module("spall")); 28 29 exe.root_module.addImport("rayray", rayray); 29 30
+4
src/camera.zig
··· 72 72 }; 73 73 } 74 74 75 + pub fn deinit(self: *Camera) void { 76 + self.image.deinit(); 77 + } 78 + 75 79 pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void { 76 80 if (x >= self.image_width or y >= self.image_height) return error.OutOfBounds; 77 81 const i = x + self.image_width * y;
+74
src/hittable.zig
··· 1 + const std = @import("std"); 2 + 3 + const zm = @import("zmath"); 4 + 5 + const Ray = @import("ray.zig"); 6 + pub const Sphere = @import("hittable/sphere.zig"); 7 + 8 + pub const HitRecord = struct { 9 + p: zm.Vec, 10 + normal: zm.Vec = zm.f32x4s(1.0), 11 + t: f32, 12 + front_face: bool = true, 13 + 14 + pub fn setFaceNormal(self: *HitRecord, r: *Ray, outward_normal: zm.Vec) void { 15 + self.front_face = zm.dot3(r.dir, outward_normal)[0] < 0.0; 16 + self.normal = if (self.front_face) outward_normal else -outward_normal; 17 + } 18 + }; 19 + 20 + pub const HittableType = enum { 21 + sphere, 22 + }; 23 + 24 + pub const Hittable = union(HittableType) { 25 + sphere: Sphere, 26 + 27 + pub fn initSphere(sphere: Sphere) Hittable { 28 + return .{ .sphere = sphere }; 29 + } 30 + 31 + pub fn hit(self: *Hittable, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord { 32 + switch (self.*) { 33 + .sphere => |*sphere| { 34 + return sphere.hit(r, ray_tmin, ray_tmax); 35 + }, 36 + } 37 + 38 + return null; 39 + } 40 + }; 41 + 42 + pub const HittableList = struct { 43 + list: std.ArrayList(Hittable), 44 + 45 + pub fn init(allocator: std.mem.Allocator) HittableList { 46 + const list = std.ArrayList(Hittable).init(allocator); 47 + 48 + return .{ .list = list }; 49 + } 50 + 51 + pub fn deinit(self: *HittableList) void { 52 + self.list.deinit(); 53 + } 54 + 55 + pub fn add(self: *HittableList, item: Hittable) !void { 56 + try self.list.append(item); 57 + } 58 + 59 + pub fn hit(self: *HittableList, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord { 60 + var rec: ?HitRecord = null; 61 + var hit_anything = false; 62 + var closest_so_far = ray_tmax; 63 + 64 + for (self.list.items) |*object| { 65 + if (object.hit(r, ray_tmin, closest_so_far)) |new_rec| { 66 + rec = new_rec; 67 + hit_anything = true; 68 + closest_so_far = new_rec.t; 69 + } 70 + } 71 + 72 + return rec; 73 + } 74 + };
+38
src/hittable/sphere.zig
··· 1 + const zm = @import("zmath"); 2 + 3 + const Ray = @import("../ray.zig"); 4 + const HitRecord = @import("../hittable.zig").HitRecord; 5 + 6 + const Sphere = @This(); 7 + 8 + center: zm.Vec, 9 + radius: f32, 10 + 11 + pub fn hit(self: *Sphere, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord { 12 + const oc = r.orig - self.center; 13 + const a = zm.lengthSq3(r.dir)[0]; 14 + const half_b = zm.dot3(oc, r.dir)[0]; 15 + const c = zm.dot3(oc, oc)[0] - self.radius * self.radius; 16 + 17 + const discriminant = half_b * half_b - a * c; 18 + if (discriminant < 0) return null; 19 + 20 + const sqrtd = @sqrt(discriminant); 21 + 22 + // Find the nearest root that lies in the acceptable range 23 + var root = (-half_b - sqrtd) / a; 24 + if (root <= ray_tmin or ray_tmax <= root) { 25 + root = (-half_b + sqrtd) / a; 26 + if (root <= ray_tmin or ray_tmax <= root) return null; 27 + } 28 + 29 + var rec = HitRecord{ 30 + .t = root, 31 + .p = r.at(root), 32 + }; 33 + 34 + const outward_normal = (rec.p - self.center) / zm.f32x4s(self.radius); 35 + rec.setFaceNormal(r, outward_normal); 36 + 37 + return rec; 38 + }
+10 -2
src/main.zig
··· 2 2 3 3 const a = @import("a"); 4 4 const spall = @import("spall"); 5 + const zm = @import("zmath"); 5 6 6 - const Raytracer = @import("rayray").Raytracer; 7 + const rayray = @import("rayray"); 8 + const Hittable = rayray.hittable.Hittable; 9 + const HittableList = rayray.hittable.HittableList; 10 + const Sphere = rayray.hittable.Sphere; 7 11 8 12 pub const std_options = .{ 9 13 .log_level = .debug, ··· 21 25 spall.init_thread(); 22 26 defer spall.deinit_thread(); 23 27 28 + var world = HittableList.init(allocator); 29 + try world.add(Hittable.initSphere(Sphere{ .center = zm.f32x4(0, 0, -1, 0), .radius = 0.5 })); 30 + try world.add(Hittable.initSphere(Sphere{ .center = zm.f32x4(0, -100.5, -1, 0), .radius = 100 })); 31 + 24 32 const s = spall.trace(@src(), "Main", .{}); 25 33 26 - var raytracer = try Raytracer.init(allocator); 34 + var raytracer = try rayray.Raytracer.init(allocator, world); 27 35 defer raytracer.deinit(); 28 36 29 37 const img = try raytracer.render();
+6 -5
src/ray.zig
··· 1 + const std = @import("std"); 2 + 1 3 const zm = @import("zmath"); 2 4 5 + const hittable = @import("hittable.zig"); 3 6 const Ray = @This(); 4 7 5 8 orig: zm.Vec, ··· 16 19 return self.orig + zm.f32x4s(t) * self.dir; 17 20 } 18 21 19 - pub fn color(r: *Ray) zm.Vec { 20 - const t = hitSphere(zm.f32x4(0, 0, -1, 0), 0.5, r); 21 - if (t > 0.0) { 22 - const N = zm.normalize3(r.at(t) - zm.f32x4(0, 0, -1, 0)); 23 - return zm.f32x4s(0.5) * zm.f32x4(N[0] + 1, N[1] + 1, N[2] + 1, 1); 22 + pub fn color(r: *Ray, world: *hittable.HittableList) zm.Vec { 23 + if (world.hit(r, 0, std.math.inf(f32))) |rec| { 24 + return zm.f32x4s(0.5) * (rec.normal + zm.f32x4(1, 1, 1, 1)); 24 25 } 25 26 26 27 const unit_direction = zm.normalize3(r.dir);
+10 -6
src/rayray.zig
··· 6 6 const zm = @import("zmath"); 7 7 8 8 const Camera = @import("camera.zig"); 9 + pub const hittable = @import("hittable.zig"); 9 10 const Ray = @import("ray.zig"); 10 11 11 12 const log = std.log.scoped(.rayray); ··· 16 17 allocator: std.mem.Allocator, 17 18 18 19 camera: Camera, 20 + world: hittable.HittableList, 19 21 20 - pub fn init(allocator: std.mem.Allocator) !Self { 22 + pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList) !Self { 21 23 return .{ 22 24 .allocator = allocator, 23 25 .camera = try Camera.init(allocator, 400, 16.0 / 9.0), 26 + .world = world, 24 27 }; 25 28 } 26 29 27 - pub fn deinit(self: *const Self) void { 28 - _ = self; 30 + pub fn deinit(self: *Self) void { 31 + self.camera.deinit(); 32 + self.world.deinit(); 29 33 } 30 34 31 35 // TODO: Render in cubes not in rows ··· 50 54 const finished_threads = try self.allocator.alloc(bool, num_threads); 51 55 52 56 for (0..num_threads) |row| { 53 - const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, row, row_height, &threads[row].done }); 57 + const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, &self.world, row, row_height, &threads[row].done }); 54 58 threads[row].thread = t; 55 59 } 56 60 ··· 85 89 return self.camera.image; 86 90 } 87 91 88 - fn render_thread(cam: *Camera, row: usize, height: usize, done: *bool) void { 92 + fn render_thread(cam: *Camera, world: *hittable.HittableList, row: usize, height: usize, done: *bool) void { 89 93 spall.init_thread(); 90 94 defer spall.deinit_thread(); 91 95 ··· 102 106 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); 103 107 const ray_direction = pixel_center - cam.camera_center; 104 108 var ray = Ray.init(cam.camera_center, ray_direction); 105 - const col = vecToRgba(ray.color()); 109 + const col = vecToRgba(ray.color(world)); 106 110 107 111 cam.setPixel(i, j, col) catch break; 108 112 }