diff --git a/in_one_weekend/src/camera.zig b/in_one_weekend/src/camera.zig index e1c7e63..a489ceb 100644 --- a/in_one_weekend/src/camera.zig +++ b/in_one_weekend/src/camera.zig @@ -30,7 +30,7 @@ pub const Camera = struct { pub fn getRay(self: Camera, u: f32, v: f32) Ray { return Ray{ .origin = self.origin, - .direction = self.lowerLeftCorner.add(self.horizontal.mul(u)).add(self.vertical.mul(v)).sub(self.origin), + .direction = self.lowerLeftCorner.add(self.horizontal.mul_s(u)).add(self.vertical.mul_s(v)).sub(self.origin), }; } }; \ No newline at end of file diff --git a/in_one_weekend/src/hittable.zig b/in_one_weekend/src/hittable.zig index fb8a01c..2e2dfdf 100644 --- a/in_one_weekend/src/hittable.zig +++ b/in_one_weekend/src/hittable.zig @@ -1,12 +1,14 @@ const Point3 = @import("vec3.zig").Point3; const Vec3 = @import("vec3.zig").Vec3; const Ray = @import("ray.zig").Ray; +const Material = @import("material.zig").Material; pub const HitRecord = struct { p: Point3, normal: Vec3, t: f32, front_face: bool, + material: Material, pub fn setFaceNormal(hit: *HitRecord, ray: Ray, outward_normal: Vec3) void { const front_face = Vec3.dot(ray.direction, outward_normal) < 0; diff --git a/in_one_weekend/src/main.zig b/in_one_weekend/src/main.zig index c9e1372..0e80a90 100644 --- a/in_one_weekend/src/main.zig +++ b/in_one_weekend/src/main.zig @@ -1,6 +1,7 @@ const std = @import("std"); const print = std.debug.print; const Random = std.rand.Random; + const sdl = @import("sdl.zig"); const vec3 = @import("vec3.zig"); const Vec3 = vec3.Vec3; @@ -11,6 +12,10 @@ const Ray = @import("ray.zig").Ray; const Sphere = @import("sphere.zig").Sphere; const World = @import("world.zig").World; const Camera = @import("camera.zig").Camera; +const material = @import("material.zig"); +const Material = material.Material; +const MaterialType = material.MaterialType; +const scatter = @import("material.zig").scatter; // From: https://github.com/Nelarius/weekend-raytracer-zig/blob/master/src/main.zig // See https://github.com/zig-lang/zig/issues/565 @@ -41,15 +46,16 @@ fn rayColor(ray: Ray, world: World, rng: *Random, depth: i32) Color { } if (world.hit(ray, 0.001, 99999)) |hit| { - const target = hit.p.add(vec3.random_in_hemisphere(rng, hit.normal)); - const newRay = Ray{ .origin = hit.p, .direction = target.sub(hit.p)}; - return rayColor(newRay, world, rng, depth - 1).div(2); + if (scatter(ray, hit, rng)) |sRay| { + return sRay.color.mul(rayColor(sRay.ray, world, rng, depth - 1)); + } + return Color{ .x = 0, .y = 0, .z = 0 }; } - const unitDirection = vec3.unitVector(ray.direction); + const unitDirection = ray.direction.unit(); 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)); + return white.mul_s(1.0 - t).add(blue.mul_s(t)); } fn setProgress(surface: *sdl.c.SDL_Surface, width: usize, height: usize, percent: f32) void { @@ -87,9 +93,18 @@ pub fn main() anyerror!void { const maxDepth = 5; // World + const materialGround = Material{ + .materialType = MaterialType.Lambertian, + .color = Color{ .x = 0.8, .y = 0.8, .z = 0.0 }, + }; + const materialCenter = Material{ + .materialType = MaterialType.Lambertian, + .color = Color{ .x = 0.7, .y = 0.3, .z = 0.3 }, + }; + 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 }, + Sphere{ .center = Point3{ .x = 0, .y = 0, .z = -1 }, .radius = 0.5, .material = materialCenter }, + Sphere{ .center = Point3{ .x = 0, .y = -100.5, .z = -1 }, .radius = 100, .material = materialGround }, }; const world = World{ .spheres = spheres[0..spheres.len] }; diff --git a/in_one_weekend/src/material.zig b/in_one_weekend/src/material.zig new file mode 100644 index 0000000..a3bcb7f --- /dev/null +++ b/in_one_weekend/src/material.zig @@ -0,0 +1,35 @@ +const std = @import("std"); +const Random = std.rand.Random; + +const vec3 = @import("vec3.zig"); +const Color = @import("color.zig").Color; +const Ray = @import("ray.zig").Ray; +const HitRecord = @import("hittable.zig").HitRecord; + +pub const MaterialType = enum { + Lambertian, +}; + +pub const Material = struct { + materialType: MaterialType, + color: Color, +}; + +pub const ScatteredRay = struct { + ray: Ray, + color: Color, +}; + +pub fn scatter(ray: Ray, hit: HitRecord, rng: *Random) ?ScatteredRay { + var scatterDirection = hit.normal.add(vec3.random_unit_vector(rng)); + + // Catch degenerate scatter direction + if (scatterDirection.nearZero()) { + scatterDirection = hit.normal; + } + + return ScatteredRay{ + .ray = Ray{ .origin = hit.p, .direction = scatterDirection}, + .color = hit.material.color, + }; +} \ No newline at end of file diff --git a/in_one_weekend/src/ray.zig b/in_one_weekend/src/ray.zig index 01c980e..ecabed2 100644 --- a/in_one_weekend/src/ray.zig +++ b/in_one_weekend/src/ray.zig @@ -6,6 +6,6 @@ pub const Ray = struct { direction: Vec3, pub fn at(self: Ray, t: f32) Point3 { - return self.origin.add(self.direction.mul(t)); + return self.origin.add(self.direction.mul_s(t)); } }; diff --git a/in_one_weekend/src/sphere.zig b/in_one_weekend/src/sphere.zig index fb2ae38..9edcb22 100644 --- a/in_one_weekend/src/sphere.zig +++ b/in_one_weekend/src/sphere.zig @@ -4,10 +4,12 @@ const Point3 = @import("vec3.zig").Point3; const Vec3 = @import("vec3.zig").Vec3; const HitRecord = @import("hittable.zig").HitRecord; const Ray = @import("ray.zig").Ray; +const Material = @import("material.zig").Material; pub const Sphere = struct { center: Point3, radius: f32, + material: Material, pub fn hit(self: Sphere, ray: Ray, t_min: f32, t_max: f32) ?HitRecord { const oc = ray.origin.sub(self.center); @@ -37,6 +39,7 @@ pub const Sphere = struct { .p = rayAtRoot, .normal = rayAtRoot.sub(self.center).div(self.radius), .front_face = false, + .material = self.material, }; const outwardNormal = hitLocal.p.sub(self.center).div(self.radius); HitRecord.setFaceNormal(&hitLocal, ray, outwardNormal); diff --git a/in_one_weekend/src/vec3.zig b/in_one_weekend/src/vec3.zig index 456204b..fcc6c55 100644 --- a/in_one_weekend/src/vec3.zig +++ b/in_one_weekend/src/vec3.zig @@ -24,7 +24,15 @@ pub const Vec3 = packed struct { }; } - pub fn mul(self: Vec3, scalar: f32) Vec3 { + pub fn mul(self: Vec3, v: Vec3) Vec3 { + return Vec3{ + .x = self.x * v.x, + .y = self.y * v.y, + .z = self.z * v.z, + }; + } + + pub fn mul_s(self: Vec3, scalar: f32) Vec3 { return Vec3{ .x = self.x * scalar, .y = self.y * scalar, @@ -60,14 +68,17 @@ pub const Vec3 = packed struct { }; } - // pub fn unit_vector() Vec3 { - // return - // } -}; + pub fn unit(self: Vec3) Vec3 { + return self.div(self.length()); + } -pub fn unitVector(vec: Vec3) Vec3 { - return vec.div(vec.length()); -} + pub fn nearZero(self: Vec3) bool { + const t = 1e-8; + return math.absFloat(self.x) < t + and math.absFloat(self.y) < t + and math.absFloat(self.z) < t; + } +}; pub fn random(rng: *Random, min: f32, max: f32) Vec3 { const range = max - min; @@ -88,7 +99,7 @@ pub fn random_in_unit_sphere(rng: *Random) Vec3 { } pub fn random_unit_vector(rng: *Random) Vec3 { - return unitVector(random_in_unit_sphere(rng)); + return random_in_unit_sphere(rng).unit(); } pub fn random_in_hemisphere(rng: *Random, normal: Vec3) Vec3 { @@ -97,7 +108,7 @@ pub fn random_in_hemisphere(rng: *Random, normal: Vec3) Vec3 { // In the same hemisphere as the normal return in_unit_sphere; } else { - return in_unit_sphere.mul(-1); + return in_unit_sphere.mul_s(-1); } }