zigrtiow: use Sphere, Hittable, and HittableList abstractions.

This commit is contained in:
Bill Thiede 2022-08-04 20:13:31 -07:00
parent 5043a7e526
commit 84a0ba2ec6
4 changed files with 141 additions and 17 deletions

36
zigrtiow/src/hittable.zig Normal file
View File

@ -0,0 +1,36 @@
const vec = @import("./vec.zig");
const ray = @import("./ray.zig");
const Sphere = @import("./sphere.zig").Sphere;
const HittableList = @import("./hittable_list.zig").HittableList;
const Vec3 = vec.Vec3;
const Point3 = vec.Point3;
const Ray = ray.Ray;
pub const Hittable = union(enum) {
sphere: Sphere,
hittable_list: HittableList,
pub fn hit(hittable: Hittable, r: Ray, t_min: f32, t_max: f32) ?HitRecord {
return switch (hittable) {
.sphere => |sphere| sphere.hit(r, t_min, t_max),
.hittable_list => |hittable_list| hittable_list.hit(r, t_min, t_max),
};
}
};
pub const HitRecord = struct {
p: Point3,
normal: Vec3,
t: f32,
front_face: bool,
pub fn set_face_normal(hr: *HitRecord, r: Ray, outward_normal: Vec3) void {
hr.front_face = r.direction().dot(outward_normal) < 0;
hr.normal = if (hr.front_face)
outward_normal
else
outward_normal.scale(-1);
}
};

View File

@ -0,0 +1,35 @@
const std = @import("std");
const ray = @import("./ray.zig");
const hittable = @import("./hittable.zig");
const info = std.log.info;
const ArrayList = std.ArrayList;
const Ray = ray.Ray;
const HitRecord = hittable.HitRecord;
const Hittable = hittable.Hittable;
pub const HittableList = struct {
objects: ArrayList(Hittable),
pub fn init() HittableList {
const allocator = std.heap.page_allocator;
return HittableList{
.objects = ArrayList(Hittable).init(allocator),
};
}
pub fn hit(hittable_list: HittableList, r: Ray, t_min: f32, t_max: f32) ?HitRecord {
var temp_rec: ?HitRecord = null;
var closest_so_far = t_max;
for (hittable_list.objects.items) |object| {
if (object.hit(r, t_min, closest_so_far)) |rec| {
temp_rec = rec;
closest_so_far = rec.t;
}
}
return temp_rec;
}
pub fn add(hittable_list: *HittableList, object: Hittable) anyerror!void {
try hittable_list.objects.append(object);
}
};

View File

@ -2,27 +2,24 @@ const std = @import("std");
const color = @import("./color.zig");
const ray = @import("./ray.zig");
const vec = @import("./vec.zig");
const sphere = @import("./sphere.zig");
const hittable = @import("./hittable.zig");
const hittable_list = @import("./hittable_list.zig");
const Color = vec.Color;
const Vec3 = vec.Vec3;
const Hittable = hittable.Hittable;
const HittableList = hittable_list.HittableList;
const Point3 = vec.Point3;
const Ray = ray.Ray;
const Sphere = sphere.Sphere;
const Vec3 = vec.Vec3;
const info = std.log.info;
const write_color = color.write_color;
const Ray = ray.Ray;
fn hit_sphere(center: Point3, radius: f32, r: Ray) f32 {
const oc = r.origin().sub(center);
const a = r.direction().length_squared();
const half_b = Vec3.dot(oc, r.direction());
const c = oc.length_squared() - radius * radius;
const discriminant = half_b * half_b - a * c;
return if (discriminant < 0) -1 else (-half_b - @sqrt(discriminant)) / a;
}
fn ray_color(r: Ray) Color {
var t = hit_sphere(Point3.init(0, 0, -1), 0.5, r);
if (t > 0) {
const n = r.at(t).sub(Vec3.init(0, 0, -1)).unit();
fn ray_color(r: Ray, world: Hittable) Color {
var hit = world.hit(r, 0, std.math.inf(f32));
if (hit) |rec| {
const n = rec.normal;
return Color.init(
n.x() + 1,
n.y() + 1,
@ -30,7 +27,7 @@ fn ray_color(r: Ray) Color {
).scale(0.5);
}
var unit_direction = r.direction().unit();
t = 0.5 * (unit_direction.y() + 1);
const t = 0.5 * (unit_direction.y() + 1);
return Color.init(1, 1, 1).scale(1 - t).add(Color.init(0.5, 0.7, 1.0).scale(t));
}
@ -40,6 +37,11 @@ pub fn main() anyerror!void {
const image_width = 400;
const image_height = @floatToInt(isize, @intToFloat(f32, image_width) / aspect_ratio);
// World
var world = HittableList.init();
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, 0, -1), 0.5) });
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;
@ -64,7 +66,7 @@ pub fn main() anyerror!void {
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);
const pixel_color = ray_color(r, Hittable{ .hittable_list = world });
try write_color(pixel_color);
}
}

51
zigrtiow/src/sphere.zig Normal file
View File

@ -0,0 +1,51 @@
const vec = @import("./vec.zig");
const ray = @import("./ray.zig");
const hittable = @import("./hittable.zig");
const Vec3 = vec.Vec3;
const Point3 = vec.Point3;
const Ray = ray.Ray;
const HitRecord = hittable.HitRecord;
pub const Sphere = struct {
center: Point3,
radius: f32,
pub fn init(center: Point3, radius: f32) Sphere {
return Sphere{
.center = center,
.radius = radius,
};
}
pub fn hit(sphere: Sphere, r: Ray, t_min: f32, t_max: f32) ?HitRecord {
const center = sphere.center;
const radius = sphere.radius;
const oc = r.origin().sub(center);
const a = r.direction().length_squared();
const half_b = Vec3.dot(oc, r.direction());
const c = oc.length_squared() - radius * radius;
const discriminant = half_b * half_b - a * c;
if (discriminant < 0) return null;
const sqrtd = @sqrt(discriminant);
// Find the nearest root that lies in the acceptable range.
var root = (-half_b - sqrtd) / a;
if ((root < t_min) or (t_max < root)) {
root = (-half_b + sqrtd) / a;
if ((root < t_min) or (t_max < root)) return null;
}
const p = r.at(root);
const outward_normal = p.sub(center).scale(1 / radius);
var hr = HitRecord{
.t = root,
.p = p,
.normal = Vec3.init(0, 0, 0),
.front_face = false,
};
hr.set_face_normal(r, outward_normal);
return hr;
}
};