8add369e |
(in-package :fwoar.lisp-sandbox.1)
|
96842cff |
(defclass hittable ()
())
|
8add369e |
|
96842cff |
(fw.lu:defclass+ sphere (hittable)
((center :initarg :center)
(radius :initarg :radius)
(material-color :initarg :color
:reader material-color
:initform (vec3 (random 1.0d0)
(random 1.0d0)
(random 1.0d0)))
(material :initarg :material
:reader .material
:initform (lambertian-material (random 0.8)))))
(fw.lu:defclass+ ray ()
((origin :initarg :origin)
(direction :initarg :direction)))
(fw.lu:defclass+ hit-record ()
((p :initarg :p :reader .p)
(time :initarg :time :reader .time)
(thing :initarg :thing :accessor .thing)
(material :initarg :material :accessor .material)
(normal :initarg :normal :accessor .normal :initform ())
(front-face :initarg :front-face :accessor .front-face :initform ())))
|
8bf41476 |
|
96842cff |
(defclass camera ()
((origin :initarg :origin :reader origin)
(lower-left-corner :initarg :lower-left-corner :reader lower-left-corner)
(horizontal :initarg :horizontal :reader horizontal)
(vertical :initarg :vertical :reader vertical)))
|
8bf41476 |
|
8add369e |
(defstruct (vec3 (:type vector)
(:constructor vec3 (x y z))
(:conc-name v3-))
x y z)
(defun call-with-ppm-header (stream size callback &optional (colors 255))
(format stream "P3~%~d ~d~%~d~%"
(size-width size)
(size-height size)
colors)
(funcall callback stream))
#.(progn (defmacro ig (&rest syms) `'(declare (ignore ,@syms)))
nil)
|
38ff817c |
(defun format-color (s v _ __)
#.(ig _ __)
(fw.lu:vector-destructuring-bind (r g b) (scale-to-8bit v)
(format s "~4d ~4d ~4d" r g b)))
|
8add369e |
(defun write-colors (stream colors columns)
|
38ff817c |
(funcall colors
(lambda (color pos)
(fwoar.lisp-sandbox.canvas-server:send-update
(scale-to-8bit color) pos)))
#+(or)
|
8add369e |
(let ((intermediate ())
(idx 0))
(funcall colors
(lambda (color)
(push color intermediate)
(when (= (1- columns)
(mod idx columns))
(format stream "~{~/fwoar.lisp-sandbox.1::format-color/~^ ~}~&" intermediate)
(setf intermediate ()))
(incf idx)))
(when intermediate
(format stream "~{~/fwoar.lisp-sandbox.1::format-color/~^ ~}~&" intermediate))))
(defgeneric at (self it)
(:method ((ray ray) (it number))
(with-slots (origin direction) ray
(vec+ origin
(vec* it direction)))))
(defun set-face-normal (hit-record r outward-normal)
(prog1 hit-record
(with-slots (direction) r
(let ((front-face (< (dot direction outward-normal)
0)))
(setf (.front-face hit-record) front-face
(.normal hit-record) (if front-face
outward-normal
(vec* -1 outward-normal)))))))
(defgeneric hit (thing ray t-min t-max)
(:method ((things list) (r ray) (t-min real) (t-max real))
(let (temp-rec
(hit-anything nil)
(closest-so-far t-max))
(loop for thing in things
for (hit-p hit-rec) = (multiple-value-list
(hit thing r t-min closest-so-far))
when hit-p do
(setf hit-anything t
closest-so-far (.time hit-rec)
temp-rec hit-rec))
(when hit-anything
(values hit-anything
temp-rec))))
(:method ((sphere sphere) (r ray) (t-min real) (t-max real))
|
38ff817c |
(declare (optimize (speed 3)))
|
8add369e |
(uiop:nest (with-slots ((%center center) (%radius radius)) sphere)
(let ((center %center) (radius %radius)))
(with-slots ((%origin origin) (%direction direction)) r)
(let ((origin %origin) (direction %direction)))
(let* ((oc (vec- origin center))
(a (length-squared direction))
(half-b (dot oc direction))
(c (- (length-squared oc)
(* radius radius)))
(discriminant (- (* half-b half-b)
(* a c))))
(if (< discriminant 0)
(return-from hit nil)
(let* ((sqrtd (sqrt discriminant))
(root (/ (- (- half-b)
sqrtd)
a)))
(when (or (< root t-min)
(< t-max root))
(setf root (/ (- sqrtd half-b)
a))
(when (or (< root t-min)
(< t-max root))
(return-from hit nil)))
(let* ((p (at r root))
(outward-normal (vec/ (vec- p center)
radius)))
(values t
|
38ff817c |
(set-face-normal (hit-record p root sphere (.material sphere))
|
8add369e |
r
outward-normal)))))))))
(defun hit-sphere (center radius r)
(with-slots (origin direction) r
(let* ((oc (vec- origin center))
(a (length-squared direction))
(half-b (dot oc direction))
(c (- (length-squared oc)
(expt radius 2)))
(discriminant (- (* half-b half-b)
(* a c))))
(if (< discriminant 0)
-1.0d0
(/ (- (- half-b) (sqrt discriminant))
a)))))
|
38ff817c |
(defgeneric scatter (material ray-in rec))
|
9c02b840 |
(defun reflect (v n)
(vec- v
(vec* 2.0d0
(vec* (dot v n)
n))))
(defun refract (uv n eta*/eta)
(let* ((cos-theta (min 1.0d0
(dot (negate uv)
n)))
(out-perp (vec* eta*/eta
(vec+ uv
(vec* cos-theta
n))))
(out-parallel (vec* (- (sqrt (abs (- 1.0d0
(length-squared out-perp)))))
n)))
(vec+ out-perp out-parallel)))
(defun reflectance (cosine ref-idx)
(let* ((r0 (/ (- 1 ref-idx)
(+ 1 ref-idx)))
(r0 (* r0 r0)))
(+ r0
(* (- 1 r0)
(expt (- 1 cosine)
5)))))
|
96842cff |
(define-symbol-macro infinity
#.sb-ext:double-float-positive-infinity)
|
8bf41476 |
(defgeneric ray-color (ray world depth)
(:method :around (r w (depth integer))
(if (<= depth 0)
(vec3 1.0d0 1.0d0 1.0d0)
(call-next-method)))
(:method ((ray ray) world (depth integer))
|
8add369e |
(multiple-value-bind (hit-p rec)
|
8bf41476 |
(hit world ray 0.001d0 infinity)
|
8add369e |
(when hit-p
|
38ff817c |
(return-from ray-color
|
9c02b840 |
(funcall (.material rec) rec ray world depth)))
|
8add369e |
(with-slots (direction) ray
(let* ((unit-direction (unit-vector direction))
(it (+ (* 0.5 (v3-y unit-direction))
1.0d0)))
(vec+ (vec* (- 1.0d0 it)
#(1.0d0 1.0d0 1.0d0))
(vec* it
#(0.5d0 0.7d0 1.0d0))))))))
|
8bf41476 |
(defun camera (&key
(aspect-ratio 16/9)
|
9c02b840 |
(viewport-height 2.0d0)
|
8bf41476 |
(viewport-width (* aspect-ratio viewport-height))
|
9c02b840 |
(focal-length 1.0d0))
|
8bf41476 |
(let ((origin (vec3 0d0 0.0d0 0.0d0))
(horizontal (vec3 viewport-width 0.0d0 0.0d0))
(vertical (vec3 0.0d0 viewport-height 0.0d0)))
(make-instance 'camera
:origin origin
:horizontal horizontal
:vertical vertical
:lower-left-corner (vec- (vec- (vec- origin
(vec/ horizontal 2))
(vec/ vertical 2))
(vec3 0 0 focal-length)))))
(defgeneric get-ray (camera u v)
(:method ((camera camera) (u real) (v real))
(with-slots (origin horizontal vertical lower-left-corner) camera
(macrolet ((-> (v &body forms)
(if forms
`(-> (,(caar forms) ,v ,@(cdar forms))
,@(cdr forms))
v)))
(ray origin
(-> lower-left-corner
(vec+ (vec* u horizontal))
(vec+ (vec* v vertical))
(vec- origin)))))))
|
38ff817c |
(declaim (notinline u-loop))
(defun u-loop (j image-width image-height camera world max-depth c)
(loop for i from 0 below image-width
for u = (/ (* 1.0d0 i)
(1- image-width))
for v = (/ (* 1.0d0 j)
(1- image-height))
for r = (get-ray camera u v)
for color = (loop for s below *samples-per-pixel*
for u = (/ i
(1- image-width))
then (/ (+ i (random 1.0d0))
(1- image-width))
for v = (/ j
(1- image-height))
then (/ (+ j (random 1.0d0))
(1- image-height))
for r = (get-ray camera u v)
for pixel-color = (ray-color r world max-depth)
then (vec+ pixel-color
(ray-color r world max-depth))
finally (return pixel-color))
do (funcall c color
(list i
(- image-height j)))))
(defvar *thread-queues*
(make-array 8))
(defun start-worker (id)
(let ((mailbox (sb-concurrency:make-mailbox :name (format nil "mailbox-~d" id))))
(setf (aref *thread-queues* id) mailbox)
(bt:make-thread
(lambda ()
(loop
(destructuring-bind (*samples-per-pixel*
j image-width image-height camera world max-depth c)
(sb-concurrency:receive-message mailbox)
(u-loop j image-width image-height camera world max-depth c))))
:name (format nil "worker-~d" id))))
|
9c02b840 |
(defun start-workers (&optional (n 8))
(loop for x below n
|
38ff817c |
collect (start-worker x)))
(defun shuffle (seq)
(let ((arr (map 'vector 'identity seq)))
(loop for i from (1- (length arr)) downto 1
for j = (random i)
do (rotatef (aref arr i)
(aref arr j))
finally (return (coerce arr (type-of seq))))))
|
6b17e8ac |
#+(or)
(progn (let ((it (map 'list
'sb-concurrency:mailbox-count
*thread-queues*)))
(list it (apply '+ it)))
)
|