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