;;; RAY -- Ray-trace a simple scene with spheres, generating a ".pgm" file. ;;; Translated to Scheme from Paul Graham's book ANSI Common Lisp, Example 9.8 (library (rnrs-benchmarks ray) (export main) (import (rnrs) (rnrs r5rs) (rnrs arithmetic flonums) (rnrs-benchmarks)) (define (make-point x y z) (vector x y z)) (define (point-x p) (vector-ref p 0)) (define (point-y p) (vector-ref p 1)) (define (point-z p) (vector-ref p 2)) (define (sq x) (fl* x x)) (define (mag x y z) (flsqrt (fl+ (sq x) (sq y) (sq z)))) (define (unit-vector x y z) (let ((d (mag x y z))) (make-point (fl/ x d) (fl/ y d) (fl/ z d)))) (define (distance p1 p2) (mag (fl- (point-x p1) (point-x p2)) (fl- (point-y p1) (point-y p2)) (fl- (point-z p1) (point-z p2)))) (define (minroot a b c) (if (flzero? a) (fl/ (fl- c) b) (let ((disc (fl- (sq b) (fl* 4.0 a c)))) (if (flnegative? disc) #f (let ((discrt (flsqrt disc)) (minus-b (fl- b)) (two-a (fl* 2.0 a))) (flmin (fl/ (fl+ minus-b discrt) two-a) (fl/ (fl- minus-b discrt) two-a))))))) (define *world* '()) (define eye (make-point 0.0 0.0 200.0)) (define (tracer pathname res) (call-with-output-file/truncate pathname (lambda (p) (let ((extent (* res 100))) (display "P2 " p) (write extent p) (display " " p) (write extent p) (display " 255" p) (newline p) (do ((y 0 (+ y 1))) ((= y extent)) (do ((x 0 (+ x 1))) ((= x extent)) (write (color-at (fl+ -50.0 (fl/ (exact->inexact x) (exact->inexact res))) (fl+ -50.0 (fl/ (exact->inexact y) (exact->inexact res)))) p) (newline p))))))) (define (color-at x y) (let ((ray (unit-vector (fl- x (point-x eye)) (fl- y (point-y eye)) (fl- (point-z eye))))) (inexact->exact (flround (fl* (sendray eye ray) 255.0))))) (define (sendray pt ray) (let* ((x (first-hit pt ray)) (s (vector-ref x 0)) (int (vector-ref x 1))) (if s (fl* (lambert s int ray) (surface-color s)) 0.0))) (define (first-hit pt ray) (let loop ((lst *world*) (surface #f) (hit #f) (dist 1e308)) (if (null? lst) (vector surface hit) (let ((s (car lst))) (let ((h (intersect s pt ray))) (if h (let ((d (distance h pt))) (if (fl x 2)) (do ((z 2 (+ z 1))) ((> z 7)) (defsphere (fl* (exact->inexact x) 200.0) 300.0 (fl* (exact->inexact z) -400.0) 40.0 0.75))) (tracer "spheres.pgm" (if (null? opt) 1 (car opt)))) (define (run) (ray-test 1) 'ok) (define (main . args) (run-benchmark "ray" ray-iters (lambda (result) (equal? result 'ok)) (lambda () (lambda () (run))))))