update raytracer benchmark

This commit is contained in:
Simon Cruanes 2023-06-08 15:48:28 -04:00
parent 112e5a183b
commit 0ace7726f4
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
8 changed files with 305 additions and 205 deletions

View file

@ -28,4 +28,14 @@ bench-fib:
hyperfine -L psize $(BENCH_PSIZE) \ hyperfine -L psize $(BENCH_PSIZE) \
'./_build/default/benchs/fib_rec.exe -cutoff $(BENCH_CUTOFF) -niter $(NITER) -psize={psize} -n $(N)' './_build/default/benchs/fib_rec.exe -cutoff $(BENCH_CUTOFF) -niter $(NITER) -psize={psize} -n $(N)'
NX?=400
NY?=200
NS?=150
RAYOUT?=out.ppm
RAY_J?=1,4,8,16
bench-raytracer:
dune build $(DUNE_OPTS_BENCH) benchs/raytracer/raytracer.exe
hyperfine -L j $(RAY_J) \
'./_build/default/benchs/raytracer/raytracer.exe -nx=$(NX) -ny=$(NY) -ns=$(NS) -j={j}'
.PHONY: test clean .PHONY: test clean

View file

@ -1,4 +1,5 @@
; parallel version of https://github.com/samrat/rayml
(executable (executable
(name raytracer) (name raytracer)
(flags :standard -warn-error -a+8)) (libraries moonpool unix))

View file

@ -1,10 +1,7 @@
type ray = { origin: Vec3.vec3; type ray = {
dir: Vec3.vec3 } origin: Vec3.vec3;
dir: Vec3.vec3;
}
let point_at_parameter r t = let point_at_parameter r t = Vec3.add r.origin (Vec3.mul t r.dir)
Vec3.add r.origin (Vec3.mul t r.dir) let create o d = { origin = o; dir = d }
let create o d =
{ origin = o;
dir = d }
;;

View file

