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