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 inline fn scatter(self: *Material, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
27 return switch (self.*) {
28 .lambertian => |*lambert| lambert.scatter(r, 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 inline fn scatter(self: *Lambertian, r: *Ray, 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.initT(rec.p, scatter_dir, r.tm);
45 return Ray{ .orig = rec.p, .dir = scatter_dir, .tm = r.tm };
46 }
47};
48
49pub const Metal = struct {
50 albedo: zm.Vec,
51 /// fuzz < 1
52 fuzz: f32,
53
54 pub inline fn scatter(self: *Metal, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
55 const reflected = util.reflect(r.dir, rec.normal);
56 const scattered = Ray.initT(rec.p, zm.normalize3(reflected) + zm.f32x4s(self.fuzz) * util.randomUnitVec(), r.tm);
57 attenuation.* = self.albedo;
58 return if (zm.dot3(scattered.dir, rec.normal)[0] > 0) scattered else null;
59 }
60};
61
62pub const Dielectric = struct {
63 refraction_index: f32,
64
65 pub fn scatter(self: *Dielectric, r: *Ray, rec: *hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
66 attenuation.* = zm.f32x4s(1.0);
67 const ri = if (rec.front_face) (1.0 / self.refraction_index) else self.refraction_index;
68
69 const unit_direction = zm.normalize3(r.dir);
70 const cos_theta = @min(zm.dot3(-unit_direction, rec.normal)[0], 1.0);
71 const sin_theta = @sqrt(1.0 - math.pow(f32, cos_theta, 2));
72
73 const cannot_refract = ri * sin_theta > 1.0;
74 const direction = if (cannot_refract or reflectance(cos_theta, ri) > util.randomF32())
75 util.reflect(unit_direction, rec.normal)
76 else
77 util.refract(unit_direction, rec.normal, ri);
78
79 // return Ray.initT(rec.p, direction, r.tm);
80 return Ray{ .orig = rec.p, .dir = direction, .tm = r.tm };
81 }
82
83 inline fn reflectance(cosine: f32, refraction_index: f32) f32 {
84 var r0 = (1 - refraction_index) / (1 + refraction_index);
85 r0 = r0 * r0;
86 return r0 + (1 - r0) * math.pow(f32, 1 - cosine, 5);
87 }
88};