|
518 | 518 | (interpolate z3 2.5 3.5 5.5) => 2.0)) |
519 | 519 |
|
520 | 520 | ;; ### Octaves of noise |
| 521 | +;; |
| 522 | +;; Fractal Brownian Motion is implemented by computing a weighted sum of the same base noise function using different frequencies. |
521 | 523 | (defn fractal-brownian-motion |
522 | 524 | [base octaves & args] |
523 | 525 | (let [scales (take (count octaves) (iterate #(* 2 %) 1))] |
524 | 526 | (reduce + 0.0 |
525 | 527 | (map (fn [amplitude scale] (* amplitude (apply base (map #(* scale %) args)))) |
526 | 528 | octaves scales)))) |
527 | 529 |
|
528 | | - |
| 530 | +;; Here the Fractal Brownian Motion is tested using an alternating 1D function and later a 2D checkboard function. |
529 | 531 | (facts "Fractal Brownian motion" |
530 | 532 | (let [base1 (fn [x] (if (>= (mod x 2.0) 1.0) 1.0 0.0)) |
531 | 533 | base2 (fn [y x] (if (= (Math/round (mod y 2.0)) (Math/round (mod x 2.0))) |
|
545 | 547 | (fractal-brownian-motion base1 [0.0 1.0] 0.0) => 0.0 |
546 | 548 | (fractal-brownian-motion base1 [0.0 1.0] 0.5) => 1.0)) |
547 | 549 |
|
548 | | - |
| 550 | +;; ### Remapping and clamping |
| 551 | +;; |
| 552 | +;; The remap function is used to map a range of values of an input tensor to a different range. |
549 | 553 | (defn remap |
550 | 554 | [value low1 high1 low2 high2] |
551 | 555 | (dfn/+ low2 (dfn/* (dfn/- value low1) (/ (- high2 low2) (- high1 low1))))) |
552 | 556 |
|
553 | | - |
554 | 557 | (tabular "Remap values of tensor" |
555 | 558 | (fact ((remap (tensor/->tensor [?value]) ?low1 ?high1 ?low2 ?high2) 0) |
556 | 559 | => ?expected) |
|
564 | 567 | 1 0 2 0 4 2) |
565 | 568 |
|
566 | 569 |
|
| 570 | +;; The clamp function is used to clamp a value to a range. |
567 | 571 | (defn clamp |
568 | 572 | [value low high] |
569 | 573 | (dfn/max low (dfn/min value high))) |
570 | 574 |
|
571 | | - |
572 | 575 | (tabular "Clamp values of tensor" |
573 | 576 | (fact ((clamp (tensor/->tensor [?value]) ?low ?high) 0) => ?expected) |
574 | 577 | ?value ?low ?high ?expected |
|
577 | 580 | 0 2 3 2 |
578 | 581 | 4 2 3 3) |
579 | 582 |
|
580 | | - |
| 583 | +;; ### Generating octaves of noise |
| 584 | +;; |
| 585 | +;; The octaves function is to create a series of decreasing weights and normalize them so that they add up to 1. |
581 | 586 | (defn octaves |
582 | 587 | [n decay] |
583 | 588 | (let [series (take n (iterate #(* % decay) 1.0)) |
584 | 589 | sum (apply + series)] |
585 | 590 | (mapv #(/ % sum) series))) |
586 | 591 |
|
587 | | - |
| 592 | +;; Here is an example of noise weights decreasing by 50% at each octave. |
588 | 593 | (octaves 4 0.5) |
589 | 594 |
|
590 | 595 |
|
| 596 | +;; Now a noise array can be generated using octaves of noise. |
591 | 597 | (defn noise-octaves |
592 | 598 | [tensor octaves low high] |
593 | 599 | (tensor/clone |
|
603 | 609 | low high 0 255) |
604 | 610 | 0 255))) |
605 | 611 |
|
| 612 | +;; ### 2D examples |
| 613 | +;; |
| 614 | +;; Here is an example of 4 octaves of Worley noise. |
606 | 615 | (bufimg/tensor->image (noise-octaves worley-norm (octaves 4 0.6) 120 230)) |
607 | 616 |
|
| 617 | +;; Here is an example of 4 octaves of Perlin noise. |
608 | 618 | (bufimg/tensor->image (noise-octaves perlin-norm (octaves 4 0.6) 120 230)) |
609 | 619 |
|
| 620 | +;; Here is an example of 4 octaves of mixed Perlin and Worley noise. |
610 | 621 | (bufimg/tensor->image (noise-octaves perlin-worley-norm (octaves 4 0.6) 120 230)) |
611 | 622 |
|
612 | 623 |
|
@@ -1320,4 +1331,5 @@ float shadow(vec3 point) |
1320 | 1331 | ;; * [Vertical density profile](https://www.wedesoft.de/software/2023/05/03/volumetric-clouds/) |
1321 | 1332 | ;; * [Powder function](https://advances.realtimerendering.com/s2015/index.html) |
1322 | 1333 | ;; * [Curl noise](https://www.wedesoft.de/software/2023/03/20/procedural-global-cloud-cover/) |
| 1334 | +;; * [Precomputed atmospheric scattering](https://ebruneton.github.io/precomputed_atmospheric_scattering/) |
1323 | 1335 | ;; * [Deep opacity maps](https://www.wedesoft.de/software/2023/05/03/volumetric-clouds/) |
0 commit comments