Skip to content

Instantly share code, notes, and snippets.

@jesterKing
Last active July 12, 2024 07:33
Show Gist options
  • Select an option

  • Save jesterKing/82008d04287c06e64972469caeefeef9 to your computer and use it in GitHub Desktop.

Select an option

Save jesterKing/82008d04287c06e64972469caeefeef9 to your computer and use it in GitHub Desktop.

Revisions

  1. jesterKing revised this gist Jul 12, 2024. 1 changed file with 17 additions and 17 deletions.
    34 changes: 17 additions & 17 deletions raytrace.zig
    Original file line number Diff line number Diff line change
    @@ -199,23 +199,6 @@ const HittableList = struct {
    }
    };

    const Hittable = union(enum) {
    sphere: *Sphere,
    hittable_list: *HittableList,
    pub fn hit(self: Hittable, r: *Ray, ray_t: Interval, rec: *HitRecord) bool {
    return switch(self) {
    inline else => |s| s.hit(r, ray_t, rec),
    };
    }

    pub fn append(self: Hittable, object: Hittable) !void {
    switch(self) {
    .hittable_list => |l| try l.append(object),
    else => return,
    }
    }
    };

    const Sphere = struct {
    center: Point3,
    radius: f64,
    @@ -255,6 +238,23 @@ const Sphere = struct {
    }
    };

    const Hittable = union(enum) {
    sphere: *Sphere,
    hittable_list: *HittableList,
    pub fn hit(self: Hittable, r: *Ray, ray_t: Interval, rec: *HitRecord) bool {
    return switch(self) {
    inline else => |s| s.hit(r, ray_t, rec),
    };
    }

    pub fn append(self: Hittable, object: Hittable) !void {
    switch(self) {
    .hittable_list => |l| try l.append(object),
    else => return,
    }
    }
    };

    const Color = Vec3;
    const Point3 = Vec3;
    const Vec3 = struct {
  2. jesterKing revised this gist Jul 12, 2024. 1 changed file with 16 additions and 0 deletions.
    16 changes: 16 additions & 0 deletions build.zig
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    const std = @import("std");

    pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
    .name = "raytrace",
    .target = target,
    .optimize = optimize,
    .root_source_file = b.path("raytrace.zig"),
    });

    b.installArtifact(exe);
    }

  3. jesterKing renamed this gist Jul 12, 2024. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. jesterKing created this gist Jul 12, 2024.
    343 changes: 343 additions & 0 deletions raytrace.ppm
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,343 @@
    const std = @import("std");

    // set logging level to info.
    pub const std_options = .{
    .log_level = .info,
    .logFn = myLogFn,
    };

    const infinity = std.math.inf(f64);

    // log function that writes to stderr
    pub fn myLogFn(
    comptime _: std.log.Level,
    comptime _: @TypeOf(.EnumLiteral),
    comptime format: []const u8,
    args: anytype,
    ) void {
    std.debug.lockStdErr();
    defer std.debug.unlockStdErr();
    const stderr = std.io.getStdErr().writer();
    nosuspend stderr.print(format, args) catch return;
    }
    const stdout = std.io.getStdOut().writer();

    pub fn main() !void {
    var _world = HittableList.init();
    defer _world.deinit();

    var world = Hittable{.hittable_list = &_world};

    const aspect_ratio : f64 = 16.0 / 9.0;
    const image_width : usize = 400;
    var image_height = @as(usize, @intFromFloat(image_width / aspect_ratio));
    image_height = if (image_height < 1) 1 else image_height;

    const viewport_height = 2.0;
    const viewport_width = viewport_height * (@as(f64, @floatFromInt(image_width)))/(@as(f64, @floatFromInt(image_height)));

    const focal_length = 1.0;
    const camera_center = Point3.initZero();

    const viewport_u = Vec3.init(viewport_width, 0.0, 0.0);
    const viewport_v = Vec3.init(0.0, -viewport_height, 0.0);

    const pixel_delta_u = viewport_u.divide(usize, image_width);
    const pixel_delta_v = viewport_v.divide(usize, image_height);

    const viewport_upper_left =
    camera_center
    .subtract(Vec3.init(0, 0, focal_length))
    .subtract(viewport_u.divide(i32, 2))
    .subtract(viewport_v.divide(i32, 2));

    const pixel00_loc =
    viewport_upper_left
    .add(
    pixel_delta_u
    .add(pixel_delta_v)
    .multiply(f64, 0.5)
    );

    var sphere1 = Sphere.init(Point3.init(0, 0, -1), 0.5);
    var sphere2 = Sphere.init(Point3.init(0, -100.5, -1), 100.0);
    const a1 = Hittable{.sphere = &sphere1 };
    const a2 = Hittable{.sphere = &sphere2 };

    try world.append(a1);
    try world.append(a2);

    const ppm_writer_log = std.log.scoped(.ppm_write);

    ppm_writer_log.info("Starting to write PPM image ...\n\n", .{});

    try stdout.print("P3\n{} {}\n255\n", .{ image_width, image_height });

    for (0..image_height) |h| {
    ppm_writer_log.info("\rScanlines remaining: {}", .{image_height - h});
    for (0..image_width) |w| {
    const pixel_center = pixel00_loc.add(pixel_delta_u.multiply(usize, w)).add(pixel_delta_v.multiply(usize, h));
    const ray_direction = pixel_center.subtract(camera_center);
    var ray = Ray{
    .origin = camera_center,
    .direction = ray_direction,
    };
    const col = ray_color(&ray, &world);
    writeColor(col);
    }
    try stdout.print("\n", .{});
    }

    ppm_writer_log.info("\rDone. \n", .{});
    }

    fn ray_color(r: *Ray, world: *Hittable) Color {
    var hit_record = HitRecord{};
    if(world.hit(r, Interval.init(0.0, infinity), &hit_record)) {
    const N = hit_record.n;
    const col = Color
    .init(N.x + 1, N.y + 1, N.z + 1)
    .multiply(f64, 0.5);
    return col;
    }

    const unit_direction = r.direction.unitized();
    const a :f64 = 0.5 * (unit_direction.y + 1.0);
    const blue = Color.init(0.2, 0.4, 0.7);
    const white = Color.init(1.0, 1.0, 1.0);
    return white
    .multiply(f64, 1.0 - a)
    .add(
    blue
    .multiply(f64, a)
    );
    }

    fn writeColor(color: Color) void {
    const ir = @as(u8, @intFromFloat(255.999 * color.x));
    const ig = @as(u8, @intFromFloat(255.999 * color.y));
    const ib = @as(u8, @intFromFloat(255.999 * color.z));

    nosuspend stdout.print("{} {} {} ", .{ ir, ig, ib }) catch return;
    }

    const Interval = struct {
    min: f64,
    max: f64,

    pub fn initInfinity() Interval {
    return Interval{
    .min = -infinity,
    .max = infinity,
    };
    }

    pub fn init(min: f64, max: f64) Interval {
    return Interval{
    .min = min,
    .max = max,
    };
    }

    pub fn contains(self: Interval, t: f64) bool {
    return (self.min <= t) and (t <= self.max);
    }

    pub fn surrounds(self: Interval, x: f64) bool {
    return (self.min < x) and (x < self.max);
    }
    };

    const empty = Interval.init(infinity, -infinity);
    const universe = Interval.initInfinity();

    const HitRecord = struct {
    p: Point3 = Point3.initZero(),
    n: Vec3 = Vec3.initZero(),
    t: f64 = 0.0,
    f: bool = false,

    pub fn set_face_normal(self: *HitRecord, r: *Ray, outward_normal: Vec3) void {
    self.f = r.direction.dot(outward_normal) < 0;
    self.n = if(self.f) outward_normal else outward_normal.neg();
    }
    };

    const HittableList = struct {
    objects: std.ArrayList(Hittable),

    pub fn init() HittableList {
    return HittableList{
    .objects = std.ArrayList(Hittable).init(std.heap.page_allocator),
    };
    }

    pub fn deinit(self: *HittableList) void {
    self.objects.deinit();
    }

    pub fn append(self: *HittableList, object: Hittable) !void {
    try self.objects.append(object);
    }

    pub fn hit(self: HittableList, r: *Ray, ray_t: Interval, rec: *HitRecord) bool {
    var temp_rec = HitRecord{};
    var hit_anything = false;
    var closest_so_far = ray_t.max;

    for(self.objects.items) |object| {
    if(object.hit(r, Interval.init(ray_t.min, closest_so_far), &temp_rec)) {
    hit_anything = true;
    closest_so_far = temp_rec.t;
    rec.t = temp_rec.t;
    rec.p = temp_rec.p;
    rec.n = temp_rec.n;
    rec.f = temp_rec.f;
    }
    }
    return hit_anything;
    }
    };

    const Hittable = union(enum) {
    sphere: *Sphere,
    hittable_list: *HittableList,
    pub fn hit(self: Hittable, r: *Ray, ray_t: Interval, rec: *HitRecord) bool {
    return switch(self) {
    inline else => |s| s.hit(r, ray_t, rec),
    };
    }

    pub fn append(self: Hittable, object: Hittable) !void {
    switch(self) {
    .hittable_list => |l| try l.append(object),
    else => return,
    }
    }
    };

    const Sphere = struct {
    center: Point3,
    radius: f64,

    pub fn init(center: Point3, radius: f64) Sphere {
    return Sphere{
    .center = center,
    .radius = @max(0, radius),
    };
    }

    pub fn hit(self: *Sphere, r: *Ray, ray_t: Interval, rec: *HitRecord) bool {
    const oc = self.center.subtract(r.origin);
    const a = r.direction.length_squared();
    const h = r.direction.dot(oc);
    const c = oc.length_squared() - self.radius * self.radius;
    const discriminant = h * h - a * c;

    if(discriminant < 0) {
    return false;
    }

    const sqrtd = std.math.sqrt(discriminant);
    var root = (h - sqrtd) / a;
    if(!ray_t.surrounds(root)) {
    root = (h + sqrtd) / a;
    if(!ray_t.surrounds(root)) {
    return false;
    }
    }
    rec.t = root;
    rec.p = r.at(rec.t);
    const outward_normal = rec.p.subtract(self.center).divide(f64, self.radius).unitized();
    rec.set_face_normal(r, outward_normal);

    return true;
    }
    };

    const Color = Vec3;
    const Point3 = Vec3;
    const Vec3 = struct {
    x: f64 = 0.0,
    y: f64 = 0.0,
    z: f64 = 0.0,

    pub fn initZero() Vec3 {
    return Vec3{};
    }
    pub fn init(e0: f64, e1: f64, e2: f64) Vec3 {
    return Vec3{
    .x = e0,
    .y = e1,
    .z = e2,
    };
    }

    pub inline fn neg(self: Vec3) Vec3 {
    return Vec3{
    .x = -self.x,
    .y = -self.y,
    .z = -self.z,
    };
    }

    pub inline fn add(self: Vec3, other: Vec3) Vec3 {
    return Vec3{
    .x = self.x + other.x,
    .y = self.y + other.y,
    .z = self.z + other.z,
    };
    }

    pub inline fn subtract(self: Vec3, other: Vec3) Vec3 {
    return self.add(other.neg());
    }

    pub inline fn multiply(self: Vec3, comptime T: type, t: T)Vec3 {
    const _t = if (@TypeOf(t) == f64 ) t else @as(f64, @floatFromInt(t));
    return Vec3{
    .x = self.x * _t,
    .y = self.y * _t,
    .z = self.z * _t,
    };
    }

    pub inline fn divide(self: Vec3, comptime T: type, t : T) Vec3 {
    const _t = if (@TypeOf(t) == f64) t else @as(f64, @floatFromInt(t));
    return self.multiply(f64, 1.0 / _t);
    }

    pub inline fn length(self: Vec3) f64 {
    return std.math.sqrt(self.length_squared());
    }

    pub inline fn length_squared(self: Vec3) f64 {
    return self.x * self.x + self.y * self.y + self.z * self.z;
    }

    pub inline fn dot(self: Vec3, other: Vec3) f64 {
    return self.x * other.x + self.y * other.y + self.z * other.z;
    }

    pub inline fn cross(self: Vec3, other: Vec3) Vec3 {
    return Vec3.init(
    self.y * other.z - self.z * other.y,
    self.z * other.x - self.x * other.z,
    self.x * other.y - self.y * other.x,
    );
    }

    pub inline fn unitized(self: Vec3) Vec3 {
    return self.divide(f64, self.length());
    }
    };

    const Ray = struct {
    origin: Point3 = Point3{},
    direction: Vec3 = Vec3{},

    pub fn at(self: Ray, t: f64) Point3 {
    return self.origin.add(self.direction.multiply(f64, t));
    }
    };