option to disable bounds checking; examples/path_tracing.v: optimizations

pull/3755/head
Delyan Angelov 2020-02-16 17:13:45 +02:00 committed by GitHub
parent 277c55fe5b
commit 195f3f465b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 370 additions and 315 deletions

View File

@ -49,6 +49,8 @@ jobs:
run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v run: ./v -o v2 cmd/v && ./v2 -o v3 cmd/v
- name: Test symlink - name: Test symlink
run: sudo ./v symlink run: sudo ./v symlink
- name: Ensure thirdparty/cJSON/cJSON.o is compiled, before running tests.
run: ./v examples/json.v
- name: Set up pg database - name: Set up pg database
run: | run: |
brew services start postgresql brew services start postgresql

1
.gitignore vendored
View File

@ -57,3 +57,4 @@ info.log
# vim/emacs editor backup files # vim/emacs editor backup files
*~ *~
thirdparty/freetype/ thirdparty/freetype/
cachegrind.out.*

2
examples/.gitignore vendored
View File

@ -13,6 +13,8 @@
/hello_v_js /hello_v_js
/fibonacci /fibonacci
/sqlite /sqlite
/path_tracing
*.ppm
empty_gg_freetype empty_gg_freetype
game_of_life/life_gg game_of_life/life_gg
random_ips random_ips

View File

