add noise

This commit is contained in:
Dan Frumin 2019-12-27 19:00:00 +01:00
parent 8604db96b3
commit 8240361ffa
3 changed files with 283 additions and 17 deletions

248
noise.rkt Normal file
View File

@ -0,0 +1,248 @@
#lang typed/racket
; Copyright (c) 2013 John-Paul Verkamp
; Direct translation of:
; http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
(provide
perlin
simplex)
(: grad3 (Vectorof (Vector Float Float Float)))
(define grad3
'#(#( 1.0 1.0 0.0) #(-1.0 1.0 0.0) #( 1.0 -1.0 0.0) #(-1.0 -1.0 0.0)
#( 1.0 0.0 1.0) #(-1.0 0.0 1.0) #( 1.0 0.0 -1.0) #(-1.0 0.0 -1.0)
#( 0.0 1.0 1.0) #( 0.0 -1.0 1.0) #( 0.0 1.0 -1.0) #( 0.0 -1.0 -1.0)))
(: p (Vectorof Byte))
(define p
'#(151 160 137 91 90 15 131 13 201 95 96 53 194 233 7
225 140 36 103 30 69 142 8 99 37 240 21 10 23 190 6
148 247 120 234 75 0 26 197 62 94 252 219 203 117 35
11 32 57 177 33 88 237 149 56 87 174 20 125 136 171
168 68 175 74 165 71 134 139 48 27 166 77 146 158
231 83 111 229 122 60 211 133 230 220 105 92 41 55
46 245 40 244 102 143 54 65 25 63 161 1 216 80 73
209 76 132 187 208 89 18 169 200 196 135 130 116 188
159 86 164 100 109 198 173 186 3 64 52 217 226 250
124 123 5 202 38 147 118 126 255 82 85 212 207 206
59 227 47 16 58 17 182 189 28 42 223 183 170 213 119
248 152 2 44 154 163 70 221 153 101 155 167 43 172 9
129 22 39 253 19 98 108 110 79 113 224 232 178 185
112 104 218 246 97 228 251 34 242 193 238 210 144 12
191 179 162 241 81 51 145 235 249 14 239 107 49 192
214 31 181 199 106 157 184 84 204 176 115 121 50 45
127 4 150 254 138 236 205 93 222 114 67 29 24 72 243
141 128 195 78 66 215 61 156 180))
; To remove the need for index wrapping, double the permutation table length
(: perm (Vectorof Byte))
(define perm (vector-append p p))
; This method is a *lot* faster than using (int)Math.floor(x)
; TODO: Not sure if this is actually true in Racket
(: fast-floor (Float -> Integer))
(define (fast-floor x)
(exact-floor x))
(: dot ((Vector Float Float Float) Float Float Float -> Float))
(define (dot g x y z)
(+ (* (vector-ref g 0) x)
(* (vector-ref g 1) y)
(* (vector-ref g 2) z)))
(: mix (Float Float Float -> Float))
(define (mix a b t)
(+ (* (- 1.0 t) a) (* t b)))
(: fade (Float -> Float))
(define (fade t)
(* t t t (+ (* t (- (* t 6.0) 15.0)) 10.0)))
; Classic Perlin noise, 3D version
(: perlin (case-> (Real -> Float)
(Real Real -> Float)
(Real Real Real -> Float)))
(define (perlin x [y 0.0] [z 0.0])
(perlin^ (real->double-flonum x)
(real->double-flonum y)
(real->double-flonum z)))
(: perlin^ (Float Float Float -> Float))
(define (perlin^ x y z)
; Find unit grid cell containing point
(: X Integer) (: Y Integer) (: Z Integer)
(define X (fast-floor x))
(define Y (fast-floor y))
(define Z (fast-floor z))
; Get relative xyz coordinates of point within that cell
(set! x (- x X))
(set! y (- y Y))
(set! z (- z Z))
; Wrap the integer cells at 255 (smaller integer period can be introduced here)
(set! X (bitwise-and X 255))
(set! Y (bitwise-and Y 255))
(set! Z (bitwise-and Z 255))
; Calculate a set of eight hashed gradient indices
(: gi000 Integer) (: gi001 Integer) (: gi010 Integer) (: gi011 Integer)
(: gi100 Integer) (: gi101 Integer) (: gi110 Integer) (: gi111 Integer)
(define gi000 (remainder (vector-ref perm (+ X (vector-ref perm (+ Y (vector-ref perm Z))))) 12))
(define gi001 (remainder (vector-ref perm (+ X (vector-ref perm (+ Y (vector-ref perm (+ Z 1)))))) 12))
(define gi010 (remainder (vector-ref perm (+ X (vector-ref perm (+ Y 1 (vector-ref perm Z))))) 12))
(define gi011 (remainder (vector-ref perm (+ X (vector-ref perm (+ Y 1 (vector-ref perm (+ Z 1)))))) 12))
(define gi100 (remainder (vector-ref perm (+ X 1 (vector-ref perm (+ Y (vector-ref perm Z))))) 12))
(define gi101 (remainder (vector-ref perm (+ X 1 (vector-ref perm (+ Y (vector-ref perm (+ Z 1)))))) 12))
(define gi110 (remainder (vector-ref perm (+ X 1 (vector-ref perm (+ Y 1 (vector-ref perm Z))))) 12))
(define gi111 (remainder (vector-ref perm (+ X 1 (vector-ref perm (+ Y 1 (vector-ref perm (+ Z 1)))))) 12))
; Calculate noise contributions from each of the eight corners
(: n000 Float) (: n001 Float) (: n010 Float) (: n011 Float)
(: n100 Float) (: n101 Float) (: n110 Float) (: n111 Float)
(define n000 (dot (vector-ref grad3 gi000) x y z))
(define n100 (dot (vector-ref grad3 gi100) (- x 1) y z))
(define n010 (dot (vector-ref grad3 gi010) x (- y 1) z))
(define n110 (dot (vector-ref grad3 gi110) (- x 1) (- y 1) z))
(define n001 (dot (vector-ref grad3 gi001) x y (- z 1)))
(define n101 (dot (vector-ref grad3 gi101) (- x 1) y (- z 1)))
(define n011 (dot (vector-ref grad3 gi011) x (- y 1) (- z 1)))
(define n111 (dot (vector-ref grad3 gi111) (- x 1) (- y 1) (- z 1)))
; Compute the fade curve value for each of x, y, z
(: u Float) (: v Float) (: w Float)
(define u (fade x))
(define v (fade y))
(define w (fade z))
; Interpolate along x the contributions from each of the corners
(: nx00 Float) (: nx01 Float) (: nx10 Float) (: nx11 Float)
(define nx00 (mix n000 n100 u))
(define nx01 (mix n001 n101 u))
(define nx10 (mix n010 n110 u))
(define nx11 (mix n011 n111 u))
; Interpolate the four results along y
(: nxy0 Float) (: nxy1 Float)
(define nxy0 (mix nx00 nx10 v))
(define nxy1 (mix nx01 nx11 v))
; Interpolate the two last results along z
(mix nxy0 nxy1 w))
; 3D simplex noise
(: F3 Float) (: G3 Float)
(define F3 (/ 1.0 3.0)) ; Very nice and simple skew factor for 3D
(define G3 (/ 1.0 6.0)) ; Very nice and simple unskew factor, too
(: simplex (case-> (Real -> Float)
(Real Real -> Float)
(Real Real Real -> Float)))
(define (simplex x [y 0.0] [z 0.0])
(simplex^ (real->double-flonum x)
(real->double-flonum y)
(real->double-flonum z)))
(: simplex^ (Float Float Float -> Float))
(define (simplex^ xin yin zin)
; Skew the input space to determine which simplex cell we're in
(: s Float)
(define s (* (real->double-flonum (+ xin yin zin)) F3))
(: i Integer) (: j Integer) (: k Integer)
(define i (fast-floor (+ xin s)))
(define j (fast-floor (+ yin s)))
(define k (fast-floor (+ zin s)))
(: t Float)
(define t (* (real->double-flonum (+ i j k)) G3))
(: X0 Float) (: Y0 Float) (: Z0 Float)
(: x0 Float) (: y0 Float) (: z0 Float)
(define X0 (- i t)) ; Unskew the cell origin back to (x,y,z) space
(define Y0 (- j t))
(define Z0 (- k t))
(define x0 (- xin X0)) ; The x,y,z distances from the cell origin
(define y0 (- yin Y0))
(define z0 (- zin Z0))
; For the 3D case, the simplex shape is a slightly irregular tetrahedron.
; Determine which simplex we are in.
(: i1 Integer) (: j1 Integer) (: k1 Integer)
(: i2 Integer) (: j2 Integer) (: k2 Integer)
(define-values (i1 j1 k1 i2 j2 k2)
(cond
[(and (>= x0 y0) (>= y0 z0)) (values 1 0 0 1 1 0)] ; X Y Z order
[(and (>= x0 y0) (>= x0 z0)) (values 1 0 0 1 0 1)] ; X Z Y order
[(>= x0 y0) (values 0 0 1 1 0 1)] ; Z X Y order
[(< y0 z0) (values 0 0 1 0 1 1)] ; Z Y X order
[(< x0 z0) (values 0 1 0 0 1 1)] ; Y Z X order
[else (values 0 1 0 1 1 0)])) ; Y X Z order
; A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
; a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
; a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
; c = 1/6.
(: x1 Float) (: y1 Float) (: z1 Float)
(: x2 Float) (: y2 Float) (: z2 Float)
(: x3 Float) (: y3 Float) (: z3 Float)
(define x1 (+ (- x0 i1) G3)) ; Offsets for second corner in (x,y,z) coords
(define y1 (+ (- y0 j1) G3))
(define z1 (+ (- z0 k1) G3))
(define x2 (+ (- x0 i2) (* 2.0 G3))) ; Offsets for third corner in (x,y,z) coords
(define y2 (+ (- y0 j2) (* 2.0 G3)))
(define z2 (+ (- z0 k2) (* 2.0 G3)))
(define x3 (+ (- x0 1.0) (* 3.0 G3))) ; Offsets for last corner in (x,y,z) coords
(define y3 (+ (- y0 1.0) (* 3.0 G3)))
(define z3 (+ (- z0 1.0) (* 3.0 G3)))
; Work out the hashed gradient indices of the four simplex corners
(: ii Integer) (: jj Integer) (: kk Integer)
(define ii (bitwise-and i 255))
(define jj (bitwise-and j 255))
(define kk (bitwise-and k 255))
(: gi0 Integer) (: gi1 Integer) (: gi2 Integer) (: gi3 Integer)
(define gi0 (remainder (vector-ref perm (+ ii (vector-ref perm (+ jj (vector-ref perm kk))))) 12))
(define gi1 (remainder (vector-ref perm (+ ii i1 (vector-ref perm (+ jj j1 (vector-ref perm (+ kk k1)))))) 12))
(define gi2 (remainder (vector-ref perm (+ ii i2 (vector-ref perm (+ jj j2 (vector-ref perm (+ kk k2)))))) 12))
(define gi3 (remainder (vector-ref perm (+ ii 1 (vector-ref perm (+ jj 1 (vector-ref perm (+ kk 1)))))) 12))
; Calculate the contribution from the four corners
(: t0 Float) (: n0 Float)
(define t0 (- 0.5 (* x0 x0) (* y0 y0) (* z0 z0)))
(define n0
(if (< t0 0)
0.0
(let ([t0^2 (* t0 t0)])
(* t0^2 t0^2 (dot (vector-ref grad3 gi0) x0 y0 z0)))))
(: t1 Float) (: n1 Float)
(define t1 (- 0.5 (* x1 x1) (* y1 y1) (* z1 z1)))
(define n1
(if (< t1 0)
0.0
(let ([t1^2 (* t1 t1)])
(* t1^2 t1^2 (dot (vector-ref grad3 gi1) x1 y1 z1)))))
(: t2 Float) (: n2 Float)
(define t2 (- 0.5 (* x2 x2) (* y2 y2) (* z2 z2)))
(define n2
(if (< t2 0)
0.0
(let ([t2^2 (* t2 t2)])
(* t2^2 t2^2 (dot (vector-ref grad3 gi2) x2 y2 z2)))))
(: t3 Float) (: n3 Float)
(define t3 (- 0.5 (* x3 x3) (* y3 y3) (* z3 z3)))
(define n3
(if (< t3 0)
0.0
(let ([t3^2 (* t3 t3)])
(* t3^2 t3^2 (dot (vector-ref grad3 gi3) x3 y3 z3)))))
; Add contributions from each corner to get the final noise value.
; The result is scaled to stay just inside [-1,1]
; NOTE: This scaling factor seems to work better than the given one
; I'm not sure why
(* 76.5 (+ n0 n1 n2 n3)))