@ -1,218 +1,323 @@
open Printf
open Vec3 open Vec3
open Ray open Ray
open Moonpool
type material = Lambertian of Vec3.vec3 (* albedo *) let ( let@ ) = ( @@ )
| Metal of Vec3.vec3 * float (* albedo, fuzz *) let fpf = Printf.fprintf
| Dielectric of float (* refractive index *) let pf = Printf.printf
type sphere = { center: Vec3.vec3; type material =
radius: float; | Lambertian of Vec3.vec3 (* albedo *)
mat: material; | Metal of Vec3.vec3 * float (* albedo, fuzz *)
} | Dielectric of float (* refractive index *)
type hitable = Sphere of sphere type sphere = {
| World of hitable list center: Vec3.vec3;
radius: float;
mat: material;
}
type hitable =
| Sphere of sphere
| World of hitable list
type hit_rec = { t : float; type hit_rec = {
p: Vec3.vec3; t: float;
normal: Vec3.vec3; p: Vec3.vec3;
mat: material option; } normal: Vec3.vec3;
mat: material option;
}
type scatter = { ray : Ray.ray; type scatter = {
color: Vec3.vec3; ray: Ray.ray;
scatter: bool;} color: Vec3.vec3;
scatter: bool;
}
type hit = hit_rec option type hit = hit_rec option
(* Produce a random point inside the unit sphere. Works by picking a (* Produce a random point inside the unit sphere. Works by picking a
random point in the unit cube, rejecting if not inside the sphere. *) random point in the unit cube, rejecting if not inside the sphere. *)
let rec random_in_unit_sphere () = let rec random_in_unit_sphere () =
let p = (Vec3.sub (Vec3.mul 2.0 (Vec3.of_floats ((Random.float 1.0), let p =
(Random.float 1.0), Vec3.sub
(Random.float 1.0)))) (Vec3.mul 2.0
(Vec3.of_floats (1., 1., 1.))) in (Vec3.of_floats (Random.float 1.0, Random.float 1.0, Random.float 1.0)))
if ((Vec3.dot p p) >= 1.0) (Vec3.of_floats (1., 1., 1.))
then p in
else random_in_unit_sphere () if Vec3.dot p p >= 1.0 then
p
else
random_in_unit_sphere ()
let reflect v n = let reflect v n = Vec3.sub v (Vec3.mul (2. *. Vec3.dot v n) n)
Vec3.sub v (Vec3.mul (2. *. (Vec3.dot v n)) n)
let refract v n ni_over_nt = let refract v n ni_over_nt =
let uv = Vec3.unit_vector v in let uv = Vec3.unit_vector v in
let dt = Vec3.dot uv n in let dt = Vec3.dot uv n in
let discriminant = 1.0 -. ((ni_over_nt*.ni_over_nt) *. (1.0 -. dt*.dt)) in let discriminant = 1.0 -. (ni_over_nt *. ni_over_nt *. (1.0 -. (dt *. dt))) in
if discriminant > 0.0 if discriminant > 0.0 then (
then let refracted =
let refracted = (Vec3.sub (Vec3.mul ni_over_nt (Vec3.sub v (Vec3.mul dt n))) (Vec3.mul (sqrt discriminant) n)) in Vec3.sub
Some(refracted) (Vec3.mul ni_over_nt (Vec3.sub v (Vec3.mul dt n)))
else None (Vec3.mul (sqrt discriminant) n)
in
Some refracted
) else
None
let hit_scatter r_in hit_rec = let hit_scatter r_in hit_rec =
match hit_rec.mat with match hit_rec.mat with
(* reflect in random direction *) (* reflect in random direction *)
Some(Lambertian(albedo)) -> | Some (Lambertian albedo) ->
let target = (Vec3.add (Vec3.add hit_rec.p hit_rec.normal) (random_in_unit_sphere ())) in let target =
let scatter = { ray = Ray.create hit_rec.p (Vec3.sub target hit_rec.p); Vec3.add (Vec3.add hit_rec.p hit_rec.normal) (random_in_unit_sphere ())
color = albedo; in
scatter = true;} let scatter =
in scatter {
ray = Ray.create hit_rec.p (Vec3.sub target hit_rec.p);
color = albedo;
scatter = true;
}
in
scatter
(* "shiny"- angle of reflectance = angle of incidence *) (* "shiny"- angle of reflectance = angle of incidence *)
| Some(Metal(albedo, fuzz)) -> | Some (Metal (albedo, fuzz)) ->
let reflected = reflect (Vec3.unit_vector r_in.dir) hit_rec.normal in let reflected = reflect (Vec3.unit_vector r_in.dir) hit_rec.normal in
let scattered_ray = Ray.create hit_rec.p (Vec3.add reflected (Vec3.mul fuzz (random_in_unit_sphere ()))) in let scattered_ray =
let scattered = { ray = scattered_ray; Ray.create hit_rec.p
color = albedo; (Vec3.add reflected (Vec3.mul fuzz (random_in_unit_sphere ())))
scatter = (Vec3.dot scattered_ray.dir hit_rec.normal) > 0.0;} in in
let scattered =
{
ray = scattered_ray;
color = albedo;
scatter = Vec3.dot scattered_ray.dir hit_rec.normal > 0.0;
}
in
scattered scattered
| Some (Dielectric ref_idx) ->
| Some(Dielectric(ref_idx)) ->
let reflected = reflect (Vec3.unit_vector r_in.dir) hit_rec.normal in let reflected = reflect (Vec3.unit_vector r_in.dir) hit_rec.normal in
let attenuation = Vec3.of_floats (1.0, 1.0, 1.0) in let attenuation = Vec3.of_floats (1.0, 1.0, 1.0) in
let (outward_normal, ni_over_nt) = let outward_normal, ni_over_nt =
if (Vec3.dot r_in.dir hit_rec.normal) > 0.0 if Vec3.dot r_in.dir hit_rec.normal > 0.0 then
then (Vec3.neg hit_rec.normal, ref_idx) Vec3.neg hit_rec.normal, ref_idx
else (hit_rec.normal, 1.0 /. ref_idx) in else
hit_rec.normal, 1.0 /. ref_idx
in
let scattered_ray = let scattered_ray =
match (refract r_in.dir outward_normal ni_over_nt) with match refract r_in.dir outward_normal ni_over_nt with
| Some(refracted) -> Ray.create hit_rec.p refracted | Some refracted -> Ray.create hit_rec.p refracted
| None -> Ray.create hit_rec.p reflected in | None -> Ray.create hit_rec.p reflected
in
let scattered = let scattered =
{ ray= scattered_ray; { ray = scattered_ray; color = attenuation; scatter = false }
color= attenuation; in
scatter = false; } in
scattered scattered
| None -> failwith "not a real material type" | None -> failwith "not a real material type"
let hit_sphere sphere ray (tmin, tmax) = let hit_sphere sphere ray (tmin, tmax) : hit =
let oc = sub ray.origin sphere.center in let oc = sub ray.origin sphere.center in
let a = dot ray.dir ray.dir in let a = dot ray.dir ray.dir in
let b = (dot oc ray.dir) in let b = dot oc ray.dir in
let c = (dot oc oc) -. (sphere.radius *. sphere.radius) in let c = dot oc oc -. (sphere.radius *. sphere.radius) in
let discriminant = b*.b -. a*.c in let discriminant = (b *. b) -. (a *. c) in
if (discriminant > 0.0) if discriminant > 0.0 then (
then let t = (-.b -. sqrt discriminant) /. a in
let t = (-.b -. (sqrt discriminant)) /. a in
if (t < tmax && t > tmin) if t < tmax && t > tmin then (
then
let p = Ray.point_at_parameter ray t in let p = Ray.point_at_parameter ray t in
Some { t = t; Some
p = p; {
normal = mul (1. /. sphere.radius) (sub p sphere.center); t;
mat = Some(sphere.mat) p;
} normal = mul (1. /. sphere.radius) (sub p sphere.center);
else mat = Some sphere.mat;
let t = (-.b +. (sqrt discriminant)) /. a in }
if (t < tmax && t > tmin) ) else (
then let t = (-.b +. sqrt discriminant) /. a in
if t < tmax && t > tmin then (
let p = Ray.point_at_parameter ray t in let p = Ray.point_at_parameter ray t in
Some { t = t; Some
p = p; {
normal = mul (1. /. sphere.radius) (sub p sphere.center); t;
mat = Some(sphere.mat); p;
} normal = mul (1. /. sphere.radius) (sub p sphere.center);
else None mat = Some sphere.mat;
else None }
) else
None
)
) else
None
let rec hit_world world ray (tmin, tmax) = let rec hit_world (world : hitable list) ray (tmin, tmax) : hit =
List.fold_left List.fold_left
(fun acc h -> (fun acc h ->
let prev_rec = match acc with let prev_rec =
None -> { t = tmax; match acc with
p = Vec3.of_floats (-1., -1., -1.); | None ->
normal = Vec3.of_floats (-1., -1., -1.); {
mat = None } t = tmax;
| Some(r) -> r in p = Vec3.of_floats (-1., -1., -1.);
match (hit h ray (tmin, prev_rec.t)) with normal = Vec3.of_floats (-1., -1., -1.);
Some(r) -> Some r mat = None;
| None -> acc) None world }
| Some r -> r
in
match hit h ray (tmin, prev_rec.t) with
| Some r -> Some r
| None -> acc)
None world
and hit h ray (tmin, tmax) = and hit h ray (tmin, tmax) : hit =
match h with match h with
Sphere(s) -> hit_sphere s ray (tmin, tmax) | Sphere s -> hit_sphere s ray (tmin, tmax)
| World(w) -> hit_world w ray (tmin, tmax) | World w -> hit_world w ray (tmin, tmax)
let rec get_color world ray depth : vec3 =
let rec get_color world ray depth = match hit world ray (0., Float.infinity) with
match (hit world ray (0., Float.infinity)) with | Some hit_result ->
Some hit_result -> if depth < 50 then (
if (depth < 50) let s = hit_scatter ray hit_result in
then let s = hit_scatter ray hit_result in Vec3.pmul s.color (get_color world s.ray (depth + 1))
Vec3.pmul s.color (get_color world s.ray (depth+1)) ) else
else Vec3.of_floats (0., 0., 0.) Vec3.of_floats (0., 0., 0.)
| None -> | None ->
let unit_direction = unit_vector ray.dir in let unit_direction = unit_vector ray.dir in
let t = 0.5 *. (unit_direction.y +. 1.0) in let t = 0.5 *. (unit_direction.y +. 1.0) in
add (mul (1.0 -. t) {x= 1.0; y=1.0; z= 1.0}) (mul t {x= 0.5; y= 0.7; z= 1.0}) add
(mul (1.0 -. t) { x = 1.0; y = 1.0; z = 1.0 })
(mul t { x = 0.5; y = 0.7; z = 1.0 })
let mk_world () =
let write_to_file filename = let sphere1 =
Random.self_init (); Sphere
{
let sphere1 = Sphere {center = Vec3.of_floats (0., 0., -1.); center = Vec3.of_floats (0., 0., -1.);
radius = 0.5; radius = 0.5;
mat = Lambertian (Vec3.of_floats (0.8, 0.3, 0.3)) } in mat = Lambertian (Vec3.of_floats (0.8, 0.3, 0.3));
let sphere2 = Sphere {center = Vec3.of_floats (0., -100.5, -1.); }
radius = 100.0; in
mat = Lambertian (Vec3.of_floats (0.8, 0.8, 0.0))} in let sphere2 =
let sphere3 = Sphere {center = Vec3.of_floats (-1.0, 0., -1.); Sphere
radius = 0.5; {
mat = Metal ((Vec3.of_floats (0.8, 0.6, 0.2)), 0.4)} in center = Vec3.of_floats (0., -100.5, -1.);
radius = 100.0;
mat = Lambertian (Vec3.of_floats (0.8, 0.8, 0.0));
}
in
let sphere3 =
Sphere
{
center = Vec3.of_floats (-1.0, 0., -1.);
radius = 0.5;
mat = Metal (Vec3.of_floats (0.8, 0.6, 0.2), 0.4);
}
in
(* let sphere4 = Sphere {center = Vec3.of_floats (1.0, 0., -1.); *) (* let sphere4 = Sphere {center = Vec3.of_floats (1.0, 0., -1.); *)
(* radius = 0.5; *) (* radius = 0.5; *)
(* mat = Metal ((Vec3.of_floats (0.8, 0.8, 0.8)), 0.1)} in *) (* mat = Metal ((Vec3.of_floats (0.8, 0.8, 0.8)), 0.1)} in *)
let sphere4 = Sphere {center = Vec3.of_floats (1.0, 0.0, -1.); let sphere4 =
radius = 0.5; Sphere
mat = Dielectric (1.5)} in {
let world = World [sphere3; sphere2; sphere1; sphere4;] in center = Vec3.of_floats (1.0, 0.0, -1.);
radius = 0.5;
mat = Dielectric 1.5;
}
in
World [ sphere3; sphere2; sphere1; sphere4 ]
type config = {
nx: int;
ny: int;
ns: int;
j: int;
out: string;
}
let run (config : config) =
Random.self_init ();
let world = mk_world () in
let nx = 400 in let nx = 400 in
let ny = 200 in let ny = 200 in
let ns = 150 in (* samples per pixel *) let ns = 150 in
let oc = open_out filename in (* samples per pixel *)
fprintf oc "P3\n"; let oc = open_out config.out in
fprintf oc "%d\n" nx; let@ () =
fprintf oc "%d\n" ny; Fun.protect ~finally:(fun () ->
fprintf oc "\n255\n"; flush oc;
close_out oc)
in
fpf oc "P3\n";
fpf oc "%d\n" nx;
fpf oc "%d\n" ny;
fpf oc "\n255\n";
let lower_left_corner = Vec3.of_floats (-2., -1., -1.) in let lower_left_corner = Vec3.of_floats (-2., -1., -1.) in
let horizontal = Vec3.of_floats (4., 0., 0.) in let horizontal = Vec3.of_floats (4., 0., 0.) in
let vertical = Vec3.of_floats (0., 2., 0.) in let vertical = Vec3.of_floats (0., 2., 0.) in
let origin = Vec3.of_floats (0., 0., 0.) in let origin = Vec3.of_floats (0., 0., 0.) in
for j = ny downto 1 do for j = ny downto 1 do
for i = 0 to nx-1 do for i = 0 to nx - 1 do
let color = ref {x=0.; y=0.; z=0.} in let color = ref { x = 0.; y = 0.; z = 0. } in
for s = 0 to ns-1 do for _step = 0 to ns - 1 do
(* NOTE: Random.float is bounds __inclusive__ *) (* NOTE: Random.float is bounds __inclusive__ *)
let u = (Float.of_int i +. (Random.float 1.0)) /. (Float.of_int nx) in let u = (Float.of_int i +. Random.float 1.0) /. Float.of_int nx in
let v = (Float.of_int j +. (Random.float 1.0)) /. (Float.of_int ny) in let v = (Float.of_int j +. Random.float 1.0) /. Float.of_int ny in
let r = { origin = origin;
dir = Vec3.add lower_left_corner (Vec3.add (Vec3.mul u horizontal) (Vec3.mul v vertical)) } in
color := Vec3.add !color (get_color world r 0);
let r =
{
origin;
dir =
Vec3.add lower_left_corner
(Vec3.add (Vec3.mul u horizontal) (Vec3.mul v vertical));
}
in
color := Vec3.add !color (get_color world r 0)
done; done;
color := Vec3.mul (1. /. (Float.of_int ns)) !color ; color := Vec3.mul (1. /. Float.of_int ns) !color;
(* gamma correction *) (* gamma correction *)
color := Vec3.of_floats (sqrt(!color.x), color := Vec3.of_floats (sqrt !color.x, sqrt !color.y, sqrt !color.z);
sqrt(!color.y), let { x = r; y = g; z = b } = !color in
sqrt(!color.z)); let ir, ig, ib =
let {x=r; y=g; z=b} = !color in ( Int.of_float (r *. 255.99),
let (ir, ig, ib) = (Int.of_float (r*.255.99), Int.of_float (g *. 255.99),
Int.of_float (g*.255.99), Int.of_float (b *. 255.99) )
Int.of_float (b*.255.99)) in in
fprintf oc "%d " ir; fpf oc "%d " ir;
fprintf oc "%d " ig; fpf oc "%d " ig;
fprintf oc "%d \n" ib; fpf oc "%d \n" ib
done; done
done; done
Out_channel.close oc
let () = let () =
write_to_file "out.ppm" let nx = ref 400 in
let ny = ref 200 in
let ns = ref 150 in
let j = ref 4 in
let out = ref "out.ppm" in
let progress = ref false in
let opts =
[
"-j", Arg.Set_int j, " set minimum number of threads";
"-nx", Arg.Set_int nx, " pixels in x axis";
"-ny", Arg.Set_int ny, " pixels in y axis";
"-ns", Arg.Set_int ns, " number of samples per pixel";
"-o", Arg.Set_string out, " output file";
"-p", Arg.Set progress, " progress bar";
]
|> Arg.align
in
Arg.parse opts ignore "";
let config = { nx = !nx; ny = !ny; ns = !ns; out = !out; j = !j } in
let t = Unix.gettimeofday () in
run config;
let elapsed = Unix.gettimeofday () -. t in
pf "done in %.4fs\n%!" elapsed;
()

