Schemeでもパストレース

next : http://d.hatena.ne.jp/mjt/20090209/p1

の特に工夫も無いベタ移植。Ypsilonで30分ほど。

#!r6rs
; based on http://lucille.atso-net.jp/rwp/pt/
(import (rnrs) (srfi :8) (srfi :27))

(define PI 3.141592)

(define (vec x y z)
  (let ((v (make-bytevector 24)))
    (bytevector-ieee-double-native-set! v 0 x)
    (bytevector-ieee-double-native-set! v 8 y)
    (bytevector-ieee-double-native-set! v 16 z) v))
(define (vx x)
  (bytevector-ieee-double-native-ref x 0))
(define (vy x)
  (bytevector-ieee-double-native-ref x 8))
(define (vz x)
  (bytevector-ieee-double-native-ref x 16))
(define (vadd a b)
  (vec (+ (vx a) (vx b)) (+ (vy a) (vy b)) (+ (vz a) (vz b))))
(define (vsub a b)
  (vec (- (vx a) (vx b)) (- (vy a) (vy b)) (- (vz a) (vz b))))
(define (vcross a b)
  (vec
    (- (* (vy a) (vz b)) (* (vy b) (vz a)))
    (- (* (vz a) (vx b)) (* (vz b) (vx a)))
    (- (* (vx a) (vy b)) (* (vx b) (vy a)))))
(define (vnormalize a)
  (let ((len (vlen a)))
    (if (> (abs len) 1.0e-6)
      (let ((invlen (/ 1.0 len)))
	(vec 
	  (* (vx a) invlen)
	  (* (vy a) invlen)
	  (* (vz a) invlen))))))
(define (square a)
  (* a a))
(define (vlen a)
  (sqrt (+ (square (vx a)) (square (vy a)) (square (vz a)))))
(define (vdot a b)
  (+ (* (vx a) (vx b)) (* (vy a) (vy b)) (* (vz a) (vz b))))
(define (vneg a)
  (vec (- 0.0 (vx a)) (- 0.0 (vy a)) (- 0.0 (vz a))))
(define (vscale v x)
  (vec (* x (vx v)) (* x (vy v)) (* x (vz v))))

(define org car)
(define dir cdr)
(define new-ray cons)

