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};