diff --git a/src/rayray.zig b/src/rayray.zig --- a/src/rayray.zig +++ b/src/rayray.zig @@ -7,7 +7,7 @@ pub const Camera = @import("camera.zig"); pub const hittable = @import("hittable.zig"); pub const material = @import("material.zig"); -pub const renderer = @import("renderer.zig"); +pub const tracer = @import("tracer.zig"); const IntervalUsize = @import("a").interval.IntervalUsize; @@ -60,7 +60,7 @@ defer self.allocator.free(threads); for (0..num_threads) |row| { - const ctx = renderer.Context{ .cam = &self.camera, .world = &self.world }; + const ctx = tracer.Context{ .cam = &self.camera, .world = &self.world }; const t = try std.Thread.spawn( .{}, renderThread, @@ -108,7 +108,7 @@ } }; -pub fn renderThread(ctx: renderer.Context, done: *std.atomic.Value(bool), row: usize, row_height: usize) void { +pub fn renderThread(ctx: tracer.Context, done: *std.atomic.Value(bool), row: usize, row_height: usize) void { spall.init_thread(); defer spall.deinit_thread(); @@ -120,7 +120,7 @@ const s = spall.trace(@src(), "Render Thread {}", .{row}); defer s.end(); - renderer.run(ctx, height, width); + tracer.trace(ctx, height, width); done.store(true, .Release); } diff --git a/src/renderer.zig b/src/renderer.zig deleted file mode 100644 --- a/src/renderer.zig +++ /dev/null @@ -1,78 +0,0 @@ -const std = @import("std"); - -const spall = @import("spall"); -const zigimg = @import("zigimg"); -const zm = @import("zmath"); - -const Camera = @import("camera.zig"); -const hittable = @import("hittable.zig"); -const material = @import("material.zig"); -const Ray = @import("ray.zig"); -const util = @import("util.zig"); - -const interval = @import("a").interval; -const IntervalUsize = interval.IntervalUsize; -const IntervalF32 = interval.IntervalF32; - -const log = std.log.scoped(.renderer); - -pub const Context = struct { - cam: *Camera, - world: *hittable.HittableList, -}; - -pub fn rayColor(r: *Ray, world: *hittable.HittableList, depth: usize) zm.Vec { - if (depth <= 0) return zm.f32x4(0, 0, 0, 1.0); - - if (world.hit(r, .{ .min = 0.001, .max = std.math.inf(f32) })) |rec| { - var attenuation = zm.f32x4s(1.0); - if (rec.mat.scatter(r, @constCast(&rec), &attenuation)) |new_r| { - return attenuation * rayColor(@constCast(&new_r), world, depth - 1); - } - - return zm.f32x4(0, 0, 0, 1.0); - } - - const unit_direction = zm.normalize3(r.dir); - const a = 0.5 * (unit_direction[1] + 1.0); - return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0); -} - -pub fn run(ctx: Context, height: IntervalUsize, width: IntervalUsize) void { - var height_iter = height.iter(); - while (height_iter.nextInc()) |j| { - if (j >= ctx.cam.image_height) break; - - var width_iter = width.iter(); - while (width_iter.nextExc()) |i| { - var col = zm.f32x4(0.0, 0.0, 0.0, 1.0); - for (0..ctx.cam.samples_per_pixel) |_| { - var ray = ctx.cam.getRay(i, j); - col += rayColor(&ray, ctx.world, ctx.cam.max_depth); - } - - ctx.cam.setPixel(i, j, vecToRgba(col, ctx.cam.samples_per_pixel)) catch break; - } - } -} - -fn vecToRgba(v: zm.Vec, samples_per_pixel: usize) zigimg.color.Rgba32 { - const scale: f32 = 1.0 / @as(f32, @floatFromInt(samples_per_pixel)); - const intensity = IntervalF32.init(0.0, 0.999); - - const r_scaled = linearToGamma(v[0] * scale); - const g_scaled = linearToGamma(v[1] * scale); - const b_scaled = linearToGamma(v[2] * scale); - const a_scaled = linearToGamma(v[3] * scale); - - const r: u8 = @intFromFloat(256 * intensity.clamp(r_scaled)); - const g: u8 = @intFromFloat(256 * intensity.clamp(g_scaled)); - const b: u8 = @intFromFloat(256 * intensity.clamp(b_scaled)); - const a: u8 = @intFromFloat(256 * intensity.clamp(a_scaled)); - - return zigimg.color.Rgba32.initRgba(r, g, b, a); -} - -inline fn linearToGamma(linear_component: f32) f32 { - return @sqrt(linear_component); -} diff --git a/src/tracer.zig b/src/tracer.zig new file mode 100644 --- /dev/null +++ b/src/tracer.zig @@ -0,0 +1,78 @@ +const std = @import("std"); + +const spall = @import("spall"); +const zigimg = @import("zigimg"); +const zm = @import("zmath"); + +const Camera = @import("camera.zig"); +const hittable = @import("hittable.zig"); +const material = @import("material.zig"); +const Ray = @import("ray.zig"); +const util = @import("util.zig"); + +const interval = @import("a").interval; +const IntervalUsize = interval.IntervalUsize; +const IntervalF32 = interval.IntervalF32; + +const log = std.log.scoped(.tracer); + +pub const Context = struct { + cam: *Camera, + world: *hittable.HittableList, +}; + +pub fn rayColor(r: *Ray, world: *hittable.HittableList, depth: usize) zm.Vec { + if (depth <= 0) return zm.f32x4(0, 0, 0, 1.0); + + if (world.hit(r, .{ .min = 0.001, .max = std.math.inf(f32) })) |rec| { + var attenuation = zm.f32x4s(1.0); + if (rec.mat.scatter(r, @constCast(&rec), &attenuation)) |new_r| { + return attenuation * rayColor(@constCast(&new_r), world, depth - 1); + } + + return zm.f32x4(0, 0, 0, 1.0); + } + + const unit_direction = zm.normalize3(r.dir); + const a = 0.5 * (unit_direction[1] + 1.0); + return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0); +} + +pub fn trace(ctx: Context, height: IntervalUsize, width: IntervalUsize) void { + var height_iter = height.iter(); + while (height_iter.nextInc()) |j| { + if (j >= ctx.cam.image_height) break; + + var width_iter = width.iter(); + while (width_iter.nextExc()) |i| { + var col = zm.f32x4(0.0, 0.0, 0.0, 1.0); + for (0..ctx.cam.samples_per_pixel) |_| { + var ray = ctx.cam.getRay(i, j); + col += rayColor(&ray, ctx.world, ctx.cam.max_depth); + } + + ctx.cam.setPixel(i, j, vecToRgba(col, ctx.cam.samples_per_pixel)) catch break; + } + } +} + +fn vecToRgba(v: zm.Vec, samples_per_pixel: usize) zigimg.color.Rgba32 { + const scale: f32 = 1.0 / @as(f32, @floatFromInt(samples_per_pixel)); + const intensity = IntervalF32.init(0.0, 0.999); + + const r_scaled = linearToGamma(v[0] * scale); + const g_scaled = linearToGamma(v[1] * scale); + const b_scaled = linearToGamma(v[2] * scale); + const a_scaled = linearToGamma(v[3] * scale); + + const r: u8 = @intFromFloat(256 * intensity.clamp(r_scaled)); + const g: u8 = @intFromFloat(256 * intensity.clamp(g_scaled)); + const b: u8 = @intFromFloat(256 * intensity.clamp(b_scaled)); + const a: u8 = @intFromFloat(256 * intensity.clamp(a_scaled)); + + return zigimg.color.Rgba32.initRgba(r, g, b, a); +} + +inline fn linearToGamma(linear_component: f32) f32 { + return @sqrt(linear_component); +}