;R6RS record version
(define-record-type 
  (intersection make-intersection intersection?)
  (fields
    (mutable t is-t is-t-set!)
    (mutable p is-p is-p-set!)
    (mutable n is-n is-n-set!)
    (mutable col is-col is-col-set!)
    (mutable emissiveCol is-emissiveCol is-emissiveCol-set!)
    (mutable hit is-hit is-hit-set!))
  (protocol
    (lambda (c)
      (lambda ()
	(c 1.0e+30 (vec 0.0 0.0 0.0) (vec 0.0 0.0 0.0) (vec 0.0 0.0 0.0) (vec 0.0 0.0 0.0) #f)))))

(define (Sphere center radius col emissiveCol)
  (lambda (isect ray)
    (let* ((rs (vsub (org ray) center))
	   (B (vdot rs (dir ray)))
	   (C (- (vdot rs rs) (square radius)))
	   (D (- (square B) C)))
      (if (> D 0.0)
	(let ((t (- 0.0 B (sqrt D))))
	  (if (< 0.0 t (is-t isect))
	    (begin
	    (is-t-set! isect t)
	    (is-hit-set! isect #t)
	    (let ((p (vec 
		       (+ (vx (org ray)) (* (vx (dir ray)) t))
		       (+ (vy (org ray)) (* (vy (dir ray)) t))
		       (+ (vz (org ray)) (* (vz (dir ray)) t)))))
	      (is-n-set! isect (vnormalize (vsub p center)))
	      (is-p-set! isect p))
	    (is-col-set! isect col)
	    (is-emissiveCol-set! isect emissiveCol))))))))

(define (Plane p n col emissiveCol)
  (lambda (isect ray)
    (let ((d (- 0.0 (vdot p n)))
	  (v (vdot (dir ray) n)))
      (if (> (abs v) 1.0e-6)
	(let ((t (/ (- 0.0 (+ (vdot (org ray) n) d)) v)))
	  (if (< 0.0 t (is-t isect))
	    (begin
	    (is-hit-set! isect #t)
	    (is-t-set! isect t)
	    (is-n-set! isect n)
	    (is-p-set! isect (vec
			       (+ (vx (org ray)) (* (vx (dir ray)) t))
			       (+ (vy (org ray)) (* (vy (dir ray)) t))
			       (+ (vz (org ray)) (* (vz (dir ray)) t))))
	    (is-col-set! isect col)
	    (is-emissiveCol-set! isect emissiveCol))))))))

; pt

(define IMAGE_WIDTH 256)
(define IMAGE_HEIGHT 256)
(define NSUBSAMPLES 2)
(define NPATH_SAMPLES 128)
(define MAX_TRACE_DEPTH 16)

(define (main)
  (let ((myport (open-file-output-port "out.rgb")))
  ;(let ()
  (define (line-loop y)
    (cond
      ((= y IMAGE_HEIGHT)
       (display "DONE")(newline))
      (else
	(let ((w (render IMAGE_WIDTH IMAGE_HEIGHT y NSUBSAMPLES)))
	  (display "LINE")(display y)(display " - ")(display (bytevector-length w))(newline)
	  (put-bytevector myport w))
	(line-loop (+ y 1)))))
  (line-loop 0)(close-port myport)))

(define objects
  (list
    (Sphere (vec -1.05 0.0 -2.0) 0.5 (vec 0.75 0.0 0.0) (vec 0.0 0.0 0.0))
    (Sphere (vec 0.0 0.0 -2.0) 0.5 (vec 1.0 1.0 1.0) (vec 1.0 1.0 1.0))
    (Sphere (vec 1.05 0.0 -2.0) 0.5 (vec 0.0 0.0 1.0) (vec 0.0 0.0 0.0))
    (Plane (vec 0.0 -0.5 0.0) (vec 0.0 1.0 0.0) (vec 1.0 1.0 1.0) (vec 0.0 0.0 0.0))))

(define (clamp f)
  (let ((i (exact (floor (* f 255.5)))))
    (cond
      ((< i 0) 0)
      ((> i 255) 255)
      (else i))))

(define (orthoBasis n)
  (let* ((v
	  (cond 
	    ((< -0.6 (vx n) 0.6)
	     (vec 1.0 0.0 0.0))
	    ((< -0.6 (vy n) 0.6)
	     (vec 0.0 1.0 0.0))
	    ((< -0.6 (vz n) 0.6)
	     (vec 0.0 0.0 1.0))
	    (else (vec 1.0 0.0 0.0))))
	(s (vnormalize (vcross v n))))
    (values
      s
      (vnormalize (vcross n s))
      n)))

(define (trace ray depth)
  (define (find-nearest obj isect ray)
    (cond
      ((pair? obj) 
       ((car obj) isect ray)
       (find-nearest (cdr obj) isect ray))
      (else isect)))
  (let ((isect (find-nearest objects (make-intersection) ray)))
  (if (is-hit isect)
    ;true - stage2
    (let ((r (random-real)) (phi (* 2.0 PI (random-real))))
      (let 
	((x (* (cos phi) (sqrt (- 1.0 r))))
	 (y (* (sin phi) (sqrt (- 1.0 r))))
	 (z (sqrt r)))
	(receive (b0 b1 b2) (orthoBasis (is-n isect))
	  (let*
	    ((newDir (vec
		       (+ (* x (vx b0)) (* y (vx b1)) (* z (vx b2)))
		       (+ (* x (vy b0)) (* y (vy b1)) (* z (vy b2)))
		       (+ (* x (vz b0)) (* y (vz b1)) (* z (vz b2)))))
	     (eps 0.00001)
	     (newP (vec (+ (vx (is-p isect)) (* eps (vx newDir)))
			(+ (vy (is-p isect)) (* eps (vy newDir))) 
			(+ (vz (is-p isect)) (* eps (vz newDir)))
		     ))
	     (newRay (new-ray newP newDir))
	     (fr (vscale (is-col isect) (/ 1.0 PI)))
	     (cosTheta (vdot newDir (is-n isect)))
	     (Li (trace newRay (+ depth 1)))
	     (Le (is-emissiveCol isect)))
	    ;return
	    (vec (+ (vx Le) (* PI (vx fr) (vx Li))) 
		 (+ (vy Le) (* PI (vy fr) (vy Li))) 
		 (+ (vz Le) (* PI (vz fr) (vz Li))))))))
    (vec 0.7 0.7 0.7) ;false
    )))

(define (pathTrace ray)
  (define (pathTrace-itr num col ray)
    (if (< num NPATH_SAMPLES)
      (pathTrace-itr (+ num 1) (vadd col (trace ray 0)) ray)
      (vscale col (/ 1.0 NPATH_SAMPLES))))
  (pathTrace-itr 0 (vec 0.0 0.0 0.0) ray))

(define (render width height y nsubsamples)
  (let ((img (make-bytevector (* width 4))))
    (define (render-sample-loop i u v sumr sumg sumb)
      (cond
	((= v nsubsamples) (values sumr sumg sumb))
	((= u nsubsamples) (render-sample-loop i 0 (+ v 1) sumr sumg sumb))
	(else
	  (let ((px (/ (+ i (- (/ u nsubsamples) (/ width 2.0))) (/ width 2.0)))
		(py (- 0.0 (/ (+ y (- (/ v nsubsamples) (/ height 2.0))) (/ height 2.0)))))
	    (let ((col (pathTrace (new-ray (vec 0.0 0.0 0.0) (vnormalize (vec px py -1.0))))))
	      (render-sample-loop i (+ u 1) v (+ sumr (vx col)) (+ sumg (vy col)) (+ sumb (vz col))))))))

    (define (render-pixel-loop i)
      (if (= i width)
	img
	(begin
	  (receive (sumr sumg sumb) (render-sample-loop i 0 0 0.0 0.0 0.0)
		   (bytevector-u8-set! img (+ (* i 4) 0) (clamp (/ sumr (square nsubsamples))))
		   (bytevector-u8-set! img (+ (* i 4) 1) (clamp (/ sumg (square nsubsamples))))
		   (bytevector-u8-set! img (+ (* i 4) 2) (clamp (/ sumb (square nsubsamples)))))
	  (render-pixel-loop (+ i 1))))) (render-pixel-loop 0)))

(main)

ついでにPNG変換。こちらはmosh専用。

(import (rnrs) (mosh ffi))

(define (convimage port)
  (define (convimage-loop bv idx port)
    (cond
      ((= idx (* 256 256)) bv)
      (else
	;(bytevector-u32-native-set! bv idx (bytevector-u32-ref (get-bytevector-n port 4) 0 (endianness big)))
	;            ^causes something wrong ..
	(bytevector-u8-set! bv (+ 3 (* idx 4)) 255)
	(bytevector-u8-set! bv (+ 2 (* idx 4)) (get-u8 port))
	(bytevector-u8-set! bv (+ 1 (* idx 4)) (get-u8 port))
	(bytevector-u8-set! bv (+ 0 (* idx 4)) (get-u8 port))
	(get-u8 port)
	(convimage-loop bv (+ 1 idx) port))))
  (convimage-loop (make-bytevector (* 256 256 4)) 0 port))

(let* ((libcairo (open-shared-library "libcairo.so"))
       (cairo-surface-write-to-png (c-function libcairo int cairo_surface_write_to_png void* char*))
       (cairo-image-surface-create-for-data (c-function libcairo void* cairo_image_surface_create_for_data char* int int int int)))
  (let ((myport (open-file-input-port "out.rgb")))
    (cairo-surface-write-to-png
	 (cairo-image-surface-create-for-data 
		(convimage myport)
		0 ; ARGB32
		256
		256
		(* 256 4)) "test.png")))