this repo has no description
at main 4.5 kB view raw
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};