diff options
| author | nsfisis <nsfisis@gmail.com> | 2022-12-07 22:37:14 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2022-12-07 22:54:47 +0900 |
| commit | 86914985b77ddf5d6d3a3dd6c13deed9d906a471 (patch) | |
| tree | a523883d6524d55aee713bfd6400f044f8715d52 /src/rtw/material.zig | |
| parent | e264078ed9d94b5091a6d78f8128496ac49fbde4 (diff) | |
| download | RayTracingInOneWeekend.zig-86914985b77ddf5d6d3a3dd6c13deed9d906a471.tar.gz RayTracingInOneWeekend.zig-86914985b77ddf5d6d3a3dd6c13deed9d906a471.tar.zst RayTracingInOneWeekend.zig-86914985b77ddf5d6d3a3dd6c13deed9d906a471.zip | |
refactor: separate single main.zig
Diffstat (limited to 'src/rtw/material.zig')
| -rw-r--r-- | src/rtw/material.zig | 100 |
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); +} |