@ -1,5 +1,4 @@
/********************************************************************** /**********************************************************************
*
* path tracing demo * path tracing demo
* *
* Copyright (c) 2019-2020 Dario Deledda. All rights reserved. * Copyright (c) 2019-2020 Dario Deledda. All rights reserved.
@ -24,17 +23,19 @@
* in linux: ulimit -s byte_size_of_the_stack * in linux: ulimit -s byte_size_of_the_stack
* example: ulimit -s 16000000 * example: ulimit -s 16000000
* - No OpenMP support * - No OpenMP support
*
**********************************************************************/ **********************************************************************/
import os import os
import math import math
import rand import rand
import time
/****************************************************************************** const (
* inf = f64(1e+10)
* 3D Vector utility struct eps = f64(1e-4)
* f_0 = f64(0.0)
******************************************************************************/ )
/***************************** 3D Vector utility struct **********************/
struct Vec { struct Vec {
mut: mut:
x f64 = f64(0.0) x f64 = f64(0.0)
@ -82,11 +83,38 @@ fn (v Vec) norm () Vec {
return Vec{ v.x * tmp_norm , v.y * tmp_norm, v.z * tmp_norm } return Vec{ v.x * tmp_norm , v.y * tmp_norm, v.z * tmp_norm }
} }
/****************************************************************************** /*********************************Image***************************************/
* struct Image {
* Ray width int
* height int
******************************************************************************/ data &Vec
}
fn new_image(w int, h int) Image {
return Image{
width: w,
height: h,
data: &Vec(calloc(sizeof(Vec)*w*h))
}
}
// write out a .ppm file
fn (image Image) save_as_ppm(file_name string) {
npixels := image.width * image.height
mut f_out := os.create(file_name) or { exit }
f_out.writeln('P3')
f_out.writeln('${image.width} ${image.height}')
f_out.writeln('255')
for i in 0..npixels {
c_r := to_int(image.data[i].x)
c_g := to_int(image.data[i].y)
c_b := to_int(image.data[i].z)
f_out.write('$c_r $c_g $c_b ')
}
f_out.close()
}
/*********************************** Ray *************************************/
struct Ray { struct Ray {
o Vec o Vec
d Vec d Vec
@ -99,11 +127,7 @@ enum Refl_t {
refr refr
} }
/****************************************************************************** /********************************* Sphere ************************************/
*
* Sphere
*
******************************************************************************/
struct Sphere { struct Sphere {
rad f64 = f64(0.0) // radius rad f64 = f64(0.0) // radius
p Vec // position p Vec // position
@ -112,58 +136,51 @@ struct Sphere {
refl Refl_t // reflection type => [diffuse, specular, refractive] refl Refl_t // reflection type => [diffuse, specular, refractive]
} }
[inline]
fn (sp Sphere) intersect (r Ray) f64 { fn (sp Sphere) intersect (r Ray) f64 {
op := sp.p - r.o // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0 op := sp.p - r.o // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
mut t := f64(0.0)
eps := f64(1e-4)
b := op.dot(r.d) b := op.dot(r.d)
mut det := b * b - op.dot(op) + sp.rad * sp.rad mut det := b * b - op.dot(op) + sp.rad * sp.rad
if det < 0 { if det < 0 {
return f64(0) return f64(0)
} else {
det = math.sqrt(det)
} }
t = b - det det = math.sqrt(det)
if t > eps { return t }
mut t := b - det
if t > eps {
return t
}
t = b + det t = b + det
if t > eps { return t } if t > eps {
return t
}
return f64(0) return f64(0)
} }
/****************************************************************************** /*********************************** Scenes **********************************
*
* Scenes
*
* 0) Cornell Box with 2 spheres * 0) Cornell Box with 2 spheres
* 1) Sunset * 1) Sunset
* 2) Psychedelic * 2) Psychedelic
* * The sphere fileds are: Sphere{radius, position, emission, color, material}
* the sphere fileds are: Sphere{radius, position, emission, color, material}
*
******************************************************************************/ ******************************************************************************/
const ( const (
Cen = Vec{50, 40.8, -860} // used by scene 1 Cen = Vec{50, 40.8, -860} // used by scene 1
spheres = [ spheres = [
[// scene 0 cornnel box [// scene 0 cornnel box
Sphere{rad: 1e+5, p: Vec{ 1e+5 +1,40.8,81.6} , e: Vec{} , c: Vec{.75,.25,.25} , refl: .diff},//Left Sphere{rad: 1e+5, p: Vec{ 1e+5 +1,40.8,81.6} , e: Vec{} , c: Vec{.75,.25,.25} , refl: .diff},//Left
Sphere{rad: 1e+5, p: Vec{-1e+5 +99,40.8,81.6}, e: Vec{} , c: Vec{.25,.25,.75} , refl: .diff},//Rght Sphere{rad: 1e+5, p: Vec{-1e+5 +99,40.8,81.6}, e: Vec{} , c: Vec{.25,.25,.75} , refl: .diff},//Rght
Sphere{rad: 1e+5, p: Vec{50,40.8, 1e+5} , e: Vec{} , c: Vec{.75,.75,.75} , refl: .diff},//Back Sphere{rad: 1e+5, p: Vec{50,40.8, 1e+5} , e: Vec{} , c: Vec{.75,.75,.75} , refl: .diff},//Back
Sphere{rad: 1e+5, p: Vec{50,40.8,-1e+5 +170} , e: Vec{} , c: Vec{1e-16, 1e-16, 1e-16}, refl: .diff},//Frnt Sphere{rad: 1e+5, p: Vec{50,40.8,-1e+5 +170} , e: Vec{} , c: Vec{} , refl: .diff},//Frnt
Sphere{rad: 1e+5, p: Vec{50, 1e+5, 81.6} , e: Vec{} , c: Vec{.75,.75,.75} , refl: .diff},//Botm Sphere{rad: 1e+5, p: Vec{50, 1e+5, 81.6} , e: Vec{} , c: Vec{.75,.75,.75} , refl: .diff},//Botm
Sphere{rad: 1e+5, p: Vec{50,-1e+5 +81.6,81.6}, e: Vec{} , c: Vec{.75,.75,.75} , refl: .diff},//Top Sphere{rad: 1e+5, p: Vec{50,-1e+5 +81.6,81.6}, e: Vec{} , c: Vec{.75,.75,.75} , refl: .diff},//Top
Sphere{rad: 16.5, p: Vec{27.0,16.5,47.0} , e: Vec{} , c: Vec{1,1,1}.mult_s(.999) , refl: .spec},//Mirr Sphere{rad: 16.5, p: Vec{27,16.5,47} , e: Vec{} , c: Vec{1,1,1}.mult_s(.999) , refl: .spec},//Mirr
Sphere{rad: 16.5, p: Vec{73,16.5,78} , e: Vec{} , c: Vec{1,1,1}.mult_s(.999) , refl: .refr},//Glas Sphere{rad: 16.5, p: Vec{73,16.5,78} , e: Vec{} , c: Vec{1,1,1}.mult_s(.999) , refl: .refr},//Glas
Sphere{rad: 600 , p: Vec{50,681.6-.27,81.6} , e: Vec{12,12,12}, c: Vec{1e-16, 1e-16, 1e-16}, refl: .diff} //Lite Sphere{rad: 600 , p: Vec{50,681.6-.27,81.6} , e: Vec{12,12,12}, c: Vec{}, refl: .diff} //Lite
] ],
[// scene 1 sunset
,[// scene 1 sunset
Sphere{rad: 1600, p: Vec{1.0,0.0,2.0}.mult_s(3000), e: Vec{1.0,.9,.8}.mult_s(1.2e+1*1.56*2) , c: Vec{} , refl: .diff}, // sun Sphere{rad: 1600, p: Vec{1.0,0.0,2.0}.mult_s(3000), e: Vec{1.0,.9,.8}.mult_s(1.2e+1*1.56*2) , c: Vec{} , refl: .diff}, // sun
Sphere{rad: 1560, p: Vec{1,0,2}.mult_s(3500) , e: Vec{1.0,.5,.05}.mult_s(4.8e+1*1.56*2) , c: Vec{} , refl: .diff}, // horizon sun2 Sphere{rad: 1560, p: Vec{1,0,2}.mult_s(3500) , e: Vec{1.0,.5,.05}.mult_s(4.8e+1*1.56*2) , c: Vec{} , refl: .diff}, // horizon sun2
Sphere{rad: 10000, p: Cen+Vec{0,0,-200}, e: Vec{0.00063842, 0.02001478, 0.28923243}.mult_s(6e-2*8), c: Vec{.7,.7,1}.mult_s(.25), refl: .diff}, // sky Sphere{rad: 10000, p: Cen+Vec{0,0,-200}, e: Vec{0.00063842, 0.02001478, 0.28923243}.mult_s(6e-2*8), c: Vec{.7,.7,1}.mult_s(.25), refl: .diff}, // sky
@ -175,10 +192,10 @@ spheres = [
Sphere{rad: 26.5, p: Vec{22,26.5,42}, e: Vec{}, c: Vec{1,1,1}.mult_s(.596) , refl: .spec}, // white Mirr Sphere{rad: 26.5, p: Vec{22,26.5,42}, e: Vec{}, c: Vec{1,1,1}.mult_s(.596) , refl: .spec}, // white Mirr
Sphere{rad: 13, p: Vec{75,13,82 }, e: Vec{}, c: Vec{.96,.96,.96}.mult_s(.96), refl: .refr},// Glas Sphere{rad: 13, p: Vec{75,13,82 }, e: Vec{}, c: Vec{.96,.96,.96}.mult_s(.96), refl: .refr},// Glas
Sphere{rad: 22, p: Vec{87,22,24 }, e: Vec{}, c: Vec{.6,.6,.6}.mult_s(.696) , refl: .refr} // Glas2 Sphere{rad: 22, p: Vec{87,22,24 }, e: Vec{}, c: Vec{.6,.6,.6}.mult_s(.696) , refl: .refr} // Glas2
] ],
,[// scene 3 Psychedelic [// scene 3 Psychedelic
Sphere{rad: 150, p: Vec{50+75,28,62}, e: Vec{1,1,1}.mult_s(0e-3), c: Vec{1,.9,.8}.mult_s(.93), refl: .refr}, Sphere{rad: 150, p: Vec{50+75,28,62}, e: Vec{1,1,1}.mult_s(0e-3), c: Vec{1,.9,.8}.mult_s(.93), refl: .refr},
Sphere{rad: 28 , p: Vec{50+5,-28,62}, e: Vec{1,1,1}.mult_s(1e+1), c: Vec{1,1,1}.mult_s(0) , refl: .diff}, Sphere{rad: 28 , p: Vec{50+5,-28,62}, e: Vec{1,1,1}.mult_s(1e+1), c: Vec{1,1,1}.mult_s(0) , refl: .diff},
Sphere{rad: 300, p: Vec{50,28,62} , e: Vec{1,1,1}.mult_s(0e-3), c: Vec{1,1,1}.mult_s(.93) , refl: .spec} Sphere{rad: 300, p: Vec{50,28,62} , e: Vec{1,1,1}.mult_s(0e-3), c: Vec{1,1,1}.mult_s(.93) , refl: .spec}
@ -188,15 +205,11 @@ spheres = [
) )
/****************************************************************************** /*********************************** Utilities *******************************/
*
* Utility
*
******************************************************************************/
[inline] [inline]
fn clamp(x f64) f64 { fn clamp(x f64) f64 {
if x < f64(0.0) { return f64(0.0) } if x < 0 { return 0 }
if x > f64(1.0) { return f64(1.0) } if x > 1 { return 1 }
return x return x
} }
@ -206,39 +219,27 @@ fn to_int(x f64) int {
return int(p*f64(255.0)+f64(0.5)) return int(p*f64(255.0)+f64(0.5))
} }
[inline] fn intersect(r Ray, spheres &Sphere, nspheres int) (bool, f64, int){
//fn intersect(r Ray, id1 int, scene int) (bool, f64, int){
fn intersect(r Ray, id1 int, spheres []Sphere) (bool, f64, int){
mut d := f64(0) mut d := f64(0)
inf := f64(1e+20) mut t := inf
mut t := f64(1e+20) mut id := 0
//mut i := spheres[scene].len-1 for i:=nspheres-1; i >= 0; i-- {
mut i := spheres.len-1
mut id := id1
for i >= 0 {
//d = spheres[scene][i].intersect(r)
d = spheres[i].intersect(r) d = spheres[i].intersect(r)
if d != 0.0 && d < t { if d > 0 && d < t {
t = d t = d
id = i id = i
} }
i--
} }
return (t < inf) , t, id return (t < inf) , t, id
} }
// some casual random function, try to avoid the 0 // some casual random function, try to avoid the 0
[inline]
fn rand_f64() f64 { fn rand_f64() f64 {
x := (C.rand()+1) & 0x3FFF_FFFF x := (C.rand()+1) & 0x3FFF_FFFF
return f64(x)/f64(0x3FFF_FFFF) return f64(x)/f64(0x3FFF_FFFF)
} }
/******************************************************************************
*
* Cache for sin/cos speed-up table and scene selector
*
******************************************************************************/
const( const(
cache_len = 65536 // the 2*pi angle will be splitted in 65536 part cache_len = 65536 // the 2*pi angle will be splitted in 65536 part
cache_mask = cache_len - 1 // mask to speed-up the module process cache_mask = cache_len - 1 // mask to speed-up the module process
@ -246,27 +247,30 @@ const(
struct Cache { struct Cache {
mut: mut:
scene int = 0
sin_tab [cache_len]f64 sin_tab [cache_len]f64
cos_tab [cache_len]f64 cos_tab [cache_len]f64
} }
fn (c mut Cache) fill() { fn new_tabs() Cache {
inv_len := 1.0 / f64(cache_len) mut c := Cache{}
inv_len := f64(1.0) / f64(cache_len)
for i in 0..cache_len { for i in 0..cache_len {
x := f64(i) * math.pi * 2.0 * inv_len x := f64(i) * math.pi * 2.0 * inv_len
c.sin_tab[i] = math.sin(x) c.sin_tab[i] = math.sin(x)
c.cos_tab[i] = math.cos(x) c.cos_tab[i] = math.cos(x)
} }
return c
} }
/************* Cache for sin/cos speed-up table and scene selector ***********/
const (
tabs = new_tabs()
)
/****************************************************************************** /******************* main function for the radiance calculation **************/
* fn radiance(r Ray, depthi int, scene_id int) Vec {
* main function for the radiance calculation sin_tab := &f64( tabs.sin_tab )
* cos_tab := &f64( tabs.cos_tab )
******************************************************************************/
fn radiance(r Ray, depthi int, tb &Cache) Vec {
mut depth := depthi // actual depth in the reflection tree mut depth := depthi // actual depth in the reflection tree
mut t := f64(0) // distance to intersection mut t := f64(0) // distance to intersection
mut id := 0 // id of intersected object mut id := 0 // id of intersected object
@ -275,19 +279,17 @@ fn radiance(r Ray, depthi int, tb &Cache) Vec {
v_1 := f64(1.0) v_1 := f64(1.0)
//v_2 := f64(2.0) //v_2 := f64(2.0)
scene := spheres[scene_id]
//res, t, id = intersect(r, id, tb.scene) //res, t, id = intersect(r, id, tb.scene)
res, t, id = intersect(r, id, spheres[tb.scene]) res, t, id = intersect(r, scene.data, scene.len)
if !res { return Vec{} } //if miss, return black if !res { return Vec{} } //if miss, return black
obj := spheres[tb.scene][id] // the hit object obj := scene[id] // the hit object
x := r.o + r.d.mult_s(t) x := r.o + r.d.mult_s(t)
n := (x - obj.p).norm() n := (x - obj.p).norm()
mut nl := n nl := if n.dot(r.d) < 0.0 { n } else { n.mult_s(-1) }
if n.dot(r.d) >= 0.0 {
nl = n.mult_s(-1)
}
mut f := obj.c mut f := obj.c
@ -304,7 +306,7 @@ fn radiance(r Ray, depthi int, tb &Cache) Vec {
depth++ depth++
if depth > 5 { if depth > 5 {
if rand_f64() < p { if rand_f64() < p {
f = f.mult_s(1.0/p) f = f.mult_s(f64(1.0)/p)
} else { } else {
return obj.e //R.R. return obj.e //R.R.
} }
@ -322,12 +324,12 @@ fn radiance(r Ray, depthi int, tb &Cache) Vec {
w := nl w := nl
mut u := Vec{1, 0, 0} mut u := if math.abs(w.x) > f64(0.1) {
if math.abs(w.x) > 0.1 { Vec{0, 1, 0}
u = Vec{0, 1, 0} } else {
Vec{1, 0, 0}
} }
u = u.cross(w) u = u.cross(w).norm()
u = u.norm()
v := w.cross(u) v := w.cross(u)
@ -335,154 +337,146 @@ fn radiance(r Ray, depthi int, tb &Cache) Vec {
//d := (u.mult_s(math.cos(r1) * r2s) + v.mult_s(math.sin(r1) * r2s) + w.mult_s(1.0 - r2)).norm() //d := (u.mult_s(math.cos(r1) * r2s) + v.mult_s(math.sin(r1) * r2s) + w.mult_s(1.0 - r2)).norm()
// tabbed speed-up // tabbed speed-up
d := (u.mult_s(tb.cos_tab[r1] * r2s) + v.mult_s(tb.sin_tab[r1] * r2s) + w.mult_s(1.0 - r2)).norm() d := (u.mult_s(cos_tab[r1] * r2s) + v.mult_s(sin_tab[r1] * r2s) + w.mult_s(math.sqrt(f64(1.0) - r2))).norm()
return obj.e + (f * radiance(Ray{x, d}, depth, tb)) return obj.e + f * radiance(Ray{x, d}, depth, scene_id)
} else { } else {
if obj.refl == .spec { // Ideal SPECULAR reflection if obj.refl == .spec { // Ideal SPECULAR reflection
return obj.e + (f * radiance(Ray{x, r.d - n.mult_s(2.0 * n.dot(r.d)) }, depth, tb)) return obj.e + f * radiance(Ray{x, r.d - n.mult_s(2.0 * n.dot(r.d)) }, depth, scene_id)
} }
} }
refl_ray := Ray{x, r.d - n.mult_s(2.0 * n.dot(r.d))} // Ideal dielectric REFRACTION refl_ray := Ray{x, r.d - n.mult_s(2.0 * n.dot(r.d))} // Ideal dielectric REFRACTION
into := n.dot(nl) > 0.0 // Ray from outside going in? into := n.dot(nl) > 0 // Ray from outside going in?
nc := f64(1.0) nc := f64(1.0)
nt := f64(1.5) nt := f64(1.5)
mut nnt := nt / nc nnt := if into { nc / nt } else { nt / nc }
if into { nnt = nc / nt }
ddn := r.d.dot(nl) ddn := r.d.dot(nl)
mut cos2t:= f64(0) cos2t := v_1 - nnt * nnt * (v_1 - ddn * ddn)
cos2t = v_1 - nnt * nnt * (v_1 - ddn * ddn)
if cos2t < 0.0 { // Total internal reflection if cos2t < 0.0 { // Total internal reflection
return obj.e + (f * radiance(refl_ray, depth, tb)) return obj.e + f * radiance(refl_ray, depth, scene_id)
} }
mut dirc := -1 dirc := if into { f64(1) } else { f64(-1) }
if into { dirc = 1 } tdir := (r.d.mult_s(nnt) - n.mult_s(dirc * (ddn * nnt + math.sqrt(cos2t)))).norm()
tdir := r.d.mult_s(nnt) -n.mult_s(dirc).mult_s(ddn * nnt + math.sqrt(cos2t)).norm()
a := nt - nc a := nt - nc
b := nt + nc b := nt + nc
r0 := a * a / (b * b) r0 := a * a / (b * b)
mut c := v_1 - tdir.dot(n) c := if into { v_1 + ddn } else { v_1 - tdir.dot(n) }
if into { c = v_1 + ddn }
re := r0 + (v_1 - r0) * c * c * c * c * c re := r0 + (v_1 - r0) * c * c * c * c * c
tr := v_1 - re tr := v_1 - re
p = f64(.25) + f64(.5) * re pp := f64(.25) + f64(.5) * re
rp := re / p rp := re / pp
tp := tr / (v_1 - p) tp := tr / (v_1 - pp)
mut res_f := obj.e
mut tmp := radiance(Ray{x, tdir}, depth, tb).mult_s(tp)
if rand_f64() < p {
tmp = radiance(refl_ray, depth, tb).mult_s(rp)
}
mut tmp := Vec{}
if depth > 2 { if depth > 2 {
res_f = res_f + f * tmp // Russian roulette
return res_f tmp = if rand_f64() < pp {
radiance(refl_ray, depth, scene_id).mult_s(rp)
} else {
radiance(Ray{x, tdir}, depth, scene_id).mult_s(tp)
} }
} else {
tmp1 := radiance(refl_ray, depth, tb).mult_s(re) + radiance( Ray{x, tdir}, depth, tb).mult_s(tr) tmp = (radiance(refl_ray, depth, scene_id).mult_s(re)) + (radiance( Ray{x, tdir}, depth, scene_id).mult_s(tr))
res_f = res_f + f * tmp1 }
return res_f return obj.e + (f * tmp)
} }
/****************************************************************************** /************************ beam scan routine **********************************/
* fn ray_trace(w int, h int, samps int, file_name string, scene_id int) Image {
* beam scan routine image := new_image(w, h)
*
******************************************************************************/
fn ray_trace(w int, h int, samps int, file_name string, tb &Cache) {
// inverse costants // inverse costants
w1 := f64(1.0 / w) w1 := f64(1.0 / w)
h1 := f64(1.0 / h) h1 := f64(1.0 / h)
samps1 := f64(1.0 / samps) samps1 := f64(1.0 / samps)
cam := Ray{Vec{50, 52, 296.5}, Vec{0, -0.042612, -1}.norm()} // cam position, direction cam := Ray{Vec{50, 52, 295.6}, Vec{0, -0.042612, -1}.norm()} // cam position, direction
cx := Vec{ f64(w) * .5135 / f64(h), 0, 0} cx := Vec{ f64(w) * 0.5135 / f64(h), 0, 0}
cy := ((cx.cross(cam.d)).norm()).mult_s(0.5135) cy := cx.cross(cam.d).norm().mult_s(0.5135)
mut c := [Vec{}].repeat(w * h)
mut r := Vec{} mut r := Vec{}
// OpenMP injection point! #pragma omp parallel for schedule(dynamic, 1) shared(c)
for y:=0; y < h; y++ {
eprint("\rRendering (${samps * 4} spp) ${(100.0 * f64(y)) / (f64(h) - 1.0)}%")
for x := 0; x < w; x++ {
i := (h - y - 1) * w + x
// we use sx and sy to perform a square subsampling of 4 samples
for sy := f64(0.5) ; sy < 2.5; sy += 1.0 {
for sx := f64(0.5); sx < 2.5; sx += 1.0 {
r.x = 0
r.y = 0
r.z = 0
for s := 0; s < samps; s++ {
// speed-up constants // speed-up constants
v_1 := f64(1.0) v_1 := f64(1.0)
v_2 := f64(2.0) v_2 := f64(2.0)
// OpenMP injection point! #pragma omp parallel for schedule(dynamic, 1) shared(c)
for y:=0; y < h; y++ {
eprint("\rRendering (${samps * 4} spp) ${(100.0 * f64(y)) / (f64(h) - 1.0):5.2f}%")
for x := 0; x < w; x++ {
i := (h - y - 1) * w + x
mut ivec := &image.data[i]
// we use sx and sy to perform a square subsampling of 4 samples
for sy := 0; sy < 2; sy ++ {
for sx := 0; sx < 2; sx ++ {
r = Vec{0,0,0}
for s := 0; s < samps; s++ {
r1 := v_2 * rand_f64() r1 := v_2 * rand_f64()
mut dx := v_1 - math.sqrt(v_2 - r1) dx := if r1 < v_1 { math.sqrt(r1) - v_1 } else { v_1 - math.sqrt(v_2 - r1) }
if r1 < v_1 { dx = math.sqrt(r1) - v_1 }
r2 := v_2 * rand_f64() r2 := v_2 * rand_f64()
mut dy := v_1 - math.sqrt(v_2 - r2) dy := if r2 < v_1 { math.sqrt(r2) - v_1 } else { v_1 - math.sqrt(v_2 - r2) }
if r2 < v_1 { dy = math.sqrt(r2) - v_1 }
d := cx.mult_s( ( (sx + dx)*0.5 + f64(x))*w1 - .5) +
cy.mult_s( ( (sy + dy)*0.5 + f64(y))*h1 - .5) + cam.d
r = r + radiance(Ray{cam.o+d.mult_s(140.0), d.norm()}, 0, tb).mult_s(samps1)
d := cx.mult_s( ( (f64(sx) + 0.5 + dx)*0.5 + f64(x))*w1 - .5) +
cy.mult_s( ( (f64(sy) + 0.5 + dy)*0.5 + f64(y))*h1 - .5) + cam.d
r = r + radiance(Ray{cam.o+d.mult_s(140.0), d.norm()}, 0, scene_id).mult_s(samps1)
} }
tmp_vec := Vec{clamp(r.x),clamp(r.y),clamp(r.z)}.mult_s(.25) tmp_vec := Vec{clamp(r.x),clamp(r.y),clamp(r.z)}.mult_s(.25)
c[i] = c[i] + tmp_vec *ivec = *ivec + tmp_vec
} }
} }
} }
} }
eprintln('\nRendering finished.') return image
//
// write out a .ppm file
//
mut f_out := os.create(file_name) or { exit }
f_out.writeln('P3')
f_out.writeln('${w} ${h}')
f_out.writeln('255')
for i in 0..w*h {
c_r := to_int(c[i].x)
c_g := to_int(c[i].y)
c_b := to_int(c[i].z)
f_out.write('$c_r $c_g $c_b ')
}
f_out.close()
println("image saved as [${file_name}]")
} }
fn main() { fn main() {
if os.args.len > 6 {
eprintln('Usage:\n path_tracing [samples] [image.ppm] [scene_n] [width] [height]')
exit(1)
}
mut width := 320 // width of the rendering in pixels
mut height := 200 // height of the rendering in pixels
mut samples := 4 // number of samples per pixel, increase for better quality
mut scene_id := 0 // scene to render [0 cornell box,1 sunset,2 psyco]
mut file_name := 'image.ppm' // name of the output file in .ppm format
if os.args.len >= 2 {
samples = os.args[1].int() / 4
}
if os.args.len >= 3 {
file_name = os.args[2]
}
if os.args.len >= 4 {
scene_id = os.args[3].int()
}
if os.args.len >= 5 {
width = os.args[4].int()
}
if os.args.len == 6 {
height = os.args[5].int()
}
// init the rand, using the same seed allows to obtain the same result in different runs // init the rand, using the same seed allows to obtain the same result in different runs
// change the seed from 2020 for different results // change the seed from 2020 for different results
rand.seed(2020) rand.seed(2020)
// init the sin/cos cache table t1:=time.ticks()
mut tb := Cache{}
tb.fill()
width := 1280 // width of the rendering in pixels image := ray_trace(width, height, samples, file_name, scene_id)
height := 1280 // height of the rendering in pixels t2:=time.ticks()
samples := 10 // number of samples*4 per pixel, increase for better quality
tb.scene = 1 // scene to render [0 cornell box,1 sunset,2 psyco]
file_name := "image.ppm" // name of the output file in .ppm format
ray_trace(width, height, samples, file_name, tb) eprintln('\nRendering finished. Took: ${t2-t1:5d}ms')
image.save_as_ppm( file_name )
t3:=time.ticks()
eprintln('Image saved as [${file_name}]. Took: ${t3-t2:5d}ms')
} }

View File

@ -116,9 +116,11 @@ pub fn (a mut array) sort_with_compare(compare voidptr) {
// a.insert(0, &i) // a.insert(0, &i)
// ---------------------------- // ----------------------------
pub fn (a mut array) insert(i int, val voidptr) { pub fn (a mut array) insert(i int, val voidptr) {
$if !no_bounds_checking? {
if i < 0 || i > a.len { if i < 0 || i > a.len {
panic('array.insert: index out of range (i == $i, a.len == $a.len)') panic('array.insert: index out of range (i == $i, a.len == $a.len)')
} }
}
a.ensure_cap(a.len + 1) a.ensure_cap(a.len + 1)
size := a.element_size size := a.element_size
C.memmove(a.data + (i + 1) * size, a.data + i * size, (a.len - i) * size) C.memmove(a.data + (i + 1) * size, a.data + i * size, (a.len - i) * size)
@ -135,9 +137,11 @@ pub fn (a mut array) prepend(val voidptr) {
// array.delete deletes array element at the given index // array.delete deletes array element at the given index
pub fn (a mut array) delete(i int) { pub fn (a mut array) delete(i int) {
$if !no_bounds_checking? {
if i < 0 || i >= a.len { if i < 0 || i >= a.len {
panic('array.delete: index out of range (i == $i, a.len == $a.len)') panic('array.delete: index out of range (i == $i, a.len == $a.len)')
} }
}
size := a.element_size size := a.element_size
C.memmove(a.data + i * size, a.data + (i + 1) * size, (a.len - i) * size) C.memmove(a.data + i * size, a.data + (i + 1) * size, (a.len - i) * size)
a.len-- a.len--
@ -150,25 +154,31 @@ pub fn (a mut array) clear() {
// Private function. Used to implement array[] operator // Private function. Used to implement array[] operator
fn (a array) get(i int) voidptr { fn (a array) get(i int) voidptr {
$if !no_bounds_checking? {
if i < 0 || i >= a.len { if i < 0 || i >= a.len {
panic('array.get: index out of range (i == $i, a.len == $a.len)') panic('array.get: index out of range (i == $i, a.len == $a.len)')
} }
}
return a.data + i * a.element_size return a.data + i * a.element_size
} }
// array.first returns the first element of the array // array.first returns the first element of the array
pub fn (a array) first() voidptr { pub fn (a array) first() voidptr {
$if !no_bounds_checking? {
if a.len == 0 { if a.len == 0 {
panic('array.first: array is empty') panic('array.first: array is empty')
} }
}
return a.data + 0 return a.data + 0
} }
// array.last returns the last element of the array // array.last returns the last element of the array
pub fn (a array) last() voidptr { pub fn (a array) last() voidptr {
$if !no_bounds_checking? {
if a.len == 0 { if a.len == 0 {
panic('array.last: array is empty') panic('array.last: array is empty')
} }
}
return a.data + (a.len - 1) * a.element_size return a.data + (a.len - 1) * a.element_size
} }
@ -176,9 +186,11 @@ pub fn (a array) last() voidptr {
// array.left returns a new array using the same buffer as the given array // array.left returns a new array using the same buffer as the given array
// with the first `n` elements of the given array. // with the first `n` elements of the given array.
fn (a array) left(n int) array { fn (a array) left(n int) array {
if n < 0 { // $if !no_bounds_checking? {
panic('array.left: index is negative (n == $n)') // if n < 0 {
} // panic('array.left: index is negative (n == $n)')
// }
// }
if n >= a.len { if n >= a.len {
return a.slice(0, a.len) return a.slice(0, a.len)
} }
@ -190,9 +202,11 @@ fn (a array) left(n int) array {
// If `n` is bigger or equal to the length of the given array, // If `n` is bigger or equal to the length of the given array,
// returns an empty array of the same type as the given array. // returns an empty array of the same type as the given array.
fn (a array) right(n int) array { fn (a array) right(n int) array {
if n < 0 { // $if !no_bounds_checking? {
panic('array.right: index is negative (n == $n)') // if n < 0 {
} // panic('array.right: index is negative (n == $n)')
// }
// }
if n >= a.len { if n >= a.len {
return new_array(0, 0, a.element_size) return new_array(0, 0, a.element_size)
} }
@ -206,6 +220,7 @@ fn (a array) right(n int) array {
// set to the number of the elements in the slice. // set to the number of the elements in the slice.
fn (a array) slice(start, _end int) array { fn (a array) slice(start, _end int) array {
mut end := _end mut end := _end
$if !no_bounds_checking? {
if start > end { if start > end {
panic('array.slice: invalid slice index ($start > $end)') panic('array.slice: invalid slice index ($start > $end)')
} }
@ -215,6 +230,7 @@ fn (a array) slice(start, _end int) array {
if start < 0 { if start < 0 {
panic('array.slice: slice bounds out of range ($start < 0)') panic('array.slice: slice bounds out of range ($start < 0)')
} }
}
l := end - start l := end - start
res := array{ res := array{
element_size: a.element_size element_size: a.element_size
@ -249,6 +265,7 @@ pub fn (a array) clone() array {
fn (a array) slice_clone(start, _end int) array { fn (a array) slice_clone(start, _end int) array {
mut end := _end mut end := _end
$if !no_bounds_checking? {
if start > end { if start > end {
panic('array.slice: invalid slice index ($start > $end)') panic('array.slice: invalid slice index ($start > $end)')
} }
@ -258,6 +275,7 @@ fn (a array) slice_clone(start, _end int) array {
if start < 0 { if start < 0 {
panic('array.slice: slice bounds out of range ($start < 0)') panic('array.slice: slice bounds out of range ($start < 0)')
} }
}
l := end - start l := end - start
res := array{ res := array{
element_size: a.element_size element_size: a.element_size
@ -270,9 +288,11 @@ fn (a array) slice_clone(start, _end int) array {
// Private function. Used to implement assigment to the array element. // Private function. Used to implement assigment to the array element.
fn (a mut array) set(i int, val voidptr) { fn (a mut array) set(i int, val voidptr) {
$if !no_bounds_checking? {
if i < 0 || i >= a.len { if i < 0 || i >= a.len {
panic('array.set: index out of range (i == $i, a.len == $a.len)') panic('array.set: index out of range (i == $i, a.len == $a.len)')
} }
}
C.memcpy(a.data + a.element_size * i, val, a.element_size) C.memcpy(a.data + a.element_size * i, val, a.element_size)
} }

View File

@ -33,20 +33,25 @@ pub fn (x f64) strlong() string {
return tmpstr return tmpstr
} }
[inline]
fn f32_abs(a f32) f32 { fn f32_abs(a f32) f32 {
return if a < 0 { -a } else { a } return if a < 0 { -a } else { a }
} }
[inline]
fn f64_abs(a f64) f64 { fn f64_abs(a f64) f64 {
return if a < 0 { -a } else { a } return if a < 0 { -a } else { a }
} }
// compare floats using C epsilon // compare floats using C epsilon
// == // ==
[inline]
pub fn (a f64) eq(b f64) bool { pub fn (a f64) eq(b f64) bool {
return f64_abs(a - b) <= C.DBL_EPSILON return f64_abs(a - b) <= C.DBL_EPSILON
} }
[inline]
pub fn (a f32) eq(b f32) bool { pub fn (a f32) eq(b f32) bool {
return f32_abs(a - b) <= C.FLT_EPSILON return f32_abs(a - b) <= C.FLT_EPSILON
} }

View File

@ -518,9 +518,11 @@ fn (s string) substr2(start, _end int, end_max bool) string {
} }
pub fn (s string) substr(start, end int) string { pub fn (s string) substr(start, end int) string {
$if !no_bounds_checking? {
if start > end || start > s.len || end > s.len || start < 0 || end < 0 { if start > end || start > s.len || end > s.len || start < 0 || end < 0 {
panic('substr($start, $end) out of bounds (len=$s.len)') panic('substr($start, $end) out of bounds (len=$s.len)')
} }
}
len := end - start len := end - start
mut res := string{ mut res := string{
len: len len: len
@ -1043,9 +1045,11 @@ pub fn (u ustring) count(substr ustring) int {
} }
pub fn (u ustring) substr(_start, _end int) string { pub fn (u ustring) substr(_start, _end int) string {
$if !no_bounds_checking? {
if _start > _end || _start > u.len || _end > u.len || _start < 0 || _end < 0 { if _start > _end || _start > u.len || _end > u.len || _start < 0 || _end < 0 {
panic('substr($_start, $_end) out of bounds (len=$u.len)') panic('substr($_start, $_end) out of bounds (len=$u.len)')
} }
}
end := if _end >= u.len { u.s.len } else { u.runes[_end] } end := if _end >= u.len { u.s.len } else { u.runes[_end] }
return u.s.substr(u.runes[_start], end) return u.s.substr(u.runes[_start], end)
} }
@ -1065,16 +1069,20 @@ pub fn (u ustring) right(pos int) string {
} }
fn (s string) at(idx int) byte { fn (s string) at(idx int) byte {
$if !no_bounds_checking? {
if idx < 0 || idx >= s.len { if idx < 0 || idx >= s.len {
panic('string index out of range: $idx / $s.len') panic('string index out of range: $idx / $s.len')
} }
}
return s.str[idx] return s.str[idx]
} }
pub fn (u ustring) at(idx int) string { pub fn (u ustring) at(idx int) string {
$if !no_bounds_checking? {
if idx < 0 || idx >= u.len { if idx < 0 || idx >= u.len {
panic('string index out of range: $idx / $u.runes.len') panic('string index out of range: $idx / $u.runes.len')
} }
}
return u.substr(idx, idx + 1) return u.substr(idx, idx + 1)
} }

View File

@ -140,6 +140,25 @@ $c_common_macros
#define DEFAULT_LE(a, b) (a <= b) #define DEFAULT_LE(a, b) (a <= b)
#define DEFAULT_GT(a, b) (a > b) #define DEFAULT_GT(a, b) (a > b)
#define DEFAULT_GE(a, b) (a >= b) #define DEFAULT_GE(a, b) (a >= b)
// NB: macro_fXX_eq and macro_fXX_ne are NOT used
// in the generated C code. They are here just for
// completeness/testing.
#define macro_f64_eq(a, b) (a == b)
#define macro_f64_ne(a, b) (a != b)
#define macro_f64_lt(a, b) (a < b)
#define macro_f64_le(a, b) (a <= b)
#define macro_f64_gt(a, b) (a > b)
#define macro_f64_ge(a, b) (a >= b)
#define macro_f32_eq(a, b) (a == b)
#define macro_f32_ne(a, b) (a != b)
#define macro_f32_lt(a, b) (a < b)
#define macro_f32_le(a, b) (a <= b)
#define macro_f32_gt(a, b) (a > b)
#define macro_f32_ge(a, b) (a >= b)
//================================== GLOBALS =================================*/ //================================== GLOBALS =================================*/
byte g_str_buf[1024]; byte g_str_buf[1024];
int load_so(byteptr); int load_so(byteptr);

View File

@ -217,6 +217,10 @@ fn (p mut Parser) bterm() string {
if is_float && p.cur_fn.name != 'f32_abs' && p.cur_fn.name != 'f64_abs' { if is_float && p.cur_fn.name != 'f32_abs' && p.cur_fn.name != 'f64_abs' {
p.gen(')') p.gen(')')
match tok { match tok {
// NB: For more precision/stability, the == and != float
// comparisons are done with V functions that use the epsilon
// constants for the given type.
// Everything else uses native comparisons (C macros) for speed.
.eq { .eq {
p.cgen.set_placeholder(ph, '${expr_type}_eq(') p.cgen.set_placeholder(ph, '${expr_type}_eq(')
} }
@ -224,16 +228,16 @@ fn (p mut Parser) bterm() string {
p.cgen.set_placeholder(ph, '${expr_type}_ne(') p.cgen.set_placeholder(ph, '${expr_type}_ne(')
} }
.le { .le {
p.cgen.set_placeholder(ph, '${expr_type}_le(') p.cgen.set_placeholder(ph, 'macro_${expr_type}_le(')
} }
.ge { .ge {
p.cgen.set_placeholder(ph, '${expr_type}_ge(') p.cgen.set_placeholder(ph, 'macro_${expr_type}_ge(')
} }
.gt { .gt {
p.cgen.set_placeholder(ph, '${expr_type}_gt(') p.cgen.set_placeholder(ph, 'macro_${expr_type}_gt(')
} }
.lt { .lt {
p.cgen.set_placeholder(ph, '${expr_type}_lt(') p.cgen.set_placeholder(ph, 'macro_${expr_type}_lt(')
} }
else { else {
}} }}