181 lines
5.4 KiB
Scheme
181 lines
5.4 KiB
Scheme
;;; 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<? d dist)
|
|
(loop (cdr lst) s h d)
|
|
(loop (cdr lst) surface hit dist)))
|
|
(loop (cdr lst) surface hit dist)))))))
|
|
|
|
(define (lambert s int ray)
|
|
(let ((n (normal s int)))
|
|
(flmax 0.0
|
|
(fl+ (fl* (point-x ray) (point-x n))
|
|
(fl* (point-y ray) (point-y n))
|
|
(fl* (point-z ray) (point-z n))))))
|
|
|
|
(define (make-sphere color radius center)
|
|
(vector color radius center))
|
|
|
|
(define (sphere-color s) (vector-ref s 0))
|
|
(define (sphere-radius s) (vector-ref s 1))
|
|
(define (sphere-center s) (vector-ref s 2))
|
|
|
|
(define (defsphere x y z r c)
|
|
(let ((s (make-sphere c r (make-point x y z))))
|
|
(set! *world* (cons s *world*))
|
|
s))
|
|
|
|
(define (surface-color s)
|
|
(sphere-color s))
|
|
|
|
(define (intersect s pt ray)
|
|
(sphere-intersect s pt ray))
|
|
|
|
(define (sphere-intersect s pt ray)
|
|
(let* ((xr (point-x ray))
|
|
(yr (point-y ray))
|
|
(zr (point-z ray))
|
|
(c (sphere-center s))
|
|
(n (minroot
|
|
(fl+ (sq xr) (sq yr) (sq zr))
|
|
(fl* 2.0
|
|
(fl+ (fl* (fl- (point-x pt) (point-x c)) xr)
|
|
(fl* (fl- (point-y pt) (point-y c)) yr)
|
|
(fl* (fl- (point-z pt) (point-z c)) zr)))
|
|
(fl+ (sq (fl- (point-x pt) (point-x c)))
|
|
(sq (fl- (point-y pt) (point-y c)))
|
|
(sq (fl- (point-z pt) (point-z c)))
|
|
(fl- (sq (sphere-radius s)))))))
|
|
(if n
|
|
(make-point (fl+ (point-x pt) (fl* n xr))
|
|
(fl+ (point-y pt) (fl* n yr))
|
|
(fl+ (point-z pt) (fl* n zr)))
|
|
#f)))
|
|
|
|
(define (normal s pt)
|
|
(sphere-normal s pt))
|
|
|
|
(define (sphere-normal s pt)
|
|
(let ((c (sphere-center s)))
|
|
(unit-vector (fl- (point-x c) (point-x pt))
|
|
(fl- (point-y c) (point-y pt))
|
|
(fl- (point-z c) (point-z pt)))))
|
|
|
|
(define (ray-test . opt)
|
|
(set! *world* '())
|
|
(defsphere 0.0 -300.0 -1200.0 200.0 0.8)
|
|
(defsphere -80.0 -150.0 -1200.0 200.0 0.7)
|
|
(defsphere 70.0 -100.0 -1200.0 200.0 0.9)
|
|
(do ((x -2 (+ x 1)))
|
|
((> 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))))))
|