this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

a lot of stuff

Altagos ac0dba66 bb01e58f

+382 -205
+57
src/Scene.zig
··· 1 + const std = @import("std"); 2 + 3 + const Camera = @import("Camera.zig"); 4 + const hittable = @import("hittable.zig"); 5 + const Material = @import("material.zig").Material; 6 + const Texture = @import("texture.zig").Texture; 7 + 8 + allocator: std.mem.Allocator, 9 + 10 + camera: Camera = undefined, 11 + world: hittable.HittableList, 12 + 13 + materials: std.ArrayList(*Material), 14 + textures: std.ArrayList(*Texture), 15 + 16 + const Self = @This(); 17 + 18 + pub fn init(alloc: std.mem.Allocator) Self { 19 + return Self{ 20 + .allocator = alloc, 21 + .world = hittable.HittableList.init(alloc), 22 + .materials = std.ArrayList(*Material).init(alloc), 23 + .textures = std.ArrayList(*Texture).init(alloc), 24 + }; 25 + } 26 + 27 + pub fn deinit(self: *Self) void { 28 + self.camera.deinit(); 29 + self.world.deinit(); 30 + 31 + for (self.materials.items) |mat| { 32 + self.allocator.destroy(mat); 33 + } 34 + 35 + for (self.textures.items) |tex| { 36 + self.allocator.destroy(tex); 37 + } 38 + 39 + self.materials.deinit(); 40 + self.textures.deinit(); 41 + } 42 + 43 + pub fn createMaterial(self: *Self, mat: anytype) !*Material { 44 + const ptr = try Material.init(self.allocator, mat); 45 + try self.materials.append(ptr); 46 + return ptr; 47 + } 48 + 49 + pub fn createTexture(self: *Self, tex: anytype) !*Texture { 50 + const ptr = try Texture.init(self.allocator, tex); 51 + try self.textures.append(ptr); 52 + return ptr; 53 + } 54 + 55 + pub fn setCamera(self: *Self, cam: Camera.Options) !void { 56 + self.camera = try Camera.init(self.allocator, cam); 57 + }
+5 -12
src/hittable.zig
··· 26 26 }; 27 27 28 28 pub const Hittable = union(enum) { 29 - sphere: struct { Sphere, []const u8 }, 29 + sphere: Sphere, 30 30 31 - pub fn initSphere(name: []const u8, s: Sphere) Hittable { 32 - // std.log.info("created sphere with mat: {}", .{s.mat}); 33 - return .{ .sphere = .{ s, name } }; 31 + pub fn initSphere(s: Sphere) Hittable { 32 + return .{ .sphere = s }; 34 33 } 35 34 36 35 pub fn boundingBox(self: *Hittable) AABB { 37 36 switch (self.*) { 38 - inline else => |*n| return n[0].bbox, 39 - } 40 - } 41 - 42 - pub fn getName(self: *Hittable) []const u8 { 43 - switch (self.*) { 44 - .sphere => |*s| return s[1], 37 + inline else => |*n| return n.bbox, 45 38 } 46 39 } 47 40 48 41 pub fn hit(self: *const Hittable, r: *Ray, ray_t: IntervalF32) ?HitRecord { 49 42 switch (self.*) { 50 - inline else => |*n| return n[0].hit(r, ray_t), 43 + inline else => |*n| return n.hit(r, ray_t), 51 44 } 52 45 } 53 46 };
+3 -3
src/interval.zig
··· 60 60 pub fn next(self: *Iterator) ?T { 61 61 self.current += 1; 62 62 if (self.current < self.interval.max or (self.current == self.interval.max and self.upper_boundry == .inclusive)) { 63 - return self.current; 63 + return self.current - 1; 64 64 } else return null; 65 65 } 66 66 67 67 pub fn nextInc(self: *Iterator) ?T { 68 68 self.current += 1; 69 - return if (self.current <= self.interval.max) self.current else null; 69 + return if (self.current <= self.interval.max) self.current - 1 else null; 70 70 } 71 71 72 72 pub fn nextExc(self: *Iterator) ?T { 73 73 self.current += 1; 74 - return if (self.current < self.interval.max) self.current else null; 74 + return if (self.current < self.interval.max) self.current - 1 else null; 75 75 } 76 76 }; 77 77
+14 -4
src/main.zig
··· 9 9 const Sphere = rayray.hittable.Sphere; 10 10 const zm = rayray.zmath; 11 11 12 - const scences = @import("scences.zig"); 12 + const scenes = @import("scenes.zig"); 13 13 14 14 pub const std_options = .{ 15 15 .log_level = .debug, ··· 21 21 defer arena.deinit(); 22 22 const allocator = arena.allocator(); 23 23 24 + // var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 25 + // const allocator = gpa.allocator(); 26 + // defer { 27 + // const deinit_status = gpa.deinit(); 28 + // //fail test; can't try in defer as defer is executed after we return 29 + // if (deinit_status == .leak) @panic("LEAK"); 30 + // } 31 + 24 32 // Setting up the world 25 - var scence = try scences.checker(allocator); 26 - defer scence.deinit(); 33 + var scene = rayray.Scene.init(allocator); 34 + defer scene.deinit(); 35 + 36 + try scenes.inOneWeekend(&scene); 27 37 28 38 std.log.info("World created", .{}); 29 39 30 40 // Raytracing part 31 - var raytracer = try rayray.Raytracer.init(allocator, scence.world, scence.camera); 41 + var raytracer = try rayray.Raytracer.init(allocator, scene); 32 42 defer raytracer.deinit(); 33 43 34 44 var timer = try std.time.Timer.start();
+52 -11
src/material.zig
··· 1 1 const math = @import("std").math; 2 + const mem = @import("std").mem; 2 3 3 4 const zm = @import("zmath"); 4 5 ··· 7 8 const util = @import("util.zig"); 8 9 const texture = @import("texture.zig"); 9 10 10 - pub const Material = union(enum) { 11 + pub const MaterialType = enum { 12 + lambertian, 13 + metal, 14 + dielectric, 15 + textured, 16 + }; 17 + 18 + pub const Material = union(MaterialType) { 11 19 lambertian: Lambertian, 12 20 metal: Metal, 13 21 dielectric: Dielectric, 22 + textured: Textured, 23 + 24 + pub fn init(alloc: mem.Allocator, data: anytype) !*Material { 25 + const material = try alloc.create(Material); 26 + 27 + switch (@TypeOf(data)) { 28 + Metal => material.* = .{ .metal = data }, 29 + Dielectric => material.* = .{ .dielectric = data }, 30 + 31 + Lambertian => material.* = .{ .lambertian = data }, 32 + zm.Vec => material.* = .{ .lambertian = .{ .albedo = data } }, 33 + 34 + Textured => material.* = .{ .textured = data }, 35 + *texture.Texture => material.* = .{ .textured = .{ .tex = data } }, 36 + 37 + else => @panic("Cannot infer Material type of: " ++ @typeName(@TypeOf(data))), 38 + } 39 + 40 + return material; 41 + } 14 42 15 43 pub fn initLambertian(tex: texture.Texture) Material { 16 44 return .{ .lambertian = .{ .tex = tex } }; ··· 28 56 return .{ .dielectric = .{ .refraction_index = refraction_index } }; 29 57 } 30 58 31 - pub inline fn scatter(self: *Material, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 59 + pub inline fn scatter(self: *Material, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 32 60 return switch (self.*) { 33 - .lambertian => |*lambert| lambert.scatter(r, rec, attenuation), 34 - .metal => |*met| met.scatter(r, rec, attenuation), 35 - .dielectric => |*die| die.scatter(r, rec, attenuation), 61 + inline else => |*n| n.scatter(r, rec, attenuation), 36 62 }; 37 63 } 38 64 }; 39 65 40 66 pub const Lambertian = struct { 41 - tex: texture.Texture, 67 + albedo: zm.Vec, 68 + 69 + pub inline fn scatter(self: *Lambertian, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 70 + var scatter_dir = rec.normal + util.randomUnitVec(); 71 + 72 + if (util.nearZero(scatter_dir)) scatter_dir = rec.normal; 73 + 74 + attenuation.* = self.albedo; 75 + return Ray{ .orig = rec.p, .dir = scatter_dir, .tm = r.tm }; 76 + } 77 + }; 78 + 79 + pub const Textured = struct { 80 + tex: *texture.Texture, 42 81 43 - pub inline fn scatter(self: *Lambertian, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 82 + pub inline fn scatter(self: *Textured, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 44 83 var scatter_dir = rec.normal + util.randomUnitVec(); 45 84 46 85 if (util.nearZero(scatter_dir)) scatter_dir = rec.normal; 47 86 48 87 attenuation.* = self.tex.value(rec.u, rec.v, rec.p); 49 - // return Ray.initT(rec.p, scatter_dir, r.tm); 50 88 return Ray{ .orig = rec.p, .dir = scatter_dir, .tm = r.tm }; 51 89 } 52 90 }; ··· 56 94 /// fuzz < 1 57 95 fuzz: f32, 58 96 59 - pub inline fn scatter(self: *Metal, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 97 + pub fn init(albedo: zm.Vec, fuzz: f32) Metal { 98 + return .{ .albedo = albedo, .fuzz = if (fuzz < 1) fuzz else 1.0 }; 99 + } 100 + 101 + pub inline fn scatter(self: *Metal, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 60 102 const reflected = util.reflect(r.dir, rec.normal); 61 103 const scattered = Ray.initT(rec.p, zm.normalize3(reflected) + zm.f32x4s(self.fuzz) * util.randomUnitVec(), r.tm); 62 104 attenuation.* = self.albedo; ··· 67 109 pub const Dielectric = struct { 68 110 refraction_index: f32, 69 111 70 - pub fn scatter(self: *Dielectric, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 112 + pub fn scatter(self: *Dielectric, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 71 113 attenuation.* = zm.f32x4s(1.0); 72 114 const ri = if (rec.front_face) (1.0 / self.refraction_index) else self.refraction_index; 73 115 ··· 81 123 else 82 124 util.refract(unit_direction, rec.normal, ri); 83 125 84 - // return Ray.initT(rec.p, direction, r.tm); 85 126 return Ray{ .orig = rec.p, .dir = direction, .tm = r.tm }; 86 127 } 87 128
+74 -24
src/rayray.zig
··· 12 12 pub const interval = @import("interval.zig"); 13 13 const IntervalUsize = interval.IntervalUsize; 14 14 pub const material = @import("material.zig"); 15 + pub const Scene = @import("Scene.zig"); 15 16 pub const texture = @import("texture.zig"); 16 17 pub const tracer = @import("tracer.zig"); 17 18 pub const util = @import("util.zig"); ··· 30 31 allocator: std.mem.Allocator, 31 32 thread_pool: *std.Thread.Pool, 32 33 33 - camera: Camera, 34 - world: hittable.HittableList, 34 + scene: Scene, 35 35 36 - pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList, camera_opts: Camera.Options) !Self { 36 + pub fn init(allocator: std.mem.Allocator, scene: Scene) !Self { 37 37 var thread_pool = try allocator.create(std.Thread.Pool); 38 38 try thread_pool.init(.{ .allocator = allocator }); 39 39 40 40 return .{ 41 41 .allocator = allocator, 42 42 .thread_pool = thread_pool, 43 - .camera = try Camera.init(allocator, camera_opts), 44 - .world = world, 43 + .scene = scene, 45 44 }; 46 45 } 47 46 48 47 pub fn deinit(self: *Self) void { 49 - self.camera.deinit(); 50 - self.world.deinit(); 51 - 48 + self.scene.deinit(); 52 49 self.thread_pool.deinit(); 53 50 self.allocator.destroy(self.thread_pool); 54 51 } ··· 57 54 const chunk_height: usize = 25; 58 55 const chunk_width: usize = 25; 59 56 60 - var rows: usize = @divTrunc(self.camera.image_height, chunk_height); 61 - if (self.camera.image_height % rows != 0) { 57 + var rows: usize = @divTrunc(self.scene.camera.image_height, chunk_height); 58 + if (self.scene.camera.image_height % rows != 0) { 62 59 rows += 1; 63 60 } 64 61 65 - var cols: usize = @divTrunc(self.camera.image_width, chunk_width); 66 - if (self.camera.image_width % cols != 0) { 62 + var cols: usize = @divTrunc(self.scene.camera.image_width, chunk_width); 63 + if (self.scene.camera.image_width % cols != 0) { 67 64 cols += 1; 68 65 } 69 66 ··· 76 73 } else break :blk 1; 77 74 }; 78 75 79 - log.debug("rows: {}, cols: {}, chunk_height: {}, chunk_width: {}, num_chunks: {}, num_threads: {}", .{ 76 + log.debug("with: {}, height: {}, rows: {}, cols: {}, chunk_height: {}, chunk_width: {}, num_chunks: {}, num_threads: {}", .{ 77 + self.scene.camera.image_width, 78 + self.scene.camera.image_height, 80 79 rows, 81 80 cols, 82 81 chunk_height, ··· 87 86 88 87 var root_node = std.Progress.start(.{ 89 88 .root_name = "Ray Tracer", 90 - .estimated_total_items = 3, 89 + .estimated_total_items = 4, 91 90 }); 92 91 93 92 var bvh_node = root_node.start("Createing BVH", 0); 94 93 95 - var world_bvh = try BVH.init(self.allocator, self.world, build_options.max_depth); 94 + var world_bvh = try BVH.init(self.allocator, self.scene.world, build_options.max_depth); 96 95 97 96 bvh_node.end(); 98 97 root_node.setCompletedItems(0); 99 98 99 + var create_pixels_node = root_node.start("Create pixel array", 0); 100 + 101 + const pixels = try self.allocator.alloc(zmath.Vec, self.scene.camera.image_height * self.scene.camera.image_width); 102 + defer self.allocator.free(pixels); 103 + // const l = pixels.ptr; 104 + 105 + create_pixels_node.end(); 106 + root_node.setCompletedItems(1); 107 + 100 108 var task_node = root_node.start("Creating render tasks", 0); 101 109 102 110 const tasks = try self.allocator.alloc(TaskTracker, num_chunks); 103 111 defer self.allocator.free(tasks); 104 112 105 113 for (tasks, 0..) |*t, id| { 106 - const row: usize = @divTrunc(id, cols) * chunk_height; 107 - const col: usize = (id - cols * @divTrunc(id, cols)) * chunk_width; 114 + // const row: usize = @divTrunc(id, cols) * chunk_height; 115 + // const col: usize = (id - cols * @divTrunc(id, cols)) * chunk_width; 116 + const row: usize = (id / cols) * chunk_height; 117 + const col: usize = (id % cols) * chunk_width; 108 118 109 119 const c_height = IntervalUsize{ .min = row, .max = row + chunk_height }; 110 120 const c_width = IntervalUsize{ .min = col, .max = col + chunk_width + 1 }; 111 121 112 122 const ctx = tracer.Context{ 113 - .cam = &self.camera, 123 + .pixels = pixels, 124 + .cam = &self.scene.camera, 114 125 .world = &world_bvh, 115 126 .height = c_height, 116 127 .width = c_width, ··· 123 134 } 124 135 125 136 task_node.end(); 126 - root_node.setCompletedItems(1); 137 + root_node.setCompletedItems(2); 127 138 128 139 var render_node = root_node.start("Rendering", num_chunks); 129 140 ··· 154 165 const node_msg = try std.fmt.allocPrint(self.allocator, "Render Thread #{}", .{i}); 155 166 defer self.allocator.free(node_msg); 156 167 try nodes.append(render_node.start(node_msg, num_chunks / num_threads)); 157 - root_node.setCompletedItems(1); 168 + root_node.setCompletedItems(3); 169 + // completed_chunks -= if (completed_chunks == 0) 0 else 1; 158 170 159 171 i += 1; 160 - break :blk i; 172 + std.debug.assert(i <= num_threads); 173 + break :blk i - 1; 161 174 }; 162 175 nodes.items[idx].completeOne(); 163 176 177 + // if (i == 1) continue; 164 178 completed_chunks += 1; 165 179 render_node.setCompletedItems(completed_chunks); 166 - // if (completed_chunks % self.thread_pool.threads.len == 0) try self.camera.image.writeToFilePath("./out/out.png", .{ .png = .{} }); 180 + if (completed_chunks % self.thread_pool.threads.len == 0) try self.scene.camera.image.writeToFilePath("./out/out.png", .{ .png = .{} }); 167 181 } else if (!task_done) { 168 182 done = false; 169 183 } ··· 172 186 if (done or !self.thread_pool.is_running) break; 173 187 } 174 188 189 + std.debug.assert(completed_chunks == num_chunks); 190 + log.info("Rendering done!", .{}); 191 + 175 192 render_node.end(); 176 - root_node.setCompletedItems(3); 193 + root_node.setCompletedItems(4); 194 + 195 + var image_node = root_node.start("Creating Image", 0); 196 + defer image_node.end(); 197 + 198 + for (pixels, 0..) |pix, p| { 199 + const y = p / self.scene.camera.image_width; 200 + const x = p % self.scene.camera.image_width; 201 + if (pix[0] < 0 or pix[1] < 0 or pix[2] < 0) { 202 + // std.log.debug("wrong ({}, {}) {}", .{ x, y, pix }); 203 + try self.scene.camera.setPixel(x, y, zigimg.color.Rgba32.initRgb(255, 0, 0)); 204 + continue; 205 + } 206 + self.scene.camera.setPixel(x, y, vecToRgba(pix, self.scene.camera.samples_per_pixel_v)) catch continue; 207 + } 177 208 178 - return self.camera.image; 209 + return self.scene.camera.image; 179 210 } 180 211 }; 181 212 ··· 184 215 task.thread_id = std.Thread.getCurrentId(); 185 216 tracer.trace(ctx); 186 217 } 218 + 219 + const zero = zmath.f32x4s(0.0); 220 + const nearly_one = zmath.f32x4s(0.999); 221 + const v256 = zmath.f32x4s(256); 222 + 223 + inline fn vecToRgba(v: zmath.Vec, samples_per_pixel: zmath.Vec) zigimg.color.Rgba32 { 224 + const rgba = zmath.clampFast( 225 + @sqrt(v / samples_per_pixel), 226 + zero, 227 + nearly_one, 228 + ) * v256; // linear to gamma 229 + 230 + return zigimg.color.Rgba32.initRgba( 231 + @intFromFloat(rgba[0]), 232 + @intFromFloat(rgba[1]), 233 + @intFromFloat(rgba[2]), 234 + @intFromFloat(rgba[3]), 235 + ); 236 + }
-2
src/scences.zig
··· 1 - pub const checker = @import("scences/checker.zig").scene; 2 - pub const inOneWeekend = @import("scences/in_one_weekend.zig").scene;
-46
src/scences/checker.zig
··· 1 - const std = @import("std"); 2 - 3 - const rayray = @import("rayray"); 4 - const Camera = rayray.Camera; 5 - const Hittable = rayray.hittable.Hittable; 6 - const HittableList = rayray.hittable.HittableList; 7 - const Material = rayray.material.Material; 8 - const Sphere = rayray.hittable.Sphere; 9 - const tex = rayray.texture; 10 - const zm = rayray.zmath; 11 - 12 - camera: Camera.Options, 13 - world: HittableList, 14 - allocator: std.mem.Allocator, 15 - 16 - pub fn scene(allocator: std.mem.Allocator) !@This() { 17 - var world = HittableList.init(allocator); 18 - 19 - const c1 = try allocator.create(tex.Texture); 20 - c1.* = tex.Texture{ .solid_color = tex.SolidColor.rgb(0.2, 0.3, 0.1) }; 21 - const c2 = try allocator.create(tex.Texture); 22 - c2.* = tex.Texture{ .solid_color = tex.SolidColor.rgb(0.9, 0.9, 0.9) }; 23 - 24 - const checker = try allocator.create(Material); 25 - checker.* = Material.initLambertian(tex.Texture{ .checker_texture = tex.CheckerTexture.init(0.32, c1, c2) }); 26 - 27 - try world.add(Hittable.initSphere("s1", Sphere.init(zm.f32x4(0, -10, 0, 0), 10, checker))); 28 - try world.add(Hittable.initSphere("s2", Sphere.init(zm.f32x4(0, 10, 0, 0), 10, checker))); 29 - 30 - return .{ .allocator = allocator, .world = world, .camera = Camera.Options{ 31 - .aspect_ratio = 16.0 / 9.0, 32 - .image_width = 400, 33 - .samples_per_pixel = 100, 34 - .max_depth = 50, 35 - 36 - .vfov = 20, 37 - .look_from = zm.f32x4(13, 2, 3, 0), 38 - .look_at = zm.f32x4(0, 0, 0, 0), 39 - 40 - .defocus_angle = 0, 41 - } }; 42 - } 43 - 44 - pub fn deinit(self: *@This()) void { 45 - self.world.deinit(); 46 - }
-93
src/scences/in_one_weekend.zig
··· 1 - const std = @import("std"); 2 - 3 - const rayray = @import("rayray"); 4 - const Camera = rayray.Camera; 5 - const Hittable = rayray.hittable.Hittable; 6 - const HittableList = rayray.hittable.HittableList; 7 - const Material = rayray.material.Material; 8 - const Sphere = rayray.hittable.Sphere; 9 - const zm = rayray.zmath; 10 - 11 - camera: Camera.Options, 12 - world: HittableList, 13 - allocator: std.mem.Allocator, 14 - 15 - pub fn scene(allocator: std.mem.Allocator) !@This() { 16 - var world = HittableList.init(allocator); 17 - 18 - const material_ground = try allocator.create(Material); 19 - material_ground.* = Material.initLambertianS(zm.f32x4(0.5, 0.5, 0.5, 1.0)); 20 - try world.add(Hittable.initSphere("Ground", Sphere.init(zm.f32x4(0, -1000, 0, 0), 1000, material_ground))); 21 - 22 - const a_max = 11; 23 - const b_max = 11; 24 - const c = 1; 25 - 26 - var a: isize = -a_max; 27 - while (a < a_max) : (a += 1) { 28 - var b: isize = -b_max; 29 - while (b < b_max) : (b += 1) { 30 - const choose_mat = rayray.util.randomF32(); 31 - const center = zm.f32x4( 32 - @as(f32, @floatFromInt(a)) / c + 0.9 * rayray.util.randomF32(), 33 - 0.2, 34 - @as(f32, @floatFromInt(b)) / c + 0.9 * rayray.util.randomF32(), 35 - 0, 36 - ); 37 - 38 - if (zm.length3(center - zm.f32x4(4, 0.2, 0, 0))[0] > 0.9) { 39 - const material = try allocator.create(Material); 40 - 41 - if (choose_mat < 0.8) { 42 - // diffuse 43 - const albedo = rayray.util.randomVec3() * rayray.util.randomVec3() + zm.f32x4(0, 0, 0, 1); 44 - material.* = Material.initLambertianS(albedo); 45 - const center2 = center + zm.f32x4(0, rayray.util.randomF32M(0, 0.5), 0, 0); 46 - try world.add(Hittable.initSphere("Lambertian", Sphere.initMoving(center, center2, 0.2, material))); 47 - } else if (choose_mat < 0.95) { 48 - // metal 49 - const albedo = rayray.util.randomVec3M(0.5, 1) + zm.f32x4(0, 0, 0, 1); 50 - const fuzz = rayray.util.randomF32M(0, 0.5); 51 - material.* = Material.initMetal(albedo, fuzz); 52 - try world.add(Hittable.initSphere("Metal", Sphere.init(center, 0.2, material))); 53 - } else { 54 - // glass 55 - material.* = Material.initDielectric(1.5); 56 - try world.add(Hittable.initSphere("Dielectric", Sphere.init(center, 0.2, material))); 57 - } 58 - } 59 - } 60 - } 61 - 62 - const material1 = try allocator.create(Material); 63 - material1.* = Material.initDielectric(1.5); 64 - try world.add(Hittable.initSphere("One: Dielectric", Sphere.init(zm.f32x4(0, 1, 0, 0), 1, material1))); 65 - 66 - const material2 = try allocator.create(Material); 67 - material2.* = Material.initLambertianS(zm.f32x4(0.4, 0.2, 0.1, 1)); 68 - try world.add(Hittable.initSphere("Two: Lambertian", Sphere.init(zm.f32x4(-4, 1, 0, 0), 1, material2))); 69 - 70 - const material3 = try allocator.create(Material); 71 - material3.* = Material.initMetal(zm.f32x4(0.7, 0.6, 0.5, 1), 0); 72 - try world.add(Hittable.initSphere("Three: Metal", Sphere.init(zm.f32x4(4, 1, 0, 0), 1, material3))); 73 - 74 - // try world.add(Hittable.sphere("One: Dielectric", Sphere.init(zm.f32x4(0, 1, 0, 0), 1, material2))); 75 - 76 - return .{ .allocator = allocator, .world = world, .camera = .{ 77 - .aspect_ratio = 16.0 / 9.0, 78 - .image_width = 1200, 79 - .samples_per_pixel = 500, 80 - .max_depth = 50, 81 - 82 - .vfov = 20, 83 - .look_from = zm.f32x4(13, 2, 3, 0), 84 - .look_at = zm.f32x4(0, 0, 0, 0), 85 - 86 - .defocus_angle = 0.6, 87 - .focus_dist = 10, 88 - } }; 89 - } 90 - 91 - pub fn deinit(self: *@This()) void { 92 - self.world.deinit(); 93 - }
+2
src/scenes.zig
··· 1 + pub const checker = @import("scenes/checker.zig").scene; 2 + pub const inOneWeekend = @import("scenes/in_one_weekend.zig").scene;
+44
src/scenes/checker.zig
··· 1 + const std = @import("std"); 2 + 3 + const rayray = @import("rayray"); 4 + const Camera = rayray.Camera; 5 + const Hittable = rayray.hittable.Hittable; 6 + const HittableList = rayray.hittable.HittableList; 7 + const material = rayray.material; 8 + const Material = material.Material; 9 + const Scene = rayray.Scene; 10 + const Sphere = rayray.hittable.Sphere; 11 + const tex = rayray.texture; 12 + const zm = rayray.zmath; 13 + 14 + camera: Camera.Options, 15 + world: HittableList, 16 + allocator: std.mem.Allocator, 17 + 18 + pub fn scene(s: *Scene) !void { 19 + const c1 = try s.createTexture(tex.SolidColor.rgb(0.2, 0.3, 0.1)); 20 + const c2 = try s.createTexture(tex.SolidColor.rgb(0.9, 0.9, 0.9)); 21 + const checker_tex = try s.createTexture(tex.CheckerTexture.init(0.32, c1, c2)); 22 + 23 + const checker = try s.createMaterial(checker_tex); 24 + 25 + try s.world.add(Hittable.initSphere(Sphere.init(zm.f32x4(0, -10, 0, 0), 10, checker))); 26 + try s.world.add(Hittable.initSphere(Sphere.init(zm.f32x4(0, 10, 0, 0), 10, checker))); 27 + 28 + try s.setCamera(Camera.Options{ 29 + .aspect_ratio = 16.0 / 9.0, 30 + .image_width = 400, 31 + .samples_per_pixel = 100, 32 + .max_depth = 50, 33 + 34 + .vfov = 20, 35 + .look_from = zm.f32x4(13, 2, 3, 0), 36 + .look_at = zm.f32x4(0, 0, 0, 0), 37 + 38 + .defocus_angle = 0, 39 + }); 40 + } 41 + 42 + pub fn deinit(self: *@This()) void { 43 + self.world.deinit(); 44 + }
+97
src/scenes/in_one_weekend.zig
··· 1 + const std = @import("std"); 2 + 3 + const rayray = @import("rayray"); 4 + const Camera = rayray.Camera; 5 + const Hittable = rayray.hittable.Hittable; 6 + const HittableList = rayray.hittable.HittableList; 7 + const mat = rayray.material; 8 + const Material = rayray.material.Material; 9 + const Scene = rayray.Scene; 10 + const Sphere = rayray.hittable.Sphere; 11 + const zm = rayray.zmath; 12 + 13 + camera: Camera.Options, 14 + world: HittableList, 15 + allocator: std.mem.Allocator, 16 + 17 + pub fn scene(s: *Scene) !void { 18 + // var world = HittableList.init(allocator); 19 + 20 + const material_ground = try s.createMaterial(zm.f32x4(0.5, 0.5, 0.5, 1.0)); 21 + // material_ground.* = Material.initLambertianS(); 22 + try s.world.add(Hittable.initSphere(Sphere.init(zm.f32x4(0, -1000, 0, 0), 1000, material_ground))); 23 + 24 + const a_max = 11; 25 + const b_max = 11; 26 + 27 + var a: isize = -a_max; 28 + while (a < a_max) : (a += 1) { 29 + var b: isize = -b_max; 30 + while (b < b_max) : (b += 1) { 31 + const choose_mat = rayray.util.randomF32(); 32 + const center = zm.f32x4( 33 + @as(f32, @floatFromInt(a)) + 0.9 * rayray.util.randomF32(), 34 + 0.2, 35 + @as(f32, @floatFromInt(b)) + 0.9 * rayray.util.randomF32(), 36 + 0, 37 + ); 38 + 39 + if (zm.length3(center - zm.f32x4(4, 0.2, 0, 0))[0] > 0.9) { 40 + // const material = try allocator.create(Material); 41 + 42 + if (choose_mat < 0.8) { 43 + // diffuse 44 + const albedo = rayray.util.randomVec3() * rayray.util.randomVec3() + zm.f32x4(0, 0, 0, 1); 45 + const material = try s.createMaterial(albedo); 46 + const center2 = center + zm.f32x4(0, rayray.util.randomF32M(0, 0.5), 0, 0); 47 + try s.world.add(Hittable.initSphere(Sphere.initMoving(center, center2, 0.2, material))); 48 + } else if (choose_mat < 0.95) { 49 + // metal 50 + const albedo = rayray.util.randomVec3M(0.5, 1) + zm.f32x4(0, 0, 0, 1); 51 + const fuzz = rayray.util.randomF32M(0, 0.5); 52 + const material = try s.createMaterial(mat.Metal.init(albedo, fuzz)); 53 + try s.world.add(Hittable.initSphere(Sphere.init(center, 0.2, material))); 54 + } else { 55 + // glass 56 + const material = try s.createMaterial(mat.Dielectric{ .refraction_index = 1.5 }); 57 + try s.world.add(Hittable.initSphere(Sphere.init(center, 0.2, material))); 58 + } 59 + } 60 + } 61 + } 62 + 63 + // const material1 = try allocator.create(Material); 64 + // material1.* = Material.initDielectric(1.5); 65 + const material1 = try s.createMaterial(mat.Dielectric{ .refraction_index = 1.5 }); 66 + try s.world.add(Hittable.initSphere(Sphere.init(zm.f32x4(0, 1, 0, 0), 1, material1))); 67 + 68 + // const material2 = try allocator.create(Material); 69 + // material2.* = Material.initLambertianS(zm.f32x4(0.4, 0.2, 0.1, 1)); 70 + const material2 = try s.createMaterial(zm.f32x4(0.4, 0.2, 0.1, 1)); 71 + try s.world.add(Hittable.initSphere(Sphere.init(zm.f32x4(-4, 1, 0, 0), 1, material2))); 72 + 73 + // const material3 = try allocator.create(Material); 74 + // material3.* = Material.initMetal(zm.f32x4(0.7, 0.6, 0.5, 1), 0); 75 + const material3 = try s.createMaterial(mat.Metal.init(zm.f32x4(0.7, 0.6, 0.5, 1), 0)); 76 + try s.world.add(Hittable.initSphere(Sphere.init(zm.f32x4(4, 1, 0, 0), 1, material3))); 77 + 78 + // try world.add(Hittable.sphere("One: Dielectric", Sphere.init(zm.f32x4(0, 1, 0, 0), 1, material2))); 79 + 80 + try s.setCamera(.{ 81 + .aspect_ratio = 16.0 / 9.0, 82 + .image_width = 600, 83 + .samples_per_pixel = 50, 84 + .max_depth = 50, 85 + 86 + .vfov = 20, 87 + .look_from = zm.f32x4(13, 2, 3, 0), 88 + .look_at = zm.f32x4(0, 0, 0, 0), 89 + 90 + .defocus_angle = 0.6, 91 + .focus_dist = 10, 92 + }); 93 + } 94 + 95 + pub fn deinit(self: *@This()) void { 96 + self.world.deinit(); 97 + }
+14
src/texture.zig
··· 1 + const std = @import("std"); 1 2 const zm = @import("zmath"); 2 3 3 4 pub const Texture = union(enum) { 4 5 solid_color: SolidColor, 5 6 checker_texture: CheckerTexture, 7 + 8 + pub fn init(alloc: std.mem.Allocator, data: anytype) !*Texture { 9 + const tex = try alloc.create(Texture); 10 + 11 + switch (@TypeOf(data)) { 12 + SolidColor => tex.* = .{ .solid_color = data }, 13 + CheckerTexture => tex.* = .{ .checker_texture = data }, 14 + 15 + else => @panic("Cannot infer Texture type of: " ++ @typeName(@TypeOf(data))), 16 + } 17 + 18 + return tex; 19 + } 6 20 7 21 pub fn value(self: *Texture, u: f32, v: f32, p: zm.Vec) zm.Vec { 8 22 switch (self.*) {
+20 -10
src/tracer.zig
··· 18 18 const log = std.log.scoped(.tracer); 19 19 20 20 pub const Context = struct { 21 + pixels: []zm.Vec, 21 22 cam: *Camera, 22 23 world: *BVH, 23 24 height: IntervalUsize, ··· 29 30 30 31 pub fn rayColor(r: *Ray, world: *BVH, depth: usize) zm.Vec { 31 32 @setFloatMode(.optimized); 32 - if (depth == 0) return black; 33 + if (depth == 0) return backgroundColor(r); 33 34 34 35 if (world.hit(r, .{ .min = 0.001, .max = std.math.inf(f32) })) |rec| { 35 - var attenuation = white; 36 - if (rec.mat.scatter(r, @constCast(&rec), &attenuation)) |new_r| { 36 + var attenuation = zm.f32x4s(1.0); 37 + if (rec.mat.scatter(r, &rec, &attenuation)) |new_r| { 37 38 return attenuation * rayColor(@constCast(&new_r), world, depth - 1); 38 39 } 40 + } 39 41 40 - return black; 41 - } 42 + return backgroundColor(r); 43 + } 42 44 45 + fn backgroundColor(r: *Ray) zm.Vec { 43 46 const unit_direction = zm.normalize3(r.dir); 44 47 const a = 0.5 * (unit_direction[1] + 1.0); 45 48 return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0); ··· 51 54 if (j >= ctx.cam.image_height) break; 52 55 53 56 var width_iter = ctx.width.iter(); 54 - while (width_iter.nextExc()) |i| { 57 + while (width_iter.nextExc()) |i| inner: { 55 58 var col = zm.f32x4(0.0, 0.0, 0.0, 1.0); 56 59 57 60 for (0..ctx.cam.samples_per_pixel) |_| { ··· 59 62 col += rayColor(&ray, ctx.world, ctx.cam.max_depth); 60 63 } 61 64 62 - ctx.cam.setPixel(i, j, vecToRgba( 63 - col, 64 - ctx.cam.samples_per_pixel_v, 65 - )) catch break; 65 + setPixel(ctx.pixels, ctx.cam, i, j, col) catch { 66 + log.err("Trying to set a pixel out of bounds ({}, {})", .{ i, j }); 67 + break :inner; 68 + }; 66 69 } 67 70 } 68 71 } ··· 85 88 @intFromFloat(rgba[3]), 86 89 ); 87 90 } 91 + 92 + pub fn setPixel(pixels: []zm.Vec, cam: *Camera, x: usize, y: usize, c: zm.Vec) !void { 93 + if (x >= cam.image_width or y >= cam.image_height) return error.OutOfBounds; 94 + const i = x + cam.image_width * y; 95 + pixels[i] = c; 96 + try cam.setPixel(x, y, vecToRgba(c, cam.samples_per_pixel_v)); 97 + }