View file

@ -1,47 +1,29 @@
type vec3 = { x: float; type vec3 = {
y: float; x: float;
z: float } y: float;
z: float;
}
let of_floats (e1, e2, e3) = let of_floats (e1, e2, e3) = { x = e1; y = e2; z = e3 }
{x = e1; y = e2; z = e3} let add v w = { x = v.x +. w.x; y = v.y +. w.y; z = v.z +. w.z }
let sub v w = { x = v.x -. w.x; y = v.y -. w.y; z = v.z -. w.z }
let add v w = let neg v = sub { x = 0.0; y = 0.0; z = 0.0 } v
{x = v.x +. w.x; let dot v w = (v.x *. w.x) +. (v.y *. w.y) +. (v.z *. w.z)
y = v.y +. w.y;
z = v.z +. w.z}
let sub v w =
{x = v.x -. w.x;
y = v.y -. w.y;
z = v.z -. w.z}
let neg v =
sub {x= 0.0; y=0.0; z=0.0} v
let dot v w =
v.x*.w.x +. v.y*.w.y +.v.z*.w.z
let cross v w = let cross v w =
{x = v.y*.w.z -. v.z*.w.y; {
y = v.z*.w.x -. v.x*.w.z; x = (v.y *. w.z) -. (v.z *. w.y);
z = v.x*.w.y -. v.y*.w.x} y = (v.z *. w.x) -. (v.x *. w.z);
z = (v.x *. w.y) -. (v.y *. w.x);
}
let length v = let length v = sqrt (dot v v)
sqrt (dot v v)
let unit_vector v = let unit_vector v =
let l = length v in let l = length v in
{ x = v.x /. l; { x = v.x /. l; y = v.y /. l; z = v.z /. l }
y = v.y /. l;
z = v.z /. l }
let mul t v = let mul t v = { x = t *. v.x; y = t *. v.y; z = t *. v.z }
{ x = t *. v.x;
y = t *. v.y;
z = t *. v.z }
(* pairwise multiplication *) (* pairwise multiplication *)
let pmul v w = let pmul v w = { x = v.x *. w.x; y = v.y *. w.y; z = v.z *. w.z }
{ x = v.x *. w.x;
y = v.y *. w.y;
z = v.z *. w.z; }

