this repo has no description
1const math = @import("std").math; 2 3const zm = @import("zmath"); 4 5const hittable = @import("hittable.zig"); 6const Ray = @import("ray.zig"); 7const util = @import("util.zig"); 8 9pub const Material = union(enum) { 10 lambertian: Lambertian, 11 metal: Metal, 12 dielectric: Dielectric, 13 14 pub fn lambertian(albedo: zm.Vec) Material { 15 return .{ .lambertian = .{ .albedo = albedo } }; 16 } 17 18 pub fn metal(albedo: zm.Vec, fuzz: f32) Material { 19 return .{ .metal = .{ .albedo = albedo, .fuzz = if (fuzz < 1) fuzz else 1.0 } }; 20 } 21 22 pub fn dielectric(refraction_index: f32) Material { 23 return .{ .dielectric = .{ .refraction_index = refraction_index } }; 24 } 25 26 pub fn scatter(self: *Material, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 27 return switch (self.*) { 28 .lambertian => |*lambert| lambert.scatter(rec, attenuation), 29 .metal => |*met| met.scatter(r, rec, attenuation), 30 .dielectric => |*die| die.scatter(r, rec, attenuation), 31 }; 32 } 33}; 34 35pub const Lambertian = struct { 36 albedo: zm.Vec, 37 38 pub fn scatter(self: *Lambertian, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 39 var scatter_dir = rec.normal + util.randomUnitVec(); 40 41 if (util.nearZero(scatter_dir)) scatter_dir = rec.normal; 42 43 attenuation.* = self.albedo; 44 return Ray.init(rec.p, scatter_dir); 45 } 46}; 47 48pub const Metal = struct { 49 albedo: zm.Vec, 50 /// fuzz < 1 51 fuzz: f32, 52 53 pub fn scatter(self: *Metal, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 54 const reflected = util.reflect(r.dir, rec.normal); 55 const scattered = Ray.init(rec.p, zm.normalize3(reflected) + zm.f32x4s(self.fuzz) * util.randomUnitVec()); 56 attenuation.* = self.albedo; 57 return if (zm.dot3(scattered.dir, rec.normal)[0] > 0) scattered else null; 58 } 59}; 60 61pub const Dielectric = struct { 62 refraction_index: f32, 63 64 pub fn scatter(self: *Dielectric, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray { 65 attenuation.* = zm.f32x4s(1.0); 66 const ri = if (rec.front_face) (1.0 / self.refraction_index) else self.refraction_index; 67 68 const unit_direction = zm.normalize3(r.dir); 69 const cos_theta = @min(zm.dot3(-unit_direction, rec.normal)[0], 1.0); 70 const sin_theta = @sqrt(1.0 - cos_theta * cos_theta); 71 72 const cannot_refract = ri * sin_theta > 1.0; 73 const direction = blk: { 74 if (cannot_refract or reflectance(cos_theta, ri) > util.randomF32()) { 75 break :blk util.reflect(unit_direction, rec.normal); 76 } else { 77 break :blk util.refract(unit_direction, rec.normal, ri); 78 } 79 }; 80 81 return Ray.init(rec.p, direction); 82 } 83 84 fn reflectance(cosine: f32, refraction_index: f32) f32 { 85 var r0 = (1 - refraction_index) / (1 + refraction_index); 86 r0 = r0 * r0; 87 return r0 + (1 - r0) * math.pow(f32, 1 - cosine, 5); 88 } 89};