View File

@ -1,7 +1,8 @@
#lang racket #lang racket
(require pict3d (require pict3d
pict3d/universe pict3d/universe
"pict3d-lib.rkt") "pict3d-lib.rkt"
"noise.rkt")
(struct scene-state (struct scene-state
(done? dx dy sun-dir) (done? dx dy sun-dir)
@ -15,18 +16,31 @@
#:specular 0.3 #:specular 0.3
#:roughness 0.5)) #:roughness 0.5))
(define (mesh) (define (mesh t)
(with-color (rgba-hex "05ffa1") (let* ([lines
(let* ([lines (for*/list ([i (in-range -10 10)]
(for*/list ([i (in-range -10 10)] [j (in-range 0 10)])
[j (in-range 0 10)]) (with-color (rgba-hex "05ffa1")
(move (cylinder (pos (- 0.1) (- 0.1) -5) (move (cylinder (pos (- 0.1) (- 0.1) -5)
(pos 0.1 0.1 10)) (pos 0.1 0.1 10))
(dir i 0 j)))] (dir i 0 j))))]
[lines-frozen (freeze (apply combine lines))]) [lines-frozen (freeze (apply combine lines))]
(combine lines-frozen [spheres (for*/list ([i (in-range -10 10)]
(move (rotate-y lines-frozen 90) [j (in-range -1 10)])
(dir -7 0 5)))))) (with-color (rgba-hex "ff71ce" 0.8)
(let ([x i]
[z j]
[y (scale-number
(perlin (scale-number i -10 10 0 1)
(scale-number j -1 10 0 1)
(scale-number t 0 2000 0 1))
0 1
1 5)])
(sphere (pos x y z) 0.2))))])
(combine lines-frozen
spheres
(move (rotate-y lines-frozen 90)
(dir -7 0 5)))))
(define scene (define scene
(rotate-y (cube (pos 0 0 0) 1) 30)) (rotate-y (cube (pos 0 0 0) 1) 30))
@ -40,8 +54,9 @@
(combine (basis 'camera (combine (basis 'camera
(point-at (pos 0 4 -6) origin)) (point-at (pos 0 4 -6) origin))
(make-sunlight (scene-state-sun-dir s)) (make-sunlight (scene-state-sun-dir s))
(mesh) (mesh t)
(move-y (rotate-x scene (/ t 20)) 2))) ;; (move-y (rotate-x scene (/ t 20)) 2)
))
(define (on-key s n t k) (define (on-key s n t k)
(case k (case k

View File

@ -1,7 +1,7 @@
#lang racket #lang racket
(require pict3d) (require pict3d)
(provide rgba-hex emitted-hex) (provide rgba-hex emitted-hex scale-number)
(define (get-hex str) (define (get-hex str)
(let*-values ([(num) (string->number str 16)] (let*-values ([(num) (string->number str 16)]
@ -10,9 +10,12 @@
[(red green) (quotient/remainder num 256)]) [(red green) (quotient/remainder num 256)])
(list (scale-n red) (scale-n green) (scale-n blue)))) (list (scale-n red) (scale-n green) (scale-n blue))))
;; TODO: alpha (define (rgba-hex str [alpha 1])
(define (rgba-hex str) (apply rgba `(,@(get-hex str) ,alpha)))
(apply rgba (get-hex str)))
(define (emitted-hex str intensity) (define (emitted-hex str intensity)
(apply emitted `(,@(get-hex str) ,intensity))) (apply emitted `(,@(get-hex str) ,intensity)))
(define (scale-number s a1 a2 b1 b2)
(let ([a (- a2 a1)] [b (- b2 b1)])
(exact->inexact (+ b1 (/ (* (- s a1) b) a)))))