M hittable.ha => hittable.ha +5 -4
@@ 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;
};
M main.ha => main.ha +27 -16
@@ 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);
A material.ha => material.ha +72 -0
@@ 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;
+};
M sphere.ha => sphere.ha +5 -2
@@ 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));
};