From 6d123e2bf5e24aa6dd065321a83092c0f3dde593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bor=20Gro=C5=A1elj=20Simi=C4=87?= Date: Fri, 7 Jan 2022 03:30:44 +0100 Subject: [PATCH] implement lambertian and metal material --- hittable.ha | 9 ++++--- main.ha | 43 ++++++++++++++++++++------------ material.ha | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sphere.ha | 7 ++++-- 4 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 material.ha diff --git a/hittable.ha b/hittable.ha index e5eafca..836c241 100644 --- a/hittable.ha +++ b/hittable.ha @@ -8,12 +8,13 @@ export type hitfunc = *fn ( export type hit_record = struct { point: vector, normal: vector, + material: *const material, t: f64, front_face: bool, }; export type hittable = struct { - func: hitfunc + hit: hitfunc, }; export fn hit( @@ -22,7 +23,7 @@ export fn hit( t_lim: (f64, f64), record: nullable *hit_record ) bool = - h.func(h, r, t_lim, record); + h.hit(h, r, t_lim, record); export type hit_multi = struct { hittable, @@ -35,7 +36,7 @@ export fn set_outward_normal(record: *hit_record, r: *const ray, outward: vector }; export fn multi(h: []*hittable) hit_multi = hit_multi { - func = &multi_, + hit = &multi_, arr = h }; @@ -56,7 +57,7 @@ fn multi_( }; case let record: *hit_record => for (let i = 0z; i < len(arr); i += 1) { - if (hit(arr[i], r, t_lim, record)) { + if (arr[i].hit(arr[i], r, t_lim, record)) { have_hit = true; t_lim.1 = record.t; }; diff --git a/main.ha b/main.ha index b59d4d1..660d851 100644 --- a/main.ha +++ b/main.ha @@ -4,11 +4,19 @@ use os; use time; fn init_scene() *hittable = { - let arr: []*hittable = []; - append(arr, alloc(make_sphere(V(0.0, 0.0, -1.0), 0.5))); - append(arr, alloc(make_sphere(V(1.0, -0.7, -1.0), 0.3))); - append(arr, alloc(make_sphere(V(0.0, 100.5, -1.0), 100.0))); - return alloc(multi(arr)); + let ground = alloc(make_lambertian(&C(0.8, 0.8, 0.0))); + let center = alloc(make_lambertian(&C(0.7, 0.3, 0.3))); + let left = alloc(make_metal(&C(0.8, 0.8, 0.8))); + let right = alloc(make_metal(&C(0.8, 0.6, 0.2))); + let a = alloc(multi([])); + append(a.arr, [ + alloc(make_sphere(V(0.0, 100.5, -1.0), 100.0, ground)), + alloc(make_sphere(V(0.0, 0.0, -1.0), 0.5, center)), + alloc(make_sphere(V(1.0, -0.7, -1.0), 0.3, center)), + alloc(make_sphere(V(-1.0, 0.0, -1.0), 0.5, left)), + alloc(make_sphere(V(1.0, 0.0, -1.0), 0.5, right)), + ]...); + return a; }; fn compute_color(scene: *hittable, r: *ray, depth: uint) color = { @@ -17,16 +25,19 @@ fn compute_color(scene: *hittable, r: *ray, depth: uint) color = { }; let rec = hit_record { ... }; - if (hit(scene, r, (0.001, math::INF), &rec)) { - *r = ray { - origin = rec.point, - direction = sum(rec.normal, random_unit()), - }; - let c = compute_color(scene, r, depth - 1); - return C(c.r * 0.5, c.g * 0.5, c.b * 0.5); + if (!scene.hit(scene, r, (0.001, math::INF), &rec)) { + return lerp(&C(0.5, 0.7, 1.0), &C(1.0, 1.0, 1.0), + 0.5 * (unit(r.direction).y + 1.0)); }; - return lerp(&C(0.5, 0.7, 1.0), &C(1.0, 1.0, 1.0), - 0.5 * (unit(r.direction).y + 1.0)); + + let scattered = ray { ... }; + let att = color { ... }; + if (!rec.material.scatter(rec.material, r, &rec, &att, &scattered)) { + return C(0.0, 0.0, 0.0); + }; + + let c = compute_color(scene, &scattered, depth - 1); + return C(c.r * att.r, c.g * att.g, c.b * att.b); }; export fn main() void = { @@ -34,8 +45,8 @@ export fn main() void = { let img = image_create(width, height, C(0f64, 0f64, 0f64)); defer image_free(img); - let samples = 1u32; - let max_bounce = 30u; + let samples = 50u32; + let max_bounce = 50u; let focus = V(0.0, 0.0, 1.0); let horizontal = V(4.0, 0.0, 0.0); diff --git a/material.ha b/material.ha new file mode 100644 index 0000000..a66b9c5 --- /dev/null +++ b/material.ha @@ -0,0 +1,72 @@ +use math::{absf64}; + +export type scatterfunc = *fn( + mat: *const material, + r_in: *const ray, + rec: *const hit_record, + att: *color, + r_out: *ray +) bool; + +export type material = struct { + scatter: scatterfunc, +}; + +export type lambertian = struct { + material, + albedo: color, +}; + +export fn make_lambertian(c: *const color) lambertian = lambertian { + scatter = &lambertian_scatter, + albedo = *c, +}; + +fn lambertian_scatter( + mat: *const material, + r_in: *const ray, + rec: *const hit_record, + att: *color, + r_out: *ray +) bool = { + let l = mat: *const lambertian; + *att = l.albedo; + let dir = sum(rec.normal, random_unit()); + if (absf64(dir.x) + absf64(dir.y) + absf64(dir.z) < 3e-8) { + dir = rec.normal; + }; + *r_out = ray { + origin = rec.point, + direction = dir, + }; + return true; +}; + +export type metal = struct { + material, + albedo: color, +}; + +export fn make_metal(c: *color) metal = metal { + scatter = &metal_scatter, + albedo = *c, +}; + +fn reflect(v: *const vector, n: *const vector) vector = + diff(*v, scale(*n, 2.0 * dot(*v, *n))); + +fn metal_scatter( + m: *const material, + r_in: *const ray, + rec: *const hit_record, + att: *color, + r_out: *ray +) bool = { + let m = m: *const metal; + *r_out = ray { + origin = rec.point, + direction = reflect(&unit(r_in.direction), &rec.normal), + }; + *att = m.albedo; + return dot(r_out.direction, rec.normal) > 0.0; +}; diff --git a/sphere.ha b/sphere.ha index 9779fb7..efc1877 100644 --- a/sphere.ha +++ b/sphere.ha @@ -4,10 +4,12 @@ export type sphere = struct { hittable, center: vector, radius: f64, + material: *material, }; -export fn make_sphere(c: vector, r: f64) sphere = sphere { - func = &hit_sphere, +export fn make_sphere(c: vector, r: f64, m: *material) sphere = sphere { + hit = &hit_sphere, + material = m, center = c, radius = r, }; @@ -40,6 +42,7 @@ fn hit_sphere( }; }; record.point = line_at(r, record.t); + record.material = s.material; set_outward_normal(record, r, scale(diff(record.point, s.center), 1.0 / s.radius)); }; -- 2.45.2