View file

@ -1,13 +1,15 @@
type vec3 = { x: float; type vec3 = {
y: float; x: float;
z: float } y: float;
z: float;
}
val of_floats : float * float * float -> vec3 val of_floats : float * float * float -> vec3
val add : vec3 -> vec3 -> vec3 val add : vec3 -> vec3 -> vec3
val sub : vec3 -> vec3 -> vec3 val sub : vec3 -> vec3 -> vec3
val neg : vec3 -> vec3 val neg : vec3 -> vec3
val dot : vec3 -> vec3 -> float val dot : vec3 -> vec3 -> float
val cross: vec3 -> vec3 -> vec3 val cross : vec3 -> vec3 -> vec3
val mul : float -> vec3 -> vec3 val mul : float -> vec3 -> vec3
val unit_vector : vec3 -> vec3 val unit_vector : vec3 -> vec3
val pmul : vec3 -> vec3 -> vec3 val pmul : vec3 -> vec3 -> vec3

2
dune
View file

@ -1,5 +1,5 @@
(env (env
(_ (flags :standard -strict-sequence -warn-error -a+8 -w +a-4-40-70))) (_ (flags :standard -strict-sequence -warn-error -a+8 -w +a-4-40-41-42-70)))
(mdx) (mdx)

3
raytracer.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
DUNE_OPTS="--profile=release --display=quiet"
exec dune exec $DUNE_OPTS benchs/raytracer/raytracer.exe -- $@