this repo has no description
1const math = @import("std").math;
2const mem = @import("std").mem;
3
4const zm = @import("zmath");
5
6const hittable = @import("hittable.zig");
7const Ray = @import("Ray.zig");
8const util = @import("util.zig");
9const texture = @import("texture.zig");
10
11pub const MaterialType = enum {
12 lambertian,
13 metal,
14 dielectric,
15 textured,
16};
17
18pub const Material = union(MaterialType) {
19 lambertian: Lambertian,
20 metal: Metal,
21 dielectric: Dielectric,
22 textured: Textured,
23
24 pub fn init(alloc: mem.Allocator, data: anytype) !*Material {
25 const material = try alloc.create(Material);
26
27 switch (@TypeOf(data)) {
28 Metal => material.* = @unionInit(Material, "metal", data),
29 Dielectric => material.* = .{ .dielectric = data },
30
31 Lambertian => material.* = .{ .lambertian = data },
32 zm.Vec => material.* = .{ .lambertian = .{ .albedo = data } },
33
34 Textured => material.* = .{ .textured = data },
35 *texture.Texture => material.* = .{ .textured = .{ .tex = data } },
36
37 else => @panic("Cannot infer Material type of: " ++ @typeName(@TypeOf(data))),
38 }
39
40 return material;
41 }
42
43 pub fn initLambertian(tex: texture.Texture) Material {
44 return .{ .lambertian = .{ .tex = tex } };
45 }
46
47 pub fn initLambertianS(albedo: zm.Vec) Material {
48 return .{ .lambertian = .{ .tex = .{ .solid_color = .{ .albedo = albedo } } } };
49 }
50
51 pub fn initMetal(albedo: zm.Vec, fuzz: f32) Material {
52 return .{ .metal = .{ .albedo = albedo, .fuzz = if (fuzz < 1) fuzz else 1.0 } };
53 }
54
55 pub fn initDielectric(refraction_index: f32) Material {
56 return .{ .dielectric = .{ .refraction_index = refraction_index } };
57 }
58
59 pub inline fn scatter(self: *Material, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
60 return switch (self.*) {
61 inline else => |*n| n.scatter(r, rec, attenuation),
62 };
63 }
64};
65
66pub const Lambertian = struct {
67 albedo: zm.Vec,
68
69 pub inline fn scatter(self: *Lambertian, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
70 var scatter_dir = rec.normal + util.randomUnitVec();
71
72 if (util.nearZero(scatter_dir)) scatter_dir = rec.normal;
73
74 attenuation.* = self.albedo;
75 return Ray{ .orig = rec.p, .dir = scatter_dir, .tm = r.tm };
76 }
77};
78
79pub const Textured = struct {
80 tex: *texture.Texture,
81
82 pub inline fn scatter(self: *Textured, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
83 var scatter_dir = rec.normal + util.randomUnitVec();
84
85 if (util.nearZero(scatter_dir)) scatter_dir = rec.normal;
86
87 attenuation.* = self.tex.value(rec.u, rec.v, rec.p);
88 return Ray{ .orig = rec.p, .dir = scatter_dir, .tm = r.tm };
89 }
90};
91
92pub const Metal = struct {
93 albedo: zm.Vec,
94 /// fuzz < 1
95 fuzz: f32,
96
97 pub fn init(albedo: zm.Vec, fuzz: f32) Metal {
98 return .{ .albedo = albedo, .fuzz = if (fuzz < 1) fuzz else 1.0 };
99 }
100
101 pub inline fn scatter(self: *Metal, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
102 const reflected = util.reflect(r.dir, rec.normal);
103 const scattered = Ray.initT(rec.p, zm.normalize3(reflected) + zm.f32x4s(self.fuzz) * util.randomUnitVec(), r.tm);
104 attenuation.* = self.albedo;
105 return if (zm.dot3(scattered.dir, rec.normal)[0] > 0) scattered else null;
106 }
107};
108
109pub const Dielectric = struct {
110 refraction_index: f32,
111
112 pub fn scatter(self: *Dielectric, r: *Ray, rec: *const hittable.HitRecord, attenuation: *zm.Vec) ?Ray {
113 attenuation.* = zm.f32x4s(1.0);
114 const ri = if (rec.front_face) (1.0 / self.refraction_index) else self.refraction_index;
115
116 const unit_direction = zm.normalize3(r.dir);
117 const cos_theta = @min(zm.dot3(-unit_direction, rec.normal)[0], 1.0);
118 const sin_theta = @sqrt(1.0 - math.pow(f32, cos_theta, 2));
119
120 const cannot_refract = ri * sin_theta > 1.0;
121 const direction = if (cannot_refract or reflectance(cos_theta, ri) > util.randomF32())
122 util.reflect(unit_direction, rec.normal)
123 else
124 util.refract(unit_direction, rec.normal, ri);
125
126 return Ray{ .orig = rec.p, .dir = direction, .tm = r.tm };
127 }
128
129 inline fn reflectance(cosine: f32, refraction_index: f32) f32 {
130 var r0 = (1 - refraction_index) / (1 + refraction_index);
131 r0 = r0 * r0;
132 return r0 + (1 - r0) * math.pow(f32, 1 - cosine, 5);
133 }
134};