From 79772c0df464a54d1bcbec61aa40b23f1508a63b Mon Sep 17 00:00:00 2001 From: Fabien Freling Date: Sat, 24 Apr 2021 19:24:28 +0200 Subject: [PATCH] add hittable spheres --- in_one_weekend/src/hittable.zig | 21 +++++++++++++++ in_one_weekend/src/main.zig | 21 ++++++++++----- in_one_weekend/src/sphere.zig | 45 +++++++++++++++++++++++++++++++++ in_one_weekend/src/world.zig | 21 +++++++++++++++ 4 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 in_one_weekend/src/hittable.zig create mode 100644 in_one_weekend/src/sphere.zig create mode 100644 in_one_weekend/src/world.zig diff --git a/in_one_weekend/src/hittable.zig b/in_one_weekend/src/hittable.zig new file mode 100644 index 0000000..fb8a01c --- /dev/null +++ b/in_one_weekend/src/hittable.zig @@ -0,0 +1,21 @@ +const Point3 = @import("vec3.zig").Point3; +const Vec3 = @import("vec3.zig").Vec3; +const Ray = @import("ray.zig").Ray; + +pub const HitRecord = struct { + p: Point3, + normal: Vec3, + t: f32, + front_face: bool, + + pub fn setFaceNormal(hit: *HitRecord, ray: Ray, outward_normal: Vec3) void { + const front_face = Vec3.dot(ray.direction, outward_normal) < 0; + hit.*.front_face = front_face; + if (front_face) { + hit.*.normal = outward_normal; + } else { + const origin = Vec3{}; + hit.*.normal = origin.sub(outward_normal); + } + } +}; \ No newline at end of file diff --git a/in_one_weekend/src/main.zig b/in_one_weekend/src/main.zig index d5a409a..9affc5b 100644 --- a/in_one_weekend/src/main.zig +++ b/in_one_weekend/src/main.zig @@ -6,6 +6,8 @@ const Vec3 = vec3.Vec3; const Color = vec3.Color; const Point3 = vec3.Point3; const Ray = @import("ray.zig").Ray; +const Sphere = @import("sphere.zig").Sphere; +const World = @import("world.zig").World; // From: https://github.com/Nelarius/weekend-raytracer-zig/blob/master/src/main.zig // See https://github.com/zig-lang/zig/issues/565 @@ -29,14 +31,12 @@ fn hitSphere(center: Point3, radius: f32, ray: Ray) f32 { } } -fn rayColor(ray: Ray) Color { - var t = hitSphere(Point3{ .x = 0, .y = 0, .z = -1 }, 0.5, ray); - if (t > 0) { - const normal = vec3.unitVector(ray.at(t).sub(Vec3{ .z = -1 })); - return normal.add(Vec3{ .x = 1, .y = 1, .z = 1}).div(2); +fn rayColor(ray: Ray, world: World) Color { + if (world.hit(ray, 0, 99999)) |hit| { + return hit.normal.add(Vec3{ .x = 1, .y = 1, .z = 1}).div(2); } const unitDirection = vec3.unitVector(ray.direction); - t = 0.5 * (unitDirection.y + 1.0); + const t = 0.5 * (unitDirection.y + 1.0); const white = Color{ .x = 1.0, .y = 1.0, .z = 1.0 }; const blue = Color{ .x = 0.5, .y = 0.7, .z = 1.0 }; return white.mul(1.0 - t).add(blue.mul(t)); @@ -54,6 +54,13 @@ pub fn main() anyerror!void { const imageWidth = 600; const imageHeight = @floatToInt(usize, imageWidth / aspectRatio); + // World + const spheres = [_]Sphere{ + Sphere{ .center = Point3{ .x = 0, .y = 0, .z = -1 }, .radius = 0.5 }, + Sphere{ .center = Point3{ .x = 0, .y = -100.5, .z = -1 }, .radius = 100 }, + }; + const world = World{ .spheres = spheres[0..spheres.len] }; + // Camera const viewportHeight = 2.0; const viewportWidth = viewportHeight * aspectRatio; @@ -95,7 +102,7 @@ pub fn main() anyerror!void { .origin = origin, .direction = lowerLeftCorner.add(horizontal.mul(u)).add(vertical.mul(v)).sub(origin), }; - const pixelColor = rayColor(r); + const pixelColor = rayColor(r, world); sdl.setSurfacePixel(surface, i, j, pixelColor); i += 1; } diff --git a/in_one_weekend/src/sphere.zig b/in_one_weekend/src/sphere.zig new file mode 100644 index 0000000..fb2ae38 --- /dev/null +++ b/in_one_weekend/src/sphere.zig @@ -0,0 +1,45 @@ +const math = @import("std").math; + +const Point3 = @import("vec3.zig").Point3; +const Vec3 = @import("vec3.zig").Vec3; +const HitRecord = @import("hittable.zig").HitRecord; +const Ray = @import("ray.zig").Ray; + +pub const Sphere = struct { + center: Point3, + radius: f32, + + pub fn hit(self: Sphere, ray: Ray, t_min: f32, t_max: f32) ?HitRecord { + const oc = ray.origin.sub(self.center); + const a = ray.direction.length_squared(); + const half_b = Vec3.dot(oc, ray.direction); + const c = oc.length_squared() - self.radius * self.radius; + + const discriminant = half_b * half_b - a * c; + if (discriminant < 0) { + return null; + } + + const sqrtd = math.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 rayAtRoot = ray.at(root); + var hitLocal = HitRecord{ + .t = root, + .p = rayAtRoot, + .normal = rayAtRoot.sub(self.center).div(self.radius), + .front_face = false, + }; + const outwardNormal = hitLocal.p.sub(self.center).div(self.radius); + HitRecord.setFaceNormal(&hitLocal, ray, outwardNormal); + return hitLocal; + } +}; \ No newline at end of file diff --git a/in_one_weekend/src/world.zig b/in_one_weekend/src/world.zig new file mode 100644 index 0000000..a07a8e8 --- /dev/null +++ b/in_one_weekend/src/world.zig @@ -0,0 +1,21 @@ +const Sphere = @import("sphere.zig").Sphere; +const HitRecord = @import("hittable.zig").HitRecord; +const Ray = @import("ray.zig").Ray; + +pub const World = struct { + spheres: []const Sphere, + + pub fn hit(self: World, ray: Ray, t_min: f32, t_max: f32) ?HitRecord { + var hitRecord: ?HitRecord = null; + var closest = t_max; + + for (self.spheres) |sphere| { + if (sphere.hit(ray, t_min, closest)) |localHit| { + closest = localHit.t; + hitRecord = localHit; + } + } + + return hitRecord; + } +}; \ No newline at end of file