racket/collects/images/private/deep-flomap-render.rkt

507 lines
23 KiB
Racket
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang typed/racket/base
(require racket/match racket/math
"flonum.rkt"
"flomap.rkt"
"deep-flomap-struct.rkt"
"deep-flomap-parameters.rkt")
(provide deep-flomap-render)
;; Hacks
(define specular-blur 1/2)
(define diffuse-blur 1/2)
(define ambient-transmission-blur-fraction 1/32)
;; ===================================================================================================
;; Ray tracing ops
;; assumes direction to viewer is 0.0 0.0 1.0 (i.e. viewer above at infinity)
(: reflect-view-ray (Flonum Flonum Flonum -> (values Flonum Flonum Flonum)))
(define (reflect-view-ray nx ny nz)
(values (* 2.0 (* nz nx))
(* 2.0 (* nz ny))
(- (* 2.0 (* nz nz)) 1.0)))
;; calculates intensity of transmitted rays using Fresnel's equation
(: transmission-intensity (Flonum Flonum Flonum -> Flonum))
(define (transmission-intensity cos-i η1 η2)
(define n1/n2 (/ η1 η2))
(define cos^2-i (* cos-i cos-i))
(define sin^2-t (* (* n1/n2 n1/n2) (- 1.0 cos^2-i)))
(define cos-t (flsqrt (- 1.0 sin^2-t)))
(define n1-cos-i (* η1 cos-i))
(define n2-cos-t (* η2 cos-t))
(define n1-cos-t (* η1 cos-t))
(define n2-cos-i (* η2 cos-i))
(define perp (/ (- n1-cos-i n2-cos-t)
(+ n1-cos-i n2-cos-t)))
(define parl (/ (- n2-cos-i n1-cos-t)
(+ n2-cos-i n1-cos-t)))
(- 1.0 (* 0.5 (+ (* perp perp) (* parl parl)))))
(: transmitted-vector (Flonum Flonum Flonum Flonum Flonum Flonum Flonum Flonum
-> (values Flonum Flonum Flonum)))
(define (transmitted-vector nx ny nz ix iy iz η1 η2)
(define η1/η2 (/ η1 η2))
(define cos-i (- (fl3dot nx ny nz ix iy iz)))
(define cos^2-i (* cos-i cos-i))
(define sin^2-t (* (* η1/η2 η1/η2) (- 1.0 cos^2-i)))
(define c (- (* η1/η2 cos-i) (flsqrt (- 1.0 sin^2-t))))
(define-values (tx1 ty1 tz1) (fl3* ix iy iz η1/η2))
(define-values (tx2 ty2 tz2) (fl3* nx ny nz c))
(fl3+ tx1 ty1 tz1 tx2 ty2 tz2))
(: absorb-intensity (Flonum Flonum -> Flonum))
(define (absorb-intensity opacity dist)
(let* ([o (+ (* opacity 0.99) 0.005)])
(cond [(o . = . 0.0) 0.0]
[else (exp (* (fllog o) dist))])))
(: beckmann-distribution (Flonum Flonum -> Flonum))
(define (beckmann-distribution cos-θ m)
(define x (/ (tan (acos cos-θ)) m))
(define m*cos^2-θ (* m cos-θ cos-θ))
(/ (exp (- (* x x))) (* pi m*cos^2-θ m*cos^2-θ)))
;; ===================================================================================================
;; Pass 1: tracing from a directional light source
(: trace-directional-light (flomap flomap flomap flomap
Integer Integer Integer Integer -> (values flomap flomap)))
(define (trace-directional-light alpha-fm rgb-fm z-fm normal-fm
x-min x-max y-min y-max)
(match-define (flomap alpha-vs 1 w h) alpha-fm)
(match-define (list rgb-vs z-vs normal-vs)
(map flomap-values (list rgb-fm z-fm normal-fm)))
(define z-max (flomap-max-value z-fm))
(define opacity-z (/ z-max (transmission-density)))
;; max coordinates of the shadow image
(define sx-max (- w 1.0))
(define sy-max (- h 1.0))
;; vector pointing toward light source, incident vector, and light color
(define-values (lx ly lz) (match-let ([(list lx ly lz) (light-direction)])
(fl3normalize lx ly lz)))
(define-values (ix iy iz) (fl3- lx ly lz))
(match-define (list lr lg lb) (light-intensity))
;; view and "half" directions
(define-values (hx hy hz) (fl3-half-norm lx ly lz 0.0 0.0 1.0))
;; material properties
(define η2 (real->double-flonum (refractive-index)))
(define η1/η2 (/ 1.0 η2))
;; proportion of diffracted reflection
(define 0.5*v-dot-h (* 0.5 hz))
(define Ra (ambient-reflectance))
(define Ta (ambient-transmission))
(define Rd (diffuse-reflectance))
(define Rs (specular-reflectance))
(define Ti (ideal-transmission))
(define roughness (specular-roughness))
(define purity (specular-purity))
(match-define (list ar ag ab) (ambient-intensity))
(define-values (Tar Tag Tab) (fl3* ar ag ab Ta))
(define-values (Rar Rag Rab) (fl3* ar ag ab Ra))
(define intensity-fm (make-flomap 3 w h))
(define intensity-vs (flomap-values intensity-fm))
(define specular-fm (make-flomap 1 w h))
(define specular-vs (flomap-values specular-fm))
(define diffuse-fm (make-flomap 3 w h lz))
(define diffuse-vs (flomap-values diffuse-fm))
;(define sx-vs (make-flvector (* w h) +nan.0))
;(define sy-vs (make-flvector (* w h) +nan.0))
(define sx-fm (inline-build-flomap 1 w h (λ (k x y i) (+ (fx->fl x) 0.5))))
(define sy-fm (inline-build-flomap 1 w h (λ (k x y i) (+ (fx->fl y) 0.5))))
(define sx-vs (flomap-values sx-fm))
(define sy-vs (flomap-values sy-fm))
(define Irgb-vs (make-flvector (* 3 w h)))
(for*: ([int-y : Integer (in-range y-min y-max)]
[int-x : Integer (in-range x-min x-max)])
(define i (fx+ int-x (fx* int-y w)))
(define a (flvector-ref alpha-vs i))
(when (a . > . 0.0)
(define j (fx* 3 i))
;; altitude and surface normal
(define z (flvector-ref z-vs i))
(define nx (flvector-ref normal-vs j))
(define ny (flvector-ref normal-vs (fx+ j 1)))
(define nz (flvector-ref normal-vs (fx+ j 2)))
;; cosine of angle between light and surface normal
(define n-dot-l (fl3dot nx ny nz lx ly lz))
;; intensity of incident light (Lambert's cosine law)
(define-values (Ilr Ilg Ilb) (fl3* lr lg lb n-dot-l))
(flvector-set! intensity-vs j Ilr)
(flvector-set! intensity-vs (fx+ j 1) Ilg)
(flvector-set! intensity-vs (fx+ j 2) Ilb)
;; diffraction intensity due to specular, diffuse and ambient reflection
(cond
[(n-dot-l . > . 0.0) ; does the microfacet face the light?
(define Is
(cond
;; Cook-Torrance specular reflection intensity
[(Rs . > . 0.0)
(define n-dot-h (fl3dot nx ny nz hx hy hz))
(define n-dot-v nz)
;; geometrical attenuation factor (has something to do with local reflections)
(define G (min 1.0
(/ (* n-dot-h n-dot-v) 0.5*v-dot-h)
(/ (* n-dot-h n-dot-l) 0.5*v-dot-h)))
;; scatter distribution
(define D (beckmann-distribution n-dot-h roughness))
;; Fresnel term
(define F (- 1.0 (transmission-intensity n-dot-l 1.0 η2)))
(* Rs F (/ D n-dot-l) (/ G n-dot-v))]
[else 0.0]))
(flvector-set! specular-vs i Is)
(let*-values ([(Idr Idg Idb) (fl3* Ilr Ilg Ilb Rd)]
[(Idr Idg Idb) (fl3+ Idr Idg Idb Rar Rag Rab)])
(flvector-set! diffuse-vs j Idr)
(flvector-set! diffuse-vs (fx+ j 1) Idg)
(flvector-set! diffuse-vs (fx+ j 2) Idb))]
[else
(flvector-set! diffuse-vs j Rar)
(flvector-set! diffuse-vs (fx+ j 1) Rag)
(flvector-set! diffuse-vs (fx+ j 2) Rab)])
(when (and (Ti . > . 0.0) (n-dot-l . > . 0.0))
;; ideal transmission vector
(define-values (tx ty tz) (transmitted-vector nx ny nz ix iy iz 1.0 η2))
;; sz = z + dist * tz, so dist = (sz - z) / tz
(define dist (/ (- 0.0 z) tz))
(when (and (dist . >= . 0.0) (dist . < . +inf.0))
;; transmitted ray intersects with shadow plane at sx sy 0.0
(define sx (+ 0.5 (->fl int-x) (* dist tx)))
(define sy (+ 0.5 (->fl int-y) (* dist ty)))
;; actual transmission proportion (Fresnel's law)
(define T (* Ti (transmission-intensity n-dot-l 1.0 η2)))
;; intensity of incident light (Lambert's cosine law)
(define-values (Ilr Ilg Ilb) (fl3* lr lg lb n-dot-l))
;; normalized distance to the surface
(define norm-dist (/ dist opacity-z))
;; intensity of the light that strikes the surface
(define r (flvector-ref rgb-vs j))
(define g (flvector-ref rgb-vs (fx+ j 1)))
(define b (flvector-ref rgb-vs (fx+ j 2)))
(define-values (Ir Ig Ib)
(values (* T Ilr (absorb-intensity r norm-dist))
(* T Ilg (absorb-intensity g norm-dist))
(* T Ilb (absorb-intensity b norm-dist))))
(flvector-set! sx-vs i sx)
(flvector-set! sy-vs i sy)
(flvector-set! Irgb-vs j Ir)
(flvector-set! Irgb-vs (fx+ j 1) Ig)
(flvector-set! Irgb-vs (fx+ j 2) Ib)))))
(define diffracted-fm (fm+ (fm* (flomap-blur diffuse-fm diffuse-blur)
rgb-fm)
(fm* (flomap-blur specular-fm specular-blur)
(fm+ (fm* (- 1.0 purity) rgb-fm)
(fm* purity intensity-fm)))))
;; approximate ambient transmission by casting light downward with no refraction, then blurring
(define ambient-shadow-fm (make-flomap 3 w h))
(define ambient-shadow-vs (flomap-values ambient-shadow-fm))
(when (Ta . > . 0.0)
(for*: ([int-y : Integer (in-range y-min y-max)]
[int-x : Integer (in-range x-min x-max)])
(define i (fx+ int-x (fx* int-y w)))
(define a (flvector-ref alpha-vs i))
(when (a . > . 0.0)
(define z (flvector-ref z-vs i))
(define j (fx* 3 i))
(define r (flvector-ref rgb-vs j))
(define g (flvector-ref rgb-vs (fx+ j 1)))
(define b (flvector-ref rgb-vs (fx+ j 2)))
(define norm-dist (/ z opacity-z))
(define-values (Ir Ig Ib)
(values (* Tar (absorb-intensity r norm-dist))
(* Tag (absorb-intensity g norm-dist))
(* Tab (absorb-intensity b norm-dist))))
(flvector-set! ambient-shadow-vs j Ir)
(flvector-set! ambient-shadow-vs (fx+ j 1) Ig)
(flvector-set! ambient-shadow-vs (fx+ j 2) Ib))))
;; cast approximate shadow volumes
(define shadow-fm (flomap-blur ambient-shadow-fm (* ambient-transmission-blur-fraction (min w h))))
(define shadow-vs (flomap-values shadow-fm))
(when (Ti . > . 0.0)
;; Gaussian kernels - make as wide as possible to keep from having to reallocate
(define kxs (make-flvector w))
(define kys (make-flvector h))
(for*: ([int-y : Integer (in-range y-min (- y-max 1))]
[int-x : Integer (in-range x-min (- x-max 1))])
(define i00 (fx+ int-x (fx* int-y w)))
(define i01 (fx+ i00 1))
(define i10 (fx+ i00 w))
(define i11 (fx+ i10 1))
(define sx00 (flvector-ref sx-vs i00))
(define sx01 (flvector-ref sx-vs i01))
(define sx10 (flvector-ref sx-vs i10))
(define sx11 (flvector-ref sx-vs i11))
(when (and (flrational? sx00) (flrational? sx01)
(flrational? sx10) (flrational? sx11))
(define sy00 (flvector-ref sy-vs i00))
(define sy01 (flvector-ref sy-vs i01))
(define sy10 (flvector-ref sy-vs i10))
(define sy11 (flvector-ref sy-vs i11))
(define sx-min (min sx00 sx01 sx10 sx11))
(define sy-min (min sy00 sy01 sy10 sy11))
(define sx-max (max sx00 sx01 sx10 sx11))
(define sy-max (max sy00 sy01 sy10 sy11))
;; find the mean and standard deviation
(define sx-mid (* 0.25 (+ sx00 sx01 sx10 sx11)))
(define sy-mid (* 0.25 (+ sy00 sy01 sy10 sy11)))
(define sx-mid^2 (* 0.25 (+ (* sx00 sx00) (* sx01 sx01) (* sx10 sx10) (* sx11 sx11))))
(define sy-mid^2 (* 0.25 (+ (* sy00 sy00) (* sy01 sy01) (* sy10 sy10) (* sy11 sy11))))
(define sx-stddev (flsqrt (- sx-mid^2 (* sx-mid sx-mid))))
(define sy-stddev (flsqrt (- sy-mid^2 (* sy-mid sy-mid))))
(define x-min (fxmax 0 (fl->fx (floor sx-min))))
(define x-max (fxmin w (fx+ 1 (fl->fx (floor sx-max)))))
(define y-min (fxmax 0 (fl->fx (floor sy-min))))
(define y-max (fxmin h (fx+ 1 (fl->fx (floor sy-max)))))
(define x-size (fx- x-max x-min))
(define y-size (fx- y-max y-min))
(when (and (x-size . fx> . 0) (y-size . fx> . 0))
;; average the color
(define j00 (fx* 3 i00))
(define j01 (fx* 3 i01))
(define j10 (fx* 3 i10))
(define j11 (fx* 3 i11))
(define r (* 0.25 (+ (flvector-ref Irgb-vs j00)
(flvector-ref Irgb-vs j01)
(flvector-ref Irgb-vs j10)
(flvector-ref Irgb-vs j11))))
(define g (* 0.25 (+ (flvector-ref Irgb-vs (fx+ j00 1))
(flvector-ref Irgb-vs (fx+ j01 1))
(flvector-ref Irgb-vs (fx+ j10 1))
(flvector-ref Irgb-vs (fx+ j11 1)))))
(define b (* 0.25 (+ (flvector-ref Irgb-vs (fx+ j00 2))
(flvector-ref Irgb-vs (fx+ j01 2))
(flvector-ref Irgb-vs (fx+ j10 2))
(flvector-ref Irgb-vs (fx+ j11 2)))))
;; precalculate the Gaussian kernel for the x direction
(for ([dx (in-range x-size)])
(define x (fx+ dx x-min))
(define d (/ (- (+ 0.5 (fx->fl x)) sx-mid) sx-stddev))
(define kx (exp (* -0.5 (* d d))))
(flvector-set! kxs dx kx))
;; precalculate the Gaussian kernel for the y direction
;; this shouldn't help because it's used only once per y iteration, but it reduces allocs
;; within the loop (unsafe-flexp has no bytecode op yet, so its args and return are boxed)
(for ([dy (in-range y-size)])
(define y (fx+ dy y-min))
(define d (/ (- (+ 0.5 (fx->fl y)) sy-mid) sy-stddev))
(define ky (exp (* -0.5 (* d d))))
(flvector-set! kys dy ky))
;; normalization constant for a 2D Gaussian kernel
(define c (* 2.0 pi sx-stddev sy-stddev))
;; cast the approximate shadow volume
(let y-loop ([dy 0])
(when (dy . fx< . y-size)
(define ky (flvector-ref kys dy))
(cond [(ky . > . 0.1)
(define a (/ ky c))
(define Ir (* r a))
(define Ig (* g a))
(define Ib (* b a))
(define i (fx* 3 (fx+ x-min (fx* (fx+ dy y-min) w))))
(let x-loop ([dx 0] [i i])
(cond [(dx . fx< . x-size)
(define kx (flvector-ref kxs dx))
(when (kx . > . 0.1)
(flvector-set!
shadow-vs i (+ (* Ir kx) (flvector-ref shadow-vs i)))
(define i1 (fx+ i 1))
(flvector-set!
shadow-vs i1 (+ (* Ig kx) (flvector-ref shadow-vs i1)))
(define i2 (fx+ i 2))
(flvector-set!
shadow-vs i2 (+ (* Ib kx) (flvector-ref shadow-vs i2))))
(x-loop (fx+ 1 dx) (fx+ 3 i))]
[else
(y-loop (fx+ 1 dy))]))]
[else
(y-loop (fx+ 1 dy))])))))))
;; blur the shadow a bit to make up for approximating it with Gaussians
(values diffracted-fm (flomap-box-blur shadow-fm 1)))
;; ===================================================================================================
;; Pass 2: tracing from a directional viewer
(: trace-directional-view (flomap flomap flomap flomap flomap
Integer Integer Integer Integer -> (values flomap flomap)))
(define (trace-directional-view alpha-fm rgb-fm z-fm normal-fm shadow-fm
x-min x-max y-min y-max)
(define-values (w h) (flomap-size alpha-fm))
(match-define (list alpha-vs rgb-vs z-vs normal-vs shadow-vs)
(map flomap-values (list alpha-fm rgb-fm z-fm normal-fm shadow-fm)))
(define w-1 (fx- w 1))
(define h-1 (fx- h 1))
(define x-size (fx->fl w))
(define y-size (fx->fl h))
(define z-size (flomap-max-value z-fm))
(define x-mid (* 0.5 x-size))
(define y-mid (* 0.5 y-size))
(define opacity-z (/ z-size (transmission-density)))
;; reflected wall is tilted a bit toward the viewer
(define wall-tilt-θ (* 1/8 pi))
(define cos-wall-tilt-θ (cos wall-tilt-θ))
(define sin-wall-tilt-θ (sin wall-tilt-θ))
(match-define (list Irr Irg Irb) (reflected-intensity))
;; material properties
(define η2 (refractive-index))
(define η1/η2 (/ 1.0 η2))
(define Ri (ideal-reflectance))
(define Ti (ideal-transmission))
(define reflected-fm (make-flomap 3 w h))
(define reflected-vs (flomap-values reflected-fm))
(define transmitted-fm (make-flomap 3 w h))
(define transmitted-vs (flomap-values transmitted-fm))
(when (or (Ri . > . 0.0) (Ti . > . 0.0))
(for*: ([int-y : Integer (in-range y-min y-max)]
[int-x : Integer (in-range x-min x-max)])
(define i (fx+ int-x (fx* int-y w)))
(define a (flvector-ref alpha-vs i))
(when (a . > . 0.0)
(define j (fx* 3 i))
;; surface normal
(define nx (flvector-ref normal-vs j))
(define ny (flvector-ref normal-vs (fx+ j 1)))
(define nz (flvector-ref normal-vs (fx+ j 2)))
;; cosine of angle between viewer and surface normal
;; with gradient inferred from z flomap, this is always > 0.0
(define cos-i nz)
;; transmitted intensity
(define orig-T (transmission-intensity cos-i 1.0 η2))
(define T (* Ti orig-T))
(define R (* Ri (- 1.0 orig-T)))
;; surface coordinates
(define x (+ 0.5 (->fl int-x)))
(define y (+ 0.5 (->fl int-y)))
(define z (flvector-ref z-vs i))
;; reflection
(when (and (Ri . > . 0.0)
(int-x . fx> . 0) (int-x . fx< . w-1)
(int-y . fx> . 0) (int-y . fx< . h-1))
(define-values (rx ry rz) (reflect-view-ray nx ny nz))
;; tilt the wall a little so flat surfaces reflect something
(define ry* (- (* ry cos-wall-tilt-θ) (* rz sin-wall-tilt-θ)))
;(define rz* (+ (* ry sin-wall-tilt-θ) (* rz cos-wall-tilt-θ)))
;; distance to the wall
(define rdist (/ (- (- z-size) y) ry*))
(define sx (+ x (* rx rdist)))
(define sy (+ y (* ry rdist)))
(define sz (+ z (* rz rdist)))
(when (rdist . >= . 0.0)
(define cdist (fl3dist sx sy sz x-mid y-mid 0.0))
(define v (flsigmoid (* 0.25 (- (* 4.5 z-size) cdist))))
(let-values ([(r g b) (fl3* Irr Irg Irb (* R v))])
(flvector-set! reflected-vs j r)
(flvector-set! reflected-vs (fx+ j 1) g)
(flvector-set! reflected-vs (fx+ j 2) b))))
;; transmission (refraction)
(when (Ti . > . 0.0)
(define snx (flvector-ref normal-vs j))
(define sny (flvector-ref normal-vs (fx+ j 1)))
(define snz (flvector-ref normal-vs (fx+ j 2)))
(define-values (tx ty tz) (transmitted-vector snx sny snz 0.0 0.0 -1.0 1.0 η2))
;; sz = z + dist * tz, so dist = (sz - z) / tz
(define dist (/ (- 0.0 z) tz))
(when (and (dist . >= . 0.0) (dist . < . +inf.0))
;; Shadow intersection point
(define sx (+ x (* dist tx)))
(define sy (+ y (* dist ty)))
;; Shadow intersection color
(define sr (flomap-bilinear-ref shadow-fm 0 sx sy))
(define sg (flomap-bilinear-ref shadow-fm 1 sx sy))
(define sb (flomap-bilinear-ref shadow-fm 2 sx sy))
;; normalized distance to the surface
(define norm-dist (/ dist opacity-z))
;; intensities of each r g b by the time the light emerges from the surface
(define-values (r g b)
;; colors represent absorption rates
(let ([r (flvector-ref rgb-vs j)]
[g (flvector-ref rgb-vs (fx+ j 1))]
[b (flvector-ref rgb-vs (fx+ j 2))])
(values (* T sr (absorb-intensity r norm-dist))
(* T sg (absorb-intensity g norm-dist))
(* T sb (absorb-intensity b norm-dist)))))
(flvector-set! transmitted-vs j r)
(flvector-set! transmitted-vs (fx+ j 1) g)
(flvector-set! transmitted-vs (fx+ j 2) b))))))
(values reflected-fm transmitted-fm))
;; ===================================================================================================
;; Full rendering
(: prep-background (flomap Integer Integer -> (Option flomap)))
(define (prep-background fm w h)
(let loop ([fm (flomap-cc-crop fm w h)])
(case (flomap-components fm)
[(0) #f]
[(1) (flomap-append-components fm fm fm)]
[(2) (define value-fm (flomap-ref-component fm 1))
(loop (flomap-append-components fm value-fm value-fm))]
[(3) fm]
[(4) (flomap-drop-components (flomap-cc-superimpose (make-flomap 4 w h 1.0) fm) 1)]
[else (raise-type-error 'deep-flomap-render "flomap with 0, 1, 2, 3 or 4 components" fm)])))
(: deep-flomap-render (case-> (deep-flomap -> flomap)
(deep-flomap (Option flomap) -> flomap)))
(define deep-flomap-render
(case-lambda
[(dfm) (deep-flomap-render dfm #f)]
[(dfm background-fm)
(let ([dfm (deep-flomap-inset dfm 1)])
(define-values (w h) (deep-flomap-size dfm))
(define argb-fm (flomap-divide-alpha (deep-flomap-argb dfm)))
(define alpha-fm (flomap-ref-component argb-fm 0))
(define rgb-fm (flomap-drop-components argb-fm 1))
(define z-fm (fmmax 0.0 (deep-flomap-z dfm)))
(define normal-fm (flomap-gradient-normal z-fm))
(define bg-fm (if background-fm (prep-background background-fm w h) #f))
(define-values (x-min y-min x-max y-max)
(let-values ([(x-min y-min x-max y-max) (flomap-nonzero-rect alpha-fm)])
(values (max 0 (- x-min 1)) (max 0 (- y-min 1))
(min w (+ x-max 1)) (min h (+ y-max 1)))))
;; pass 1: trace from the light source
(define-values (diffracted-fm raw-shadow-fm)
(trace-directional-light alpha-fm rgb-fm z-fm normal-fm x-min x-max y-min y-max))
;; two Gaussian blurs by half of σ^2 is equivalent to one Gaussian blur by σ^2
(define σ^2 (real->double-flonum (sqr (* (min w h) (shadow-blur)))))
;; blur the shadow to simulate internal scatter
(define shadow-fm
(cond [bg-fm
(let* ([fm (flomap-blur raw-shadow-fm (flsqrt (* 1/3 σ^2)))]
[fm (fm* fm bg-fm)]
[fm (flomap-blur fm (flsqrt (* 1/3 σ^2)))])
fm)]
[else
(flomap-blur raw-shadow-fm (flsqrt (* 2/3 σ^2)))]))
;; pass 2: trace from the viewer
(define-values (reflected-fm raw-transmitted-fm)
(trace-directional-view alpha-fm rgb-fm z-fm normal-fm shadow-fm x-min x-max y-min y-max))
;; simulate scatter some more
(define transmitted-fm (flomap-blur raw-transmitted-fm (flsqrt (* 1/3 σ^2))))
;; add all the light together, convert to premultiplied-alpha flomap
(let* ([fm (fm+ (fm+ diffracted-fm transmitted-fm) reflected-fm)]
[fm (flomap-append-components alpha-fm fm)]
[fm (flomap-multiply-alpha fm)])
(flomap-inset fm -1)))]))