From e31f5e0a3afca22463f68141c22db1e46891ed31 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Thu, 4 Aug 2022 21:05:52 -0700 Subject: [PATCH] zigrtiow: add camera class and support supersampling. --- zigrtiow/src/camera.zig | 33 +++++++++++++++++++++++++++++++++ zigrtiow/src/color.zig | 22 ++++++++++++++++++---- zigrtiow/src/main.zig | 30 +++++++++++++++++------------- zigrtiow/src/utils.zig | 5 +++++ 4 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 zigrtiow/src/camera.zig create mode 100644 zigrtiow/src/utils.zig diff --git a/zigrtiow/src/camera.zig b/zigrtiow/src/camera.zig new file mode 100644 index 0000000..f7d2170 --- /dev/null +++ b/zigrtiow/src/camera.zig @@ -0,0 +1,33 @@ +const vec = @import("./vec.zig"); + +const Ray = @import("./ray.zig").Ray; + +const Point3 = vec.Point3; +const Vec3 = vec.Vec3; + +pub const Camera = struct { + origin: Point3, + lower_left_corner: Point3, + horizontal: Vec3, + vertical: Vec3, + + pub fn init() Camera { + const aspect_ratio = 16.0 / 9.0; + const viewport_height = 2.0; + const viewport_width = aspect_ratio * viewport_height; + const focal_length = 1.0; + const origin = Point3.init(0, 0, 0); + const horizontal = Vec3.init(viewport_width, 0.0, 0.0); + const vertical = Vec3.init(0.0, viewport_height, 0.0); + + return Camera{ + .origin = origin, + .horizontal = horizontal, + .vertical = vertical, + .lower_left_corner = origin.sub(horizontal.scale(0.5)).sub(vertical.scale(0.5)).sub(Vec3.init(0, 0, focal_length)), + }; + } + pub fn get_ray(camera: Camera, u: f32, v: f32) Ray { + return Ray.init(camera.origin, camera.lower_left_corner.add(camera.horizontal.scale(u).add(camera.vertical.scale(v).sub(camera.origin)))); + } +}; diff --git a/zigrtiow/src/color.zig b/zigrtiow/src/color.zig index 717c23d..4adca61 100644 --- a/zigrtiow/src/color.zig +++ b/zigrtiow/src/color.zig @@ -1,10 +1,24 @@ const std = @import("std"); + +const clamp = @import("./utils.zig").clamp; const Color = @import("./vec.zig").Color; -pub fn write_color(c: Color) anyerror!void { - var ir = @floatToInt(u8, 255.999 * c.x()); - var ig = @floatToInt(u8, 255.999 * c.y()); - var ib = @floatToInt(u8, 255.999 * c.z()); +pub fn write_color(c: Color, samples_per_pixel: isize) anyerror!void { + var r = c.x(); + var g = c.y(); + var b = c.z(); + + // Divide the color by the number of samples. + var scale = 1.0 / @intToFloat(f32, samples_per_pixel); + r *= scale; + g *= scale; + b *= scale; + + // Write the translated [0,255] value of each color component. + var ir = @floatToInt(u8, 256 * clamp(r, 0, 0.999)); + var ig = @floatToInt(u8, 256 * clamp(g, 0, 0.999)); + var ib = @floatToInt(u8, 256 * clamp(b, 0, 0.999)); + const stdout = std.io.getStdOut(); try stdout.writer().print("{d} {d} {d}\n", .{ ir, ig, ib }); } diff --git a/zigrtiow/src/main.zig b/zigrtiow/src/main.zig index 72dace7..257abac 100644 --- a/zigrtiow/src/main.zig +++ b/zigrtiow/src/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const camera = @import("./camera.zig"); const color = @import("./color.zig"); const ray = @import("./ray.zig"); const vec = @import("./vec.zig"); @@ -6,6 +7,7 @@ const sphere = @import("./sphere.zig"); const hittable = @import("./hittable.zig"); const hittable_list = @import("./hittable_list.zig"); +const Camera = camera.Camera; const Color = vec.Color; const Hittable = hittable.Hittable; const HittableList = hittable_list.HittableList; @@ -36,6 +38,7 @@ pub fn main() anyerror!void { const aspect_ratio: f32 = 16.0 / 9.0; const image_width = 400; const image_height = @floatToInt(isize, @intToFloat(f32, image_width) / aspect_ratio); + const samples_per_pixel = 100; // World var world = HittableList.init(); @@ -43,31 +46,32 @@ pub fn main() anyerror!void { try world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, -100.5, -1), 100) }); // Camera - const viewport_height = 2; - const viewport_width = aspect_ratio * viewport_height; - const focal_length = 1; - - const origin = Point3.init(0, 0, 0); - const horizontal = Vec3.init(viewport_width, 0, 0); - const vertical = Vec3.init(0, viewport_height, 0); - const lower_left_corner = origin.sub(horizontal.scale(0.5)).sub(vertical.scale(0.5)).sub(Vec3.init(0, 0, focal_length)); + const cam = Camera.init(); + // Render const stdout = std.io.getStdOut(); info("Writing image {d}x{d}", .{ image_width, image_height }); try stdout.writer().print("P3\n{d} {d}\n255\n", .{ image_width, image_height }); + var prng = std.rand.DefaultPrng.init(0); + const rand = prng.random(); var j: isize = image_height - 1; while (j >= 0) : (j -= 1) { //info("Scanlines remaining: {d}", .{j}); var i: isize = 0; while (i < image_width) : (i += 1) { - const u = @intToFloat(f32, i) / @intToFloat(f32, image_width - 1); - const v = @intToFloat(f32, j) / @intToFloat(f32, image_height - 1); - const r = Ray.init(origin, lower_left_corner.add(horizontal.scale(u)).add(vertical.scale(v)).sub(origin)); - const pixel_color = ray_color(r, Hittable{ .hittable_list = world }); - try write_color(pixel_color); + var pixel_color = Color.init(0, 0, 0); + var s: isize = 0; + while (s < samples_per_pixel) : (s += 1) { + const u = (@intToFloat(f32, i) + rand.float(f32)) / @intToFloat(f32, image_width - 1); + const v = (@intToFloat(f32, j) + rand.float(f32)) / @intToFloat(f32, image_height - 1); + const r = cam.get_ray(u, v); + //Ray.init(origin, lower_left_corner.add(horizontal.scale(u)).add(vertical.scale(v)).sub(origin)); + pixel_color = pixel_color.add(ray_color(r, Hittable{ .hittable_list = world })); + } + try write_color(pixel_color, samples_per_pixel); } } info("Done", .{}); diff --git a/zigrtiow/src/utils.zig b/zigrtiow/src/utils.zig new file mode 100644 index 0000000..5d398ca --- /dev/null +++ b/zigrtiow/src/utils.zig @@ -0,0 +1,5 @@ +pub fn clamp(x: f32, min: f32, max: f32) f32 { + if (x < min) return min; + if (x > max) return max; + return x; +}