aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/rtw/material.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/rtw/material.zig')
-rw-r--r--src/rtw/material.zig100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/rtw/material.zig b/src/rtw/material.zig
new file mode 100644
index 0000000..87244ab
--- /dev/null
+++ b/src/rtw/material.zig
@@ -0,0 +1,100 @@
+const std = @import("std");
+const debug = std.debug;
+const math = std.math;
+
+const Ray = @import("ray.zig").Ray;
+const vec = @import("vec.zig");
+const Vec3 = vec.Vec3;
+const rgb = vec.rgb;
+const Color = vec.Color;
+const HitRecord = @import("hit_record.zig").HitRecord;
+const Texture = @import("texture.zig").Texture;
+const rand = @import("rand.zig");
+const Random = rand.Random;
+const randomPointInUnitSphere = rand.randomPointInUnitSphere;
+const randomUnitVector = rand.randomUnitVector;
+const randomReal01 = rand.randomReal01;
+
+const MaterialTag = enum {
+ diffuse,
+ metal,
+ dielectric,
+};
+
+pub const Material = union(MaterialTag) {
+ diffuse: DiffuseMaterial,
+ metal: MetalMaterial,
+ dielectric: DielectricMaterial,
+
+ pub fn scatter(mat: Material, r_in: Ray, record: HitRecord, attenuation: *Color, scattered: *Ray, rng: Random) bool {
+ return switch (mat) {
+ MaterialTag.diffuse => |diffuse_mat| diffuse_mat.scatter(r_in, record, attenuation, scattered, rng),
+ MaterialTag.metal => |metal_mat| metal_mat.scatter(r_in, record, attenuation, scattered, rng),
+ MaterialTag.dielectric => |dielectric_mat| dielectric_mat.scatter(r_in, record, attenuation, scattered, rng),
+ };
+ }
+};
+
+pub const DiffuseMaterial = struct {
+ albedo: Texture,
+
+ fn scatter(mat: DiffuseMaterial, r_in: Ray, record: HitRecord, attenuation: *Color, scattered: *Ray, rng: Random) bool {
+ var scatter_direction = record.normal.add(randomUnitVector(rng));
+ if (scatter_direction.near_zero()) {
+ scatter_direction = record.normal;
+ }
+ scattered.* = .{ .origin = record.p, .dir = scatter_direction, .time = r_in.time };
+ attenuation.* = mat.albedo.value(record.u, record.v, record.p);
+ return true;
+ }
+};
+
+pub const MetalMaterial = struct {
+ albedo: Color,
+ fuzz: f64,
+
+ fn scatter(mat: MetalMaterial, r_in: Ray, record: HitRecord, attenuation: *Color, scattered: *Ray, rng: Random) bool {
+ debug.assert(mat.fuzz <= 1.0);
+ const reflected = reflect(r_in.dir.normalized(), record.normal);
+ scattered.* = .{ .origin = record.p, .dir = reflected.add(randomPointInUnitSphere(rng).mul(mat.fuzz)), .time = r_in.time };
+ attenuation.* = mat.albedo;
+ return reflected.dot(record.normal) > 0.0;
+ }
+};
+
+pub const DielectricMaterial = struct {
+ // index of refraction.
+ ir: f64,
+
+ fn scatter(mat: DielectricMaterial, r_in: Ray, record: HitRecord, attenuation: *Color, scattered: *Ray, rng: Random) bool {
+ const refraction_ratio = if (record.front_face) 1.0 / mat.ir else mat.ir;
+ const unit_dir = r_in.dir.normalized();
+
+ const cos_theta = @min(unit_dir.mul(-1.0).dot(record.normal), 1.0);
+ const sin_theta = @sqrt(1.0 - cos_theta * cos_theta);
+ // sin(theta') = refraction_ratio * sin(theta) <= 1
+ const can_refract = refraction_ratio * sin_theta <= 1.0;
+
+ const dir = if (can_refract and reflectance(cos_theta, refraction_ratio) < randomReal01(rng)) refract(unit_dir, record.normal, refraction_ratio) else reflect(unit_dir, record.normal);
+ scattered.* = .{ .origin = record.p, .dir = dir, .time = r_in.time };
+ attenuation.* = rgb(1.0, 1.0, 1.0);
+ return true;
+ }
+
+ fn reflectance(cos: f64, refraction_idx: f64) f64 {
+ const r0 = (1.0 - refraction_idx) / (1.0 + refraction_idx);
+ const r1 = r0 * r0;
+ return r1 + (1.0 - r1) * math.pow(f64, 1.0 - cos, 5.0);
+ }
+};
+
+fn reflect(v: Vec3, n: Vec3) Vec3 {
+ return v.sub(n.mul(2 * v.dot(n)));
+}
+
+fn refract(uv: Vec3, n: Vec3, etai_over_etat: f64) Vec3 {
+ const cos_theta = @min(uv.mul(-1.0).dot(n), 1.0);
+ const r_out_perpendicular = uv.add(n.mul(cos_theta)).mul(etai_over_etat);
+ const r_out_parallel = n.mul(-@sqrt(@fabs(1.0 - r_out_perpendicular.normSquared())));
+ return r_out_perpendicular.add(r_out_parallel);
+}