Blender V4.5
noise.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al.
2 * All Rights Reserved. (BSD-3-Clause).
3 * SPDX-FileCopyrightText: 2011 Blender Authors (GPL-2.0-or-later).
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later AND BSD-3-Clause */
6
10
11#include <algorithm>
12#include <cfloat>
13#include <cmath>
14#include <cstdint>
15
16#include "BLI_math_base.h"
17#include "BLI_math_base.hh"
19#include "BLI_math_numbers.hh"
20#include "BLI_math_vector.hh"
21#include "BLI_noise.hh"
22#include "BLI_utildefines.h"
23
24namespace blender::noise {
25
26/* -------------------------------------------------------------------- */
31
32BLI_INLINE uint32_t hash_bit_rotate(uint32_t x, uint32_t k)
33{
34 return (x << k) | (x >> (32 - k));
35}
36
37BLI_INLINE void hash_bit_mix(uint32_t &a, uint32_t &b, uint32_t &c)
38{
39 a -= c;
40 a ^= hash_bit_rotate(c, 4);
41 c += b;
42 b -= a;
43 b ^= hash_bit_rotate(a, 6);
44 a += c;
45 c -= b;
46 c ^= hash_bit_rotate(b, 8);
47 b += a;
48 a -= c;
49 a ^= hash_bit_rotate(c, 16);
50 c += b;
51 b -= a;
52 b ^= hash_bit_rotate(a, 19);
53 a += c;
54 c -= b;
55 c ^= hash_bit_rotate(b, 4);
56 b += a;
57}
58
59BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c)
60{
61 c ^= b;
62 c -= hash_bit_rotate(b, 14);
63 a ^= c;
64 a -= hash_bit_rotate(c, 11);
65 b ^= a;
66 b -= hash_bit_rotate(a, 25);
67 c ^= b;
68 c -= hash_bit_rotate(b, 16);
69 a ^= c;
70 a -= hash_bit_rotate(c, 4);
71 b ^= a;
72 b -= hash_bit_rotate(a, 14);
73 c ^= b;
74 c -= hash_bit_rotate(b, 24);
75}
76
77uint32_t hash(uint32_t kx)
78{
79 uint32_t a, b, c;
80 a = b = c = 0xdeadbeef + (1 << 2) + 13;
81
82 a += kx;
83 hash_bit_final(a, b, c);
84
85 return c;
86}
87
88uint32_t hash(uint32_t kx, uint32_t ky)
89{
90 uint32_t a, b, c;
91 a = b = c = 0xdeadbeef + (2 << 2) + 13;
92
93 b += ky;
94 a += kx;
95 hash_bit_final(a, b, c);
96
97 return c;
98}
99
100uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
101{
102 uint32_t a, b, c;
103 a = b = c = 0xdeadbeef + (3 << 2) + 13;
104
105 c += kz;
106 b += ky;
107 a += kx;
108 hash_bit_final(a, b, c);
109
110 return c;
111}
112
113uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
114{
115 uint32_t a, b, c;
116 a = b = c = 0xdeadbeef + (4 << 2) + 13;
117
118 a += kx;
119 b += ky;
120 c += kz;
121 hash_bit_mix(a, b, c);
122
123 a += kw;
124 hash_bit_final(a, b, c);
125
126 return c;
127}
128
129BLI_INLINE uint32_t float_as_uint(float f)
130{
131 union {
132 uint32_t i;
133 float f;
134 } u;
135 u.f = f;
136 return u.i;
137}
138
139uint32_t hash_float(float kx)
140{
141 return hash(float_as_uint(kx));
142}
143
145{
146 return hash(float_as_uint(k.x), float_as_uint(k.y));
147}
148
150{
151 return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z));
152}
153
155{
156 return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w));
157}
158
159uint32_t hash_float(const float4x4 &k)
160{
161 return hash(hash_float(k.x), hash_float(k.y), hash_float(k.z), hash_float(k.w));
162}
163
164/* Hashing a number of uint32_t into a float in the range [0, 1]. */
165
167{
168 return float(k) / float(0xFFFFFFFFu);
169}
170
171float hash_to_float(uint32_t kx)
172{
173 return uint_to_float_01(hash(kx));
174}
175
176float hash_to_float(uint32_t kx, uint32_t ky)
177{
178 return uint_to_float_01(hash(kx, ky));
179}
180
181float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
182{
183 return uint_to_float_01(hash(kx, ky, kz));
184}
185
186float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
187{
188 return uint_to_float_01(hash(kx, ky, kz, kw));
189}
190
191/* Hashing a number of floats into a float in the range [0, 1]. */
192
194{
195 return uint_to_float_01(hash_float(k));
196}
197
199{
200 return uint_to_float_01(hash_float(k));
201}
202
204{
205 return uint_to_float_01(hash_float(k));
206}
207
209{
210 return uint_to_float_01(hash_float(k));
211}
212
217
219{
220 return float2(hash_float_to_float(float3(k.x, k.y, k.z)),
221 hash_float_to_float(float3(k.z, k.x, k.y)));
222}
223
225{
226 return float2(hash_float_to_float(float4(k.x, k.y, k.z, k.w)),
227 hash_float_to_float(float4(k.z, k.x, k.w, k.y)));
228}
229
231{
232 return float3(hash_float_to_float(k),
234 hash_float_to_float(float2(k, 2.0)));
235}
236
238{
239 return float3(hash_float_to_float(k),
240 hash_float_to_float(float3(k.x, k.y, 1.0)),
241 hash_float_to_float(float3(k.x, k.y, 2.0)));
242}
243
245{
246 return float3(hash_float_to_float(k),
247 hash_float_to_float(float4(k.x, k.y, k.z, 1.0)),
248 hash_float_to_float(float4(k.x, k.y, k.z, 2.0)));
249}
250
252{
253 return float3(hash_float_to_float(k),
254 hash_float_to_float(float4(k.z, k.x, k.w, k.y)),
255 hash_float_to_float(float4(k.w, k.z, k.y, k.x)));
256}
257
259{
260 return float4(hash_float_to_float(k),
261 hash_float_to_float(float4(k.w, k.x, k.y, k.z)),
262 hash_float_to_float(float4(k.z, k.w, k.x, k.y)),
263 hash_float_to_float(float4(k.y, k.z, k.w, k.x)));
264}
265
267
268/* -------------------------------------------------------------------- */
277
278/* Linear Interpolation. */
279template<typename T> T static mix(T v0, T v1, float x)
280{
281 return (1 - x) * v0 + x * v1;
282}
283
284/* Bilinear Interpolation:
285 *
286 * v2 v3
287 * @ + + + + @ y
288 * + + ^
289 * + + |
290 * + + |
291 * @ + + + + @ @------> x
292 * v0 v1
293 */
294BLI_INLINE float mix(float v0, float v1, float v2, float v3, float x, float y)
295{
296 float x1 = 1.0 - x;
297 return (1.0 - y) * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x);
298}
299
300/* Trilinear Interpolation:
301 *
302 * v6 v7
303 * @ + + + + + + @
304 * +\ +\
305 * + \ + \
306 * + \ + \
307 * + \ v4 + \ v5
308 * + @ + + + +++ + @ z
309 * + + + + y ^
310 * v2 @ + +++ + + + @ v3 + \ |
311 * \ + \ + \ |
312 * \ + \ + \|
313 * \ + \ + +---------> x
314 * \+ \+
315 * @ + + + + + + @
316 * v0 v1
317 */
318BLI_INLINE float mix(float v0,
319 float v1,
320 float v2,
321 float v3,
322 float v4,
323 float v5,
324 float v6,
325 float v7,
326 float x,
327 float y,
328 float z)
329{
330 float x1 = 1.0 - x;
331 float y1 = 1.0 - y;
332 float z1 = 1.0 - z;
333 return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) +
334 z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x));
335}
336
337/* Quadrilinear Interpolation. */
338BLI_INLINE float mix(float v0,
339 float v1,
340 float v2,
341 float v3,
342 float v4,
343 float v5,
344 float v6,
345 float v7,
346 float v8,
347 float v9,
348 float v10,
349 float v11,
350 float v12,
351 float v13,
352 float v14,
353 float v15,
354 float x,
355 float y,
356 float z,
357 float w)
358{
359 return mix(mix(v0, v1, v2, v3, v4, v5, v6, v7, x, y, z),
360 mix(v8, v9, v10, v11, v12, v13, v14, v15, x, y, z),
361 w);
362}
363
364BLI_INLINE float fade(float t)
365{
366 return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
367}
368
369BLI_INLINE float negate_if(float value, uint32_t condition)
370{
371 return (condition != 0u) ? -value : value;
372}
373
374BLI_INLINE float noise_grad(uint32_t hash, float x)
375{
376 uint32_t h = hash & 15u;
377 float g = 1u + (h & 7u);
378 return negate_if(g, h & 8u) * x;
379}
380
381BLI_INLINE float noise_grad(uint32_t hash, float x, float y)
382{
383 uint32_t h = hash & 7u;
384 float u = h < 4u ? x : y;
385 float v = 2.0 * (h < 4u ? y : x);
386 return negate_if(u, h & 1u) + negate_if(v, h & 2u);
387}
388
389BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z)
390{
391 uint32_t h = hash & 15u;
392 float u = h < 8u ? x : y;
393 float vt = ELEM(h, 12u, 14u) ? x : z;
394 float v = h < 4u ? y : vt;
395 return negate_if(u, h & 1u) + negate_if(v, h & 2u);
396}
397
398BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z, float w)
399{
400 uint32_t h = hash & 31u;
401 float u = h < 24u ? x : y;
402 float v = h < 16u ? y : z;
403 float s = h < 8u ? z : w;
404 return negate_if(u, h & 1u) + negate_if(v, h & 2u) + negate_if(s, h & 4u);
405}
406
407BLI_INLINE float floor_fraction(float x, int &i)
408{
409 float x_floor = math::floor(x);
410 i = int(x_floor);
411 return x - x_floor;
412}
413
414BLI_INLINE float perlin_noise(float position)
415{
416 int X;
417
418 float fx = floor_fraction(position, X);
419
420 float u = fade(fx);
421
422 float r = mix(noise_grad(hash(X), fx), noise_grad(hash(X + 1), fx - 1.0), u);
423
424 return r;
425}
426
428{
429 int X, Y;
430
431 float fx = floor_fraction(position.x, X);
432 float fy = floor_fraction(position.y, Y);
433
434 float u = fade(fx);
435 float v = fade(fy);
436
437 float r = mix(noise_grad(hash(X, Y), fx, fy),
438 noise_grad(hash(X + 1, Y), fx - 1.0, fy),
439 noise_grad(hash(X, Y + 1), fx, fy - 1.0),
440 noise_grad(hash(X + 1, Y + 1), fx - 1.0, fy - 1.0),
441 u,
442 v);
443
444 return r;
445}
446
448{
449 int X, Y, Z;
450
451 float fx = floor_fraction(position.x, X);
452 float fy = floor_fraction(position.y, Y);
453 float fz = floor_fraction(position.z, Z);
454
455 float u = fade(fx);
456 float v = fade(fy);
457 float w = fade(fz);
458
459 float r = mix(noise_grad(hash(X, Y, Z), fx, fy, fz),
460 noise_grad(hash(X + 1, Y, Z), fx - 1, fy, fz),
461 noise_grad(hash(X, Y + 1, Z), fx, fy - 1, fz),
462 noise_grad(hash(X + 1, Y + 1, Z), fx - 1, fy - 1, fz),
463 noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1),
464 noise_grad(hash(X + 1, Y, Z + 1), fx - 1, fy, fz - 1),
465 noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1, fz - 1),
466 noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1, fy - 1, fz - 1),
467 u,
468 v,
469 w);
470
471 return r;
472}
473
475{
476 int X, Y, Z, W;
477
478 float fx = floor_fraction(position.x, X);
479 float fy = floor_fraction(position.y, Y);
480 float fz = floor_fraction(position.z, Z);
481 float fw = floor_fraction(position.w, W);
482
483 float u = fade(fx);
484 float v = fade(fy);
485 float t = fade(fz);
486 float s = fade(fw);
487
488 float r = mix(
489 noise_grad(hash(X, Y, Z, W), fx, fy, fz, fw),
490 noise_grad(hash(X + 1, Y, Z, W), fx - 1.0, fy, fz, fw),
491 noise_grad(hash(X, Y + 1, Z, W), fx, fy - 1.0, fz, fw),
492 noise_grad(hash(X + 1, Y + 1, Z, W), fx - 1.0, fy - 1.0, fz, fw),
493 noise_grad(hash(X, Y, Z + 1, W), fx, fy, fz - 1.0, fw),
494 noise_grad(hash(X + 1, Y, Z + 1, W), fx - 1.0, fy, fz - 1.0, fw),
495 noise_grad(hash(X, Y + 1, Z + 1, W), fx, fy - 1.0, fz - 1.0, fw),
496 noise_grad(hash(X + 1, Y + 1, Z + 1, W), fx - 1.0, fy - 1.0, fz - 1.0, fw),
497 noise_grad(hash(X, Y, Z, W + 1), fx, fy, fz, fw - 1.0),
498 noise_grad(hash(X + 1, Y, Z, W + 1), fx - 1.0, fy, fz, fw - 1.0),
499 noise_grad(hash(X, Y + 1, Z, W + 1), fx, fy - 1.0, fz, fw - 1.0),
500 noise_grad(hash(X + 1, Y + 1, Z, W + 1), fx - 1.0, fy - 1.0, fz, fw - 1.0),
501 noise_grad(hash(X, Y, Z + 1, W + 1), fx, fy, fz - 1.0, fw - 1.0),
502 noise_grad(hash(X + 1, Y, Z + 1, W + 1), fx - 1.0, fy, fz - 1.0, fw - 1.0),
503 noise_grad(hash(X, Y + 1, Z + 1, W + 1), fx, fy - 1.0, fz - 1.0, fw - 1.0),
504 noise_grad(hash(X + 1, Y + 1, Z + 1, W + 1), fx - 1.0, fy - 1.0, fz - 1.0, fw - 1.0),
505 u,
506 v,
507 t,
508 s);
509
510 return r;
511}
512
513/* Signed versions of perlin noise in the range [-1, 1]. The scale values were computed
514 * experimentally by the OSL developers to remap the noise output to the correct range. */
515
516float perlin_signed(float position)
517{
518 float precision_correction = 0.5f * float(math::abs(position) >= 1000000.0f);
519 /* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
520 * representation issues. */
521 position = math::mod(position, 100000.0f) + precision_correction;
522
523 return perlin_noise(position) * 0.2500f;
524}
525
526float perlin_signed(float2 position)
527{
528 float2 precision_correction = 0.5f * float2(float(math::abs(position.x) >= 1000000.0f),
529 float(math::abs(position.y) >= 1000000.0f));
530 /* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
531 * representation issues. This causes discontinuities every 100000.0f, however at such scales
532 * this usually shouldn't be noticeable. */
533 position = math::mod(position, 100000.0f) + precision_correction;
534
535 return perlin_noise(position) * 0.6616f;
536}
537
538float perlin_signed(float3 position)
539{
540 float3 precision_correction = 0.5f * float3(float(math::abs(position.x) >= 1000000.0f),
541 float(math::abs(position.y) >= 1000000.0f),
542 float(math::abs(position.z) >= 1000000.0f));
543 /* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
544 * representation issues. This causes discontinuities every 100000.0f, however at such scales
545 * this usually shouldn't be noticeable. */
546 position = math::mod(position, 100000.0f) + precision_correction;
547
548 return perlin_noise(position) * 0.9820f;
549}
550
551float perlin_signed(float4 position)
552{
553 float4 precision_correction = 0.5f * float4(float(math::abs(position.x) >= 1000000.0f),
554 float(math::abs(position.y) >= 1000000.0f),
555 float(math::abs(position.z) >= 1000000.0f),
556 float(math::abs(position.w) >= 1000000.0f));
557 /* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
558 * representation issues. This causes discontinuities every 100000.0f, however at such scales
559 * this usually shouldn't be noticeable. */
560 position = math::mod(position, 100000.0f) + precision_correction;
561
562 return perlin_noise(position) * 0.8344f;
563}
564
565/* Positive versions of perlin noise in the range [0, 1]. */
566
567float perlin(float position)
568{
569 return perlin_signed(position) / 2.0f + 0.5f;
570}
571
572float perlin(float2 position)
573{
574 return perlin_signed(position) / 2.0f + 0.5f;
575}
576
577float perlin(float3 position)
578{
579 return perlin_signed(position) / 2.0f + 0.5f;
580}
581
582float perlin(float4 position)
583{
584 return perlin_signed(position) / 2.0f + 0.5f;
585}
586
587/* Fractal perlin noise. */
588
589/* fBM = Fractal Brownian Motion */
590template<typename T>
591#if defined(_MSC_VER) && _MSC_VER >= 1930
592/* The MSVC 2022 optimizer generates bad code for perlin_fractal_distorted when perlin_fbm gets
593 * inlined leading to incorrect results and failing tests that rely on perlin noise. For now just
594 * disable inlining for this function until we can get the compiler fixed. */
596#endif
597 float
599 const float detail,
600 const float roughness,
601 const float lacunarity,
602 const bool normalize)
603{
604 float fscale = 1.0f;
605 float amp = 1.0f;
606 float maxamp = 0.0f;
607 float sum = 0.0f;
608
609 for (int i = 0; i <= int(detail); i++) {
610 float t = perlin_signed(fscale * p);
611 sum += t * amp;
612 maxamp += amp;
613 amp *= roughness;
614 fscale *= lacunarity;
615 }
616 float rmd = detail - std::floor(detail);
617 if (rmd != 0.0f) {
618 float t = perlin_signed(fscale * p);
619 float sum2 = sum + t * amp;
620 return normalize ? mix(0.5f * sum / maxamp + 0.5f, 0.5f * sum2 / (maxamp + amp) + 0.5f, rmd) :
621 mix(sum, sum2, rmd);
622 }
623 return normalize ? 0.5f * sum / maxamp + 0.5f : sum;
624}
625
626/* Explicit instantiation for Wave Texture. */
627template float perlin_fbm<float3>(float3 p,
628 const float detail,
629 const float roughness,
630 const float lacunarity,
631 const bool normalize);
632
633template float perlin_fbm<float2>(float2 p,
634 const float detail,
635 const float roughness,
636 const float lacunarity,
637 const bool normalize);
638
639template<typename T>
640float perlin_multi_fractal(T p, const float detail, const float roughness, const float lacunarity)
641{
642 float value = 1.0f;
643 float pwr = 1.0f;
644
645 for (int i = 0; i <= int(detail); i++) {
646 value *= (pwr * perlin_signed(p) + 1.0f);
647 pwr *= roughness;
648 p *= lacunarity;
649 }
650
651 const float rmd = detail - floorf(detail);
652 if (rmd != 0.0f) {
653 value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */
654 }
655
656 return value;
657}
658
659template<typename T>
661 T p, const float detail, const float roughness, const float lacunarity, const float offset)
662{
663 float pwr = roughness;
664
665 /* First unscaled octave of function; later octaves are scaled. */
666 float value = offset + perlin_signed(p);
667 p *= lacunarity;
668
669 for (int i = 1; i <= int(detail); i++) {
670 float increment = (perlin_signed(p) + offset) * pwr * value;
671 value += increment;
672 pwr *= roughness;
673 p *= lacunarity;
674 }
675
676 const float rmd = detail - floorf(detail);
677 if (rmd != 0.0f) {
678 float increment = (perlin_signed(p) + offset) * pwr * value;
679 value += rmd * increment;
680 }
681
682 return value;
683}
684
685template<typename T>
687 const float detail,
688 const float roughness,
689 const float lacunarity,
690 const float offset,
691 const float gain)
692{
693 float pwr = 1.0f;
694 float value = 0.0f;
695 float weight = 1.0f;
696
697 for (int i = 0; (weight > 0.001f) && (i <= int(detail)); i++) {
698 weight = std::min(weight, 1.0f);
699
700 float signal = (perlin_signed(p) + offset) * pwr;
701 pwr *= roughness;
702 value += weight * signal;
703 weight *= gain * signal;
704 p *= lacunarity;
705 }
706
707 const float rmd = detail - floorf(detail);
708 if ((rmd != 0.0f) && (weight > 0.001f)) {
709 weight = std::min(weight, 1.0f);
710 float signal = (perlin_signed(p) + offset) * pwr;
711 value += rmd * weight * signal;
712 }
713
714 return value;
715}
716
717template<typename T>
719 const float detail,
720 const float roughness,
721 const float lacunarity,
722 const float offset,
723 const float gain)
724{
725 float pwr = roughness;
726
727 float signal = offset - std::abs(perlin_signed(p));
728 signal *= signal;
729 float value = signal;
730 float weight = 1.0f;
731
732 for (int i = 1; i <= int(detail); i++) {
733 p *= lacunarity;
734 weight = std::clamp(signal * gain, 0.0f, 1.0f);
735 signal = offset - std::abs(perlin_signed(p));
736 signal *= signal;
737 signal *= weight;
738 value += signal * pwr;
739 pwr *= roughness;
740 }
741
742 return value;
743}
744
745enum {
751};
752
753template<typename T>
755 float detail,
756 float roughness,
757 float lacunarity,
758 float offset,
759 float gain,
760 int type,
761 bool normalize)
762{
763 switch (type) {
765 return perlin_multi_fractal<T>(p, detail, roughness, lacunarity);
766 }
768 return perlin_fbm<T>(p, detail, roughness, lacunarity, normalize);
769 }
771 return perlin_hybrid_multi_fractal<T>(p, detail, roughness, lacunarity, offset, gain);
772 }
774 return perlin_ridged_multi_fractal<T>(p, detail, roughness, lacunarity, offset, gain);
775 }
777 return perlin_hetero_terrain<T>(p, detail, roughness, lacunarity, offset);
778 }
779 default: {
780 return 0.0;
781 }
782 }
783}
784
785/* The following offset functions generate random offsets to be added to
786 * positions to act as a seed since the noise functions don't have seed values.
787 * The offset's components are in the range [100, 200], not too high to cause
788 * bad precision and not too small to be noticeable. We use float seed because
789 * OSL only supports float hashes and we need to maintain compatibility with it.
790 */
791
793{
794 return 100.0f + hash_float_to_float(seed) * 100.0f;
795}
796
798{
799 return float2(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
800 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f);
801}
802
804{
805 return float3(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
806 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f,
807 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f);
808}
809
811{
812 return float4(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
813 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f,
814 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f,
815 100.0f + hash_float_to_float(float2(seed, 3.0f)) * 100.0f);
816}
817
818/* Perlin noises to be added to the position to distort other noises. */
819
820BLI_INLINE float perlin_distortion(float position, float strength)
821{
822 return perlin_signed(position + random_float_offset(0.0)) * strength;
823}
824
825BLI_INLINE float2 perlin_distortion(float2 position, float strength)
826{
827 return float2(perlin_signed(position + random_float2_offset(0.0f)) * strength,
828 perlin_signed(position + random_float2_offset(1.0f)) * strength);
829}
830
831BLI_INLINE float3 perlin_distortion(float3 position, float strength)
832{
833 return float3(perlin_signed(position + random_float3_offset(0.0f)) * strength,
834 perlin_signed(position + random_float3_offset(1.0f)) * strength,
835 perlin_signed(position + random_float3_offset(2.0f)) * strength);
836}
837
838BLI_INLINE float4 perlin_distortion(float4 position, float strength)
839{
840 return float4(perlin_signed(position + random_float4_offset(0.0f)) * strength,
841 perlin_signed(position + random_float4_offset(1.0f)) * strength,
842 perlin_signed(position + random_float4_offset(2.0f)) * strength,
843 perlin_signed(position + random_float4_offset(3.0f)) * strength);
844}
845
846/* Distorted fractal perlin noise. */
847
848template<typename T>
850 float detail,
851 float roughness,
852 float lacunarity,
853 float offset,
854 float gain,
855 float distortion,
856 int type,
857 bool normalize)
858{
859 position += perlin_distortion(position, distortion);
860 return perlin_select<T>(position, detail, roughness, lacunarity, offset, gain, type, normalize);
861}
862
863template float perlin_fractal_distorted<float>(float position,
864 float detail,
865 float roughness,
866 float lacunarity,
867 float offset,
868 float gain,
869 float distortion,
870 int type,
871 bool normalize);
873 float detail,
874 float roughness,
875 float lacunarity,
876 float offset,
877 float gain,
878 float distortion,
879 int type,
880 bool normalize);
882 float detail,
883 float roughness,
884 float lacunarity,
885 float offset,
886 float gain,
887 float distortion,
888 int type,
889 bool normalize);
891 float detail,
892 float roughness,
893 float lacunarity,
894 float offset,
895 float gain,
896 float distortion,
897 int type,
898 bool normalize);
899
900/* Distorted fractal perlin noise that outputs a float3. The arbitrary seeds are for
901 * compatibility with shading functions. */
902
904 float detail,
905 float roughness,
906 float lacunarity,
907 float offset,
908 float gain,
909 float distortion,
910 int type,
911 bool normalize)
912{
913 position += perlin_distortion(position, distortion);
914 return float3(
915 perlin_select<float>(position, detail, roughness, lacunarity, offset, gain, type, normalize),
917 detail,
918 roughness,
919 lacunarity,
920 offset,
921 gain,
922 type,
923 normalize),
925 detail,
926 roughness,
927 lacunarity,
928 offset,
929 gain,
930 type,
931 normalize));
932}
933
935 float detail,
936 float roughness,
937 float lacunarity,
938 float offset,
939 float gain,
940 float distortion,
941 int type,
942 bool normalize)
943{
944 position += perlin_distortion(position, distortion);
946 position, detail, roughness, lacunarity, offset, gain, type, normalize),
948 detail,
949 roughness,
950 lacunarity,
951 offset,
952 gain,
953 type,
954 normalize),
956 detail,
957 roughness,
958 lacunarity,
959 offset,
960 gain,
961 type,
962 normalize));
963}
964
966 float detail,
967 float roughness,
968 float lacunarity,
969 float offset,
970 float gain,
971 float distortion,
972 int type,
973 bool normalize)
974{
975 position += perlin_distortion(position, distortion);
977 position, detail, roughness, lacunarity, offset, gain, type, normalize),
979 detail,
980 roughness,
981 lacunarity,
982 offset,
983 gain,
984 type,
985 normalize),
987 detail,
988 roughness,
989 lacunarity,
990 offset,
991 gain,
992 type,
993 normalize));
994}
995
997 float detail,
998 float roughness,
999 float lacunarity,
1000 float offset,
1001 float gain,
1002 float distortion,
1003 int type,
1004 bool normalize)
1005{
1006 position += perlin_distortion(position, distortion);
1008 position, detail, roughness, lacunarity, offset, gain, type, normalize),
1010 detail,
1011 roughness,
1012 lacunarity,
1013 offset,
1014 gain,
1015 type,
1016 normalize),
1018 detail,
1019 roughness,
1020 lacunarity,
1021 offset,
1022 gain,
1023 type,
1024 normalize));
1025}
1026
1028
1029/* -------------------------------------------------------------------- */
1048
1049/* Ensure to align with DNA. */
1050
1051enum {
1056};
1057
1058enum {
1064};
1065
1066/* ***** Distances ***** */
1067
1068float voronoi_distance(const float a, const float b)
1069{
1070 return std::abs(b - a);
1071}
1072
1074{
1075 switch (params.metric) {
1077 return math::distance(a, b);
1079 return std::abs(a.x - b.x) + std::abs(a.y - b.y);
1081 return std::max(std::abs(a.x - b.x), std::abs(a.y - b.y));
1083 return std::pow(std::pow(std::abs(a.x - b.x), params.exponent) +
1084 std::pow(std::abs(a.y - b.y), params.exponent),
1085 1.0f / params.exponent);
1086 default:
1088 break;
1089 }
1090 return 0.0f;
1091}
1092
1094{
1095 switch (params.metric) {
1097 return math::distance(a, b);
1099 return std::abs(a.x - b.x) + std::abs(a.y - b.y) + std::abs(a.z - b.z);
1101 return std::max({std::abs(a.x - b.x), std::abs(a.y - b.y), std::abs(a.z - b.z)});
1103 return std::pow(std::pow(std::abs(a.x - b.x), params.exponent) +
1104 std::pow(std::abs(a.y - b.y), params.exponent) +
1105 std::pow(std::abs(a.z - b.z), params.exponent),
1106 1.0f / params.exponent);
1107 default:
1109 break;
1110 }
1111 return 0.0f;
1112}
1113
1115{
1116 switch (params.metric) {
1118 return math::distance(a, b);
1120 return std::abs(a.x - b.x) + std::abs(a.y - b.y) + std::abs(a.z - b.z) + std::abs(a.w - b.w);
1122 return std::max(
1123 {std::abs(a.x - b.x), std::abs(a.y - b.y), std::abs(a.z - b.z), std::abs(a.w - b.w)});
1125 return std::pow(std::pow(std::abs(a.x - b.x), params.exponent) +
1126 std::pow(std::abs(a.y - b.y), params.exponent) +
1127 std::pow(std::abs(a.z - b.z), params.exponent) +
1128 std::pow(std::abs(a.w - b.w), params.exponent),
1129 1.0f / params.exponent);
1130 default:
1132 break;
1133 }
1134 return 0.0f;
1135}
1136
1137/* Possibly cheaper/faster version of Voronoi distance, in a way that does not change
1138 * logic of "which distance is the closest?". */
1139template<typename T>
1140static float voronoi_distance_bound(const T a, const T b, const VoronoiParams &params)
1141{
1142 if (params.metric == NOISE_SHD_VORONOI_EUCLIDEAN) {
1143 return math::length_squared(a - b);
1144 }
1145 return voronoi_distance(a, b, params);
1146}
1147
1148/* **** 1D Voronoi **** */
1149
1150float4 voronoi_position(const float coord)
1151{
1152 return {0.0f, 0.0f, 0.0f, coord};
1153}
1154
1156{
1157 float cellPosition = floorf(coord);
1158 float localPosition = coord - cellPosition;
1159
1160 float minDistance = FLT_MAX;
1161 float targetOffset = 0.0f;
1162 float targetPosition = 0.0f;
1163 for (int i = -1; i <= 1; i++) {
1164 float cellOffset = i;
1165 float pointPosition = cellOffset +
1166 hash_float_to_float(cellPosition + cellOffset) * params.randomness;
1167 float distanceToPoint = voronoi_distance(pointPosition, localPosition);
1168 if (distanceToPoint < minDistance) {
1169 targetOffset = cellOffset;
1170 minDistance = distanceToPoint;
1171 targetPosition = pointPosition;
1172 }
1173 }
1174
1175 VoronoiOutput octave;
1176 octave.distance = minDistance;
1177 octave.color = hash_float_to_float3(cellPosition + targetOffset);
1178 octave.position = voronoi_position(targetPosition + cellPosition);
1179 return octave;
1180}
1181
1183 const float coord,
1184 const bool calc_color)
1185{
1186 float cellPosition = floorf(coord);
1187 float localPosition = coord - cellPosition;
1188
1189 float smoothDistance = 0.0f;
1190 float smoothPosition = 0.0f;
1191 float3 smoothColor = {0.0f, 0.0f, 0.0f};
1192 float h = -1.0f;
1193 for (int i = -2; i <= 2; i++) {
1194 float cellOffset = i;
1195 float pointPosition = cellOffset +
1196 hash_float_to_float(cellPosition + cellOffset) * params.randomness;
1197 float distanceToPoint = voronoi_distance(pointPosition, localPosition);
1198 h = h == -1.0f ?
1199 1.0f :
1200 smoothstep(
1201 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
1202 float correctionFactor = params.smoothness * h * (1.0f - h);
1203 smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
1204 correctionFactor /= 1.0f + 3.0f * params.smoothness;
1205 float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
1206 if (calc_color) {
1207 /* Only compute Color output if necessary, as it is very expensive. */
1208 smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
1209 }
1210 smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
1211 }
1212
1213 VoronoiOutput octave;
1214 octave.distance = smoothDistance;
1215 octave.color = smoothColor;
1216 octave.position = voronoi_position(cellPosition + smoothPosition);
1217 return octave;
1218}
1219
1221{
1222 float cellPosition = floorf(coord);
1223 float localPosition = coord - cellPosition;
1224
1225 float distanceF1 = FLT_MAX;
1226 float distanceF2 = FLT_MAX;
1227 float offsetF1 = 0.0f;
1228 float positionF1 = 0.0f;
1229 float offsetF2 = 0.0f;
1230 float positionF2 = 0.0f;
1231 for (int i = -1; i <= 1; i++) {
1232 float cellOffset = i;
1233 float pointPosition = cellOffset +
1234 hash_float_to_float(cellPosition + cellOffset) * params.randomness;
1235 float distanceToPoint = voronoi_distance(pointPosition, localPosition);
1236 if (distanceToPoint < distanceF1) {
1237 distanceF2 = distanceF1;
1238 distanceF1 = distanceToPoint;
1239 offsetF2 = offsetF1;
1240 offsetF1 = cellOffset;
1241 positionF2 = positionF1;
1242 positionF1 = pointPosition;
1243 }
1244 else if (distanceToPoint < distanceF2) {
1245 distanceF2 = distanceToPoint;
1246 offsetF2 = cellOffset;
1247 positionF2 = pointPosition;
1248 }
1249 }
1250
1251 VoronoiOutput octave;
1252 octave.distance = distanceF2;
1253 octave.color = hash_float_to_float3(cellPosition + offsetF2);
1254 octave.position = voronoi_position(positionF2 + cellPosition);
1255 return octave;
1256}
1257
1258float voronoi_distance_to_edge(const VoronoiParams &params, const float coord)
1259{
1260 float cellPosition = floorf(coord);
1261 float localPosition = coord - cellPosition;
1262
1263 float midPointPosition = hash_float_to_float(cellPosition) * params.randomness;
1264 float leftPointPosition = -1.0f + hash_float_to_float(cellPosition - 1.0f) * params.randomness;
1265 float rightPointPosition = 1.0f + hash_float_to_float(cellPosition + 1.0f) * params.randomness;
1266 float distanceToMidLeft = fabsf((midPointPosition + leftPointPosition) / 2.0f - localPosition);
1267 float distanceToMidRight = fabsf((midPointPosition + rightPointPosition) / 2.0f - localPosition);
1268
1269 return math::min(distanceToMidLeft, distanceToMidRight);
1270}
1271
1272float voronoi_n_sphere_radius(const VoronoiParams &params, const float coord)
1273{
1274 float cellPosition = floorf(coord);
1275 float localPosition = coord - cellPosition;
1276
1277 float closestPoint = 0.0f;
1278 float closestPointOffset = 0.0f;
1279 float minDistance = FLT_MAX;
1280 for (int i = -1; i <= 1; i++) {
1281 float cellOffset = i;
1282 float pointPosition = cellOffset +
1283 hash_float_to_float(cellPosition + cellOffset) * params.randomness;
1284 float distanceToPoint = fabsf(pointPosition - localPosition);
1285 if (distanceToPoint < minDistance) {
1286 minDistance = distanceToPoint;
1287 closestPoint = pointPosition;
1288 closestPointOffset = cellOffset;
1289 }
1290 }
1291
1292 minDistance = FLT_MAX;
1293 float closestPointToClosestPoint = 0.0f;
1294 for (int i = -1; i <= 1; i++) {
1295 if (i == 0) {
1296 continue;
1297 }
1298 float cellOffset = i + closestPointOffset;
1299 float pointPosition = cellOffset +
1300 hash_float_to_float(cellPosition + cellOffset) * params.randomness;
1301 float distanceToPoint = fabsf(closestPoint - pointPosition);
1302 if (distanceToPoint < minDistance) {
1303 minDistance = distanceToPoint;
1304 closestPointToClosestPoint = pointPosition;
1305 }
1306 }
1307
1308 return fabsf(closestPointToClosestPoint - closestPoint) / 2.0f;
1309}
1310
1311/* **** 2D Voronoi **** */
1312
1314{
1315 return {coord.x, coord.y, 0.0f, 0.0f};
1316}
1317
1319{
1320 float2 cellPosition = math::floor(coord);
1321 float2 localPosition = coord - cellPosition;
1322
1323 float minDistance = FLT_MAX;
1324 float2 targetOffset = {0.0f, 0.0f};
1325 float2 targetPosition = {0.0f, 0.0f};
1326 for (int j = -1; j <= 1; j++) {
1327 for (int i = -1; i <= 1; i++) {
1328 float2 cellOffset(i, j);
1329 float2 pointPosition = cellOffset +
1330 hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
1331 float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
1332 if (distanceToPoint < minDistance) {
1333 targetOffset = cellOffset;
1334 minDistance = distanceToPoint;
1335 targetPosition = pointPosition;
1336 }
1337 }
1338 }
1339
1340 VoronoiOutput octave;
1341 octave.distance = voronoi_distance(targetPosition, localPosition, params);
1342 octave.color = hash_float_to_float3(cellPosition + targetOffset);
1343 octave.position = voronoi_position(targetPosition + cellPosition);
1344 return octave;
1345}
1346
1348 const float2 coord,
1349 const bool calc_color)
1350{
1351 float2 cellPosition = math::floor(coord);
1352 float2 localPosition = coord - cellPosition;
1353
1354 float smoothDistance = 0.0f;
1355 float3 smoothColor = {0.0f, 0.0f, 0.0f};
1356 float2 smoothPosition = {0.0f, 0.0f};
1357 float h = -1.0f;
1358 for (int j = -2; j <= 2; j++) {
1359 for (int i = -2; i <= 2; i++) {
1360 float2 cellOffset(i, j);
1361 float2 pointPosition = cellOffset +
1362 hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
1363 float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
1364 h = h == -1.0f ?
1365 1.0f :
1366 smoothstep(0.0f,
1367 1.0f,
1368 0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
1369 float correctionFactor = params.smoothness * h * (1.0f - h);
1370 smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
1371 correctionFactor /= 1.0f + 3.0f * params.smoothness;
1372 if (calc_color) {
1373 /* Only compute Color output if necessary, as it is very expensive. */
1374 float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
1375 smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
1376 }
1377 smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
1378 }
1379 }
1380
1381 VoronoiOutput octave;
1382 octave.distance = smoothDistance;
1383 octave.color = smoothColor;
1384 octave.position = voronoi_position(cellPosition + smoothPosition);
1385 return octave;
1386}
1387
1389{
1390 float2 cellPosition = math::floor(coord);
1391 float2 localPosition = coord - cellPosition;
1392
1393 float distanceF1 = FLT_MAX;
1394 float distanceF2 = FLT_MAX;
1395 float2 offsetF1 = {0.0f, 0.0f};
1396 float2 positionF1 = {0.0f, 0.0f};
1397 float2 offsetF2 = {0.0f, 0.0f};
1398 float2 positionF2 = {0.0f, 0.0f};
1399 for (int j = -1; j <= 1; j++) {
1400 for (int i = -1; i <= 1; i++) {
1401 float2 cellOffset(i, j);
1402 float2 pointPosition = cellOffset +
1403 hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
1404 float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
1405 if (distanceToPoint < distanceF1) {
1406 distanceF2 = distanceF1;
1407 distanceF1 = distanceToPoint;
1408 offsetF2 = offsetF1;
1409 offsetF1 = cellOffset;
1410 positionF2 = positionF1;
1411 positionF1 = pointPosition;
1412 }
1413 else if (distanceToPoint < distanceF2) {
1414 distanceF2 = distanceToPoint;
1415 offsetF2 = cellOffset;
1416 positionF2 = pointPosition;
1417 }
1418 }
1419 }
1420
1421 VoronoiOutput octave;
1422 octave.distance = distanceF2;
1423 octave.color = hash_float_to_float3(cellPosition + offsetF2);
1424 octave.position = voronoi_position(positionF2 + cellPosition);
1425 return octave;
1426}
1427
1429{
1430 float2 cellPosition = math::floor(coord);
1431 float2 localPosition = coord - cellPosition;
1432
1433 float2 vectorToClosest = {0.0f, 0.0f};
1434 float minDistance = FLT_MAX;
1435 for (int j = -1; j <= 1; j++) {
1436 for (int i = -1; i <= 1; i++) {
1437 float2 cellOffset(i, j);
1438 float2 vectorToPoint = cellOffset +
1439 hash_float_to_float2(cellPosition + cellOffset) * params.randomness -
1440 localPosition;
1441 float distanceToPoint = math::dot(vectorToPoint, vectorToPoint);
1442 if (distanceToPoint < minDistance) {
1443 minDistance = distanceToPoint;
1444 vectorToClosest = vectorToPoint;
1445 }
1446 }
1447 }
1448
1449 minDistance = FLT_MAX;
1450 for (int j = -1; j <= 1; j++) {
1451 for (int i = -1; i <= 1; i++) {
1452 float2 cellOffset(i, j);
1453 float2 vectorToPoint = cellOffset +
1454 hash_float_to_float2(cellPosition + cellOffset) * params.randomness -
1455 localPosition;
1456 float2 perpendicularToEdge = vectorToPoint - vectorToClosest;
1457 if (math::dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
1458 float distanceToEdge = math::dot((vectorToClosest + vectorToPoint) / 2.0f,
1459 math::normalize(perpendicularToEdge));
1460 minDistance = math::min(minDistance, distanceToEdge);
1461 }
1462 }
1463 }
1464
1465 return minDistance;
1466}
1467
1469{
1470 float2 cellPosition = math::floor(coord);
1471 float2 localPosition = coord - cellPosition;
1472
1473 float2 closestPoint = {0.0f, 0.0f};
1474 float2 closestPointOffset = {0.0f, 0.0f};
1475 float minDistanceSq = FLT_MAX;
1476 for (int j = -1; j <= 1; j++) {
1477 for (int i = -1; i <= 1; i++) {
1478 float2 cellOffset(i, j);
1479 float2 pointPosition = cellOffset +
1480 hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
1481 float distanceToPointSq = math::length_squared(pointPosition - localPosition);
1482 if (distanceToPointSq < minDistanceSq) {
1483 minDistanceSq = distanceToPointSq;
1484 closestPoint = pointPosition;
1485 closestPointOffset = cellOffset;
1486 }
1487 }
1488 }
1489
1490 minDistanceSq = FLT_MAX;
1491 float2 closestPointToClosestPoint = {0.0f, 0.0f};
1492 for (int j = -1; j <= 1; j++) {
1493 for (int i = -1; i <= 1; i++) {
1494 if (i == 0 && j == 0) {
1495 continue;
1496 }
1497 float2 cellOffset = float2(i, j) + closestPointOffset;
1498 float2 pointPosition = cellOffset +
1499 hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
1500 float distanceToPointSq = math::length_squared(closestPoint - pointPosition);
1501 if (distanceToPointSq < minDistanceSq) {
1502 minDistanceSq = distanceToPointSq;
1503 closestPointToClosestPoint = pointPosition;
1504 }
1505 }
1506 }
1507
1508 return math::distance(closestPointToClosestPoint, closestPoint) / 2.0f;
1509}
1510
1511/* **** 3D Voronoi **** */
1512
1514{
1515 return {coord.x, coord.y, coord.z, 0.0f};
1516}
1517
1519{
1520 float3 cellPosition = math::floor(coord);
1521 float3 localPosition = coord - cellPosition;
1522
1523 float minDistance = FLT_MAX;
1524 float3 targetOffset = {0.0f, 0.0f, 0.0f};
1525 float3 targetPosition = {0.0f, 0.0f, 0.0f};
1526 for (int k = -1; k <= 1; k++) {
1527 for (int j = -1; j <= 1; j++) {
1528 for (int i = -1; i <= 1; i++) {
1529 float3 cellOffset(i, j, k);
1530 float3 pointPosition = cellOffset +
1531 hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
1532 float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
1533 if (distanceToPoint < minDistance) {
1534 targetOffset = cellOffset;
1535 minDistance = distanceToPoint;
1536 targetPosition = pointPosition;
1537 }
1538 }
1539 }
1540 }
1541
1542 VoronoiOutput octave;
1543 octave.distance = voronoi_distance(targetPosition, localPosition, params);
1544 octave.color = hash_float_to_float3(cellPosition + targetOffset);
1545 octave.position = voronoi_position(targetPosition + cellPosition);
1546 return octave;
1547}
1548
1550 const float3 coord,
1551 const bool calc_color)
1552{
1553 float3 cellPosition = math::floor(coord);
1554 float3 localPosition = coord - cellPosition;
1555
1556 float smoothDistance = 0.0f;
1557 float3 smoothColor = {0.0f, 0.0f, 0.0f};
1558 float3 smoothPosition = {0.0f, 0.0f, 0.0f};
1559 float h = -1.0f;
1560 for (int k = -2; k <= 2; k++) {
1561 for (int j = -2; j <= 2; j++) {
1562 for (int i = -2; i <= 2; i++) {
1563 float3 cellOffset(i, j, k);
1564 float3 pointPosition = cellOffset +
1565 hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
1566 float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
1567 h = h == -1.0f ?
1568 1.0f :
1569 smoothstep(0.0f,
1570 1.0f,
1571 0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
1572 float correctionFactor = params.smoothness * h * (1.0f - h);
1573 smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
1574 correctionFactor /= 1.0f + 3.0f * params.smoothness;
1575 if (calc_color) {
1576 /* Only compute Color output if necessary, as it is very expensive. */
1577 float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
1578 smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
1579 }
1580 smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
1581 }
1582 }
1583 }
1584
1585 VoronoiOutput octave;
1586 octave.distance = smoothDistance;
1587 octave.color = smoothColor;
1588 octave.position = voronoi_position(cellPosition + smoothPosition);
1589 return octave;
1590}
1591
1593{
1594 float3 cellPosition = math::floor(coord);
1595 float3 localPosition = coord - cellPosition;
1596
1597 float distanceF1 = FLT_MAX;
1598 float distanceF2 = FLT_MAX;
1599 float3 offsetF1 = {0.0f, 0.0f, 0.0f};
1600 float3 positionF1 = {0.0f, 0.0f, 0.0f};
1601 float3 offsetF2 = {0.0f, 0.0f, 0.0f};
1602 float3 positionF2 = {0.0f, 0.0f, 0.0f};
1603 for (int k = -1; k <= 1; k++) {
1604 for (int j = -1; j <= 1; j++) {
1605 for (int i = -1; i <= 1; i++) {
1606 float3 cellOffset(i, j, k);
1607 float3 pointPosition = cellOffset +
1608 hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
1609 float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
1610 if (distanceToPoint < distanceF1) {
1611 distanceF2 = distanceF1;
1612 distanceF1 = distanceToPoint;
1613 offsetF2 = offsetF1;
1614 offsetF1 = cellOffset;
1615 positionF2 = positionF1;
1616 positionF1 = pointPosition;
1617 }
1618 else if (distanceToPoint < distanceF2) {
1619 distanceF2 = distanceToPoint;
1620 offsetF2 = cellOffset;
1621 positionF2 = pointPosition;
1622 }
1623 }
1624 }
1625 }
1626
1627 VoronoiOutput octave;
1628 octave.distance = distanceF2;
1629 octave.color = hash_float_to_float3(cellPosition + offsetF2);
1630 octave.position = voronoi_position(positionF2 + cellPosition);
1631 return octave;
1632}
1633
1635{
1636 float3 cellPosition = math::floor(coord);
1637 float3 localPosition = coord - cellPosition;
1638
1639 float3 vectorToClosest = {0.0f, 0.0f, 0.0f};
1640 float minDistance = FLT_MAX;
1641 for (int k = -1; k <= 1; k++) {
1642 for (int j = -1; j <= 1; j++) {
1643 for (int i = -1; i <= 1; i++) {
1644 float3 cellOffset(i, j, k);
1645 float3 vectorToPoint = cellOffset +
1646 hash_float_to_float3(cellPosition + cellOffset) *
1647 params.randomness -
1648 localPosition;
1649 float distanceToPoint = math::dot(vectorToPoint, vectorToPoint);
1650 if (distanceToPoint < minDistance) {
1651 minDistance = distanceToPoint;
1652 vectorToClosest = vectorToPoint;
1653 }
1654 }
1655 }
1656 }
1657
1658 minDistance = FLT_MAX;
1659 for (int k = -1; k <= 1; k++) {
1660 for (int j = -1; j <= 1; j++) {
1661 for (int i = -1; i <= 1; i++) {
1662 float3 cellOffset(i, j, k);
1663 float3 vectorToPoint = cellOffset +
1664 hash_float_to_float3(cellPosition + cellOffset) *
1665 params.randomness -
1666 localPosition;
1667 float3 perpendicularToEdge = vectorToPoint - vectorToClosest;
1668 if (math::dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
1669 float distanceToEdge = math::dot((vectorToClosest + vectorToPoint) / 2.0f,
1670 math::normalize(perpendicularToEdge));
1671 minDistance = math::min(minDistance, distanceToEdge);
1672 }
1673 }
1674 }
1675 }
1676
1677 return minDistance;
1678}
1679
1681{
1682 float3 cellPosition = math::floor(coord);
1683 float3 localPosition = coord - cellPosition;
1684
1685 float3 closestPoint = {0.0f, 0.0f, 0.0f};
1686 float3 closestPointOffset = {0.0f, 0.0f, 0.0f};
1687 float minDistanceSq = FLT_MAX;
1688 for (int k = -1; k <= 1; k++) {
1689 for (int j = -1; j <= 1; j++) {
1690 for (int i = -1; i <= 1; i++) {
1691 float3 cellOffset(i, j, k);
1692 float3 pointPosition = cellOffset +
1693 hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
1694 float distanceToPointSq = math::length_squared(pointPosition - localPosition);
1695 if (distanceToPointSq < minDistanceSq) {
1696 minDistanceSq = distanceToPointSq;
1697 closestPoint = pointPosition;
1698 closestPointOffset = cellOffset;
1699 }
1700 }
1701 }
1702 }
1703
1704 minDistanceSq = FLT_MAX;
1705 float3 closestPointToClosestPoint = {0.0f, 0.0f, 0.0f};
1706 for (int k = -1; k <= 1; k++) {
1707 for (int j = -1; j <= 1; j++) {
1708 for (int i = -1; i <= 1; i++) {
1709 if (i == 0 && j == 0 && k == 0) {
1710 continue;
1711 }
1712 float3 cellOffset = float3(i, j, k) + closestPointOffset;
1713 float3 pointPosition = cellOffset +
1714 hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
1715 float distanceToPointSq = math::length_squared(closestPoint - pointPosition);
1716 if (distanceToPointSq < minDistanceSq) {
1717 minDistanceSq = distanceToPointSq;
1718 closestPointToClosestPoint = pointPosition;
1719 }
1720 }
1721 }
1722 }
1723
1724 return math::distance(closestPointToClosestPoint, closestPoint) / 2.0f;
1725}
1726
1727/* **** 4D Voronoi **** */
1728
1730{
1731 return coord;
1732}
1733
1735{
1736 float4 cellPosition = math::floor(coord);
1737 float4 localPosition = coord - cellPosition;
1738
1739 float minDistance = FLT_MAX;
1740 float4 targetOffset = {0.0f, 0.0f, 0.0f, 0.0f};
1741 float4 targetPosition = {0.0f, 0.0f, 0.0f, 0.0f};
1742 for (int u = -1; u <= 1; u++) {
1743 for (int k = -1; k <= 1; k++) {
1744 for (int j = -1; j <= 1; j++) {
1745 for (int i = -1; i <= 1; i++) {
1746 float4 cellOffset(i, j, k, u);
1747 float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
1748 params.randomness;
1749 float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
1750 if (distanceToPoint < minDistance) {
1751 targetOffset = cellOffset;
1752 minDistance = distanceToPoint;
1753 targetPosition = pointPosition;
1754 }
1755 }
1756 }
1757 }
1758 }
1759
1760 VoronoiOutput octave;
1761 octave.distance = voronoi_distance(targetPosition, localPosition, params);
1762 octave.color = hash_float_to_float3(cellPosition + targetOffset);
1763 octave.position = voronoi_position(targetPosition + cellPosition);
1764 return octave;
1765}
1766
1768 const float4 coord,
1769 const bool calc_color)
1770{
1771 float4 cellPosition = math::floor(coord);
1772 float4 localPosition = coord - cellPosition;
1773
1774 float smoothDistance = 0.0f;
1775 float3 smoothColor = {0.0f, 0.0f, 0.0f};
1776 float4 smoothPosition = {0.0f, 0.0f, 0.0f, 0.0f};
1777 float h = -1.0f;
1778 for (int u = -2; u <= 2; u++) {
1779 for (int k = -2; k <= 2; k++) {
1780 for (int j = -2; j <= 2; j++) {
1781 for (int i = -2; i <= 2; i++) {
1782 float4 cellOffset(i, j, k, u);
1783 float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
1784 params.randomness;
1785 float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
1786 h = h == -1.0f ?
1787 1.0f :
1788 smoothstep(0.0f,
1789 1.0f,
1790 0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
1791 float correctionFactor = params.smoothness * h * (1.0f - h);
1792 smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
1793 correctionFactor /= 1.0f + 3.0f * params.smoothness;
1794 if (calc_color) {
1795 /* Only compute Color output if necessary, as it is very expensive. */
1796 float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
1797 smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
1798 }
1799 smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
1800 }
1801 }
1802 }
1803 }
1804
1805 VoronoiOutput octave;
1806 octave.distance = smoothDistance;
1807 octave.color = smoothColor;
1808 octave.position = voronoi_position(cellPosition + smoothPosition);
1809 return octave;
1810}
1811
1813{
1814 float4 cellPosition = math::floor(coord);
1815 float4 localPosition = coord - cellPosition;
1816
1817 float distanceF1 = FLT_MAX;
1818 float distanceF2 = FLT_MAX;
1819 float4 offsetF1 = {0.0f, 0.0f, 0.0f, 0.0f};
1820 float4 positionF1 = {0.0f, 0.0f, 0.0f, 0.0f};
1821 float4 offsetF2 = {0.0f, 0.0f, 0.0f, 0.0f};
1822 float4 positionF2 = {0.0f, 0.0f, 0.0f, 0.0f};
1823 for (int u = -1; u <= 1; u++) {
1824 for (int k = -1; k <= 1; k++) {
1825 for (int j = -1; j <= 1; j++) {
1826 for (int i = -1; i <= 1; i++) {
1827 float4 cellOffset(i, j, k, u);
1828 float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
1829 params.randomness;
1830 float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
1831 if (distanceToPoint < distanceF1) {
1832 distanceF2 = distanceF1;
1833 distanceF1 = distanceToPoint;
1834 offsetF2 = offsetF1;
1835 offsetF1 = cellOffset;
1836 positionF2 = positionF1;
1837 positionF1 = pointPosition;
1838 }
1839 else if (distanceToPoint < distanceF2) {
1840 distanceF2 = distanceToPoint;
1841 offsetF2 = cellOffset;
1842 positionF2 = pointPosition;
1843 }
1844 }
1845 }
1846 }
1847 }
1848
1849 VoronoiOutput octave;
1850 octave.distance = distanceF2;
1851 octave.color = hash_float_to_float3(cellPosition + offsetF2);
1852 octave.position = voronoi_position(positionF2 + cellPosition);
1853 return octave;
1854}
1855
1857{
1858 float4 cellPosition = math::floor(coord);
1859 float4 localPosition = coord - cellPosition;
1860
1861 float4 vectorToClosest = {0.0f, 0.0f, 0.0f, 0.0f};
1862 float minDistance = FLT_MAX;
1863 for (int u = -1; u <= 1; u++) {
1864 for (int k = -1; k <= 1; k++) {
1865 for (int j = -1; j <= 1; j++) {
1866 for (int i = -1; i <= 1; i++) {
1867 float4 cellOffset(i, j, k, u);
1868 float4 vectorToPoint = cellOffset +
1869 hash_float_to_float4(cellPosition + cellOffset) *
1870 params.randomness -
1871 localPosition;
1872 float distanceToPoint = math::dot(vectorToPoint, vectorToPoint);
1873 if (distanceToPoint < minDistance) {
1874 minDistance = distanceToPoint;
1875 vectorToClosest = vectorToPoint;
1876 }
1877 }
1878 }
1879 }
1880 }
1881
1882 minDistance = FLT_MAX;
1883 for (int u = -1; u <= 1; u++) {
1884 for (int k = -1; k <= 1; k++) {
1885 for (int j = -1; j <= 1; j++) {
1886 for (int i = -1; i <= 1; i++) {
1887 float4 cellOffset(i, j, k, u);
1888 float4 vectorToPoint = cellOffset +
1889 hash_float_to_float4(cellPosition + cellOffset) *
1890 params.randomness -
1891 localPosition;
1892 float4 perpendicularToEdge = vectorToPoint - vectorToClosest;
1893 if (math::dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
1894 float distanceToEdge = math::dot((vectorToClosest + vectorToPoint) / 2.0f,
1895 math::normalize(perpendicularToEdge));
1896 minDistance = math::min(minDistance, distanceToEdge);
1897 }
1898 }
1899 }
1900 }
1901 }
1902
1903 return minDistance;
1904}
1905
1907{
1908 float4 cellPosition = math::floor(coord);
1909 float4 localPosition = coord - cellPosition;
1910
1911 float4 closestPoint = {0.0f, 0.0f, 0.0f, 0.0f};
1912 float4 closestPointOffset = {0.0f, 0.0f, 0.0f, 0.0f};
1913 float minDistanceSq = FLT_MAX;
1914 for (int u = -1; u <= 1; u++) {
1915 for (int k = -1; k <= 1; k++) {
1916 for (int j = -1; j <= 1; j++) {
1917 for (int i = -1; i <= 1; i++) {
1918 float4 cellOffset(i, j, k, u);
1919 float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
1920 params.randomness;
1921 float distanceToPointSq = math::length_squared(pointPosition - localPosition);
1922 if (distanceToPointSq < minDistanceSq) {
1923 minDistanceSq = distanceToPointSq;
1924 closestPoint = pointPosition;
1925 closestPointOffset = cellOffset;
1926 }
1927 }
1928 }
1929 }
1930 }
1931
1932 minDistanceSq = FLT_MAX;
1933 float4 closestPointToClosestPoint = {0.0f, 0.0f, 0.0f, 0.0f};
1934 for (int u = -1; u <= 1; u++) {
1935 for (int k = -1; k <= 1; k++) {
1936 for (int j = -1; j <= 1; j++) {
1937 for (int i = -1; i <= 1; i++) {
1938 if (i == 0 && j == 0 && k == 0 && u == 0) {
1939 continue;
1940 }
1941 float4 cellOffset = float4(i, j, k, u) + closestPointOffset;
1942 float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
1943 params.randomness;
1944 float distanceToPointSq = math::length_squared(closestPoint - pointPosition);
1945 if (distanceToPointSq < minDistanceSq) {
1946 minDistanceSq = distanceToPointSq;
1947 closestPointToClosestPoint = pointPosition;
1948 }
1949 }
1950 }
1951 }
1952 }
1953
1954 return math::distance(closestPointToClosestPoint, closestPoint) / 2.0f;
1955}
1956
1957/* **** Fractal Voronoi **** */
1958
1959/* The fractalization logic is the same as for fBM Noise, except that some additions are replaced
1960 * by lerps. */
1961template<typename T>
1963 const T coord,
1964 const bool calc_color /* Only used to optimize Smooth F1 */)
1965{
1966 float amplitude = 1.0f;
1967 float max_amplitude = 0.0f;
1968 float scale = 1.0f;
1969
1971 const bool zero_input = params.detail == 0.0f || params.roughness == 0.0f;
1972
1973 for (int i = 0; i <= ceilf(params.detail); ++i) {
1974 VoronoiOutput octave = (params.feature == NOISE_SHD_VORONOI_F2) ?
1975 voronoi_f2(params, coord * scale) :
1976 (params.feature == NOISE_SHD_VORONOI_SMOOTH_F1 &&
1977 params.smoothness != 0.0f) ?
1978 voronoi_smooth_f1(params, coord * scale, calc_color) :
1979 voronoi_f1(params, coord * scale);
1980
1981 if (zero_input) {
1982 max_amplitude = 1.0f;
1983 output = octave;
1984 break;
1985 }
1986 if (i <= params.detail) {
1987 max_amplitude += amplitude;
1988 output.distance += octave.distance * amplitude;
1989 output.color += octave.color * amplitude;
1990 output.position = mix(output.position, octave.position / scale, amplitude);
1991 scale *= params.lacunarity;
1992 amplitude *= params.roughness;
1993 }
1994 else {
1995 float remainder = params.detail - floorf(params.detail);
1996 if (remainder != 0.0f) {
1997 max_amplitude = mix(max_amplitude, max_amplitude + amplitude, remainder);
1998 output.distance = mix(
1999 output.distance, output.distance + octave.distance * amplitude, remainder);
2000 output.color = mix(output.color, output.color + octave.color * amplitude, remainder);
2001 output.position = mix(
2002 output.position, mix(output.position, octave.position / scale, amplitude), remainder);
2003 }
2004 }
2005 }
2006
2007 if (params.normalize) {
2008 output.distance /= max_amplitude * params.max_distance;
2009 output.color /= max_amplitude;
2010 }
2011
2012 output.position = (params.scale != 0.0f) ? output.position / params.scale :
2013 float4{0.0f, 0.0f, 0.0f, 0.0f};
2014
2015 return output;
2016}
2017
2018/* The fractalization logic is the same as for fBM Noise, except that some additions are replaced
2019 * by lerps. */
2020template<typename T>
2022{
2023 float amplitude = 1.0f;
2024 float max_amplitude = params.max_distance;
2025 float scale = 1.0f;
2026 float distance = 8.0f;
2027
2028 const bool zero_input = params.detail == 0.0f || params.roughness == 0.0f;
2029
2030 for (int i = 0; i <= ceilf(params.detail); ++i) {
2031 const float octave_distance = voronoi_distance_to_edge(params, coord * scale);
2032
2033 if (zero_input) {
2034 distance = octave_distance;
2035 break;
2036 }
2037 if (i <= params.detail) {
2038 max_amplitude = mix(max_amplitude, params.max_distance / scale, amplitude);
2039 distance = mix(distance, math::min(distance, octave_distance / scale), amplitude);
2040 scale *= params.lacunarity;
2041 amplitude *= params.roughness;
2042 }
2043 else {
2044 float remainder = params.detail - floorf(params.detail);
2045 if (remainder != 0.0f) {
2046 float lerp_amplitude = mix(max_amplitude, params.max_distance / scale, amplitude);
2047 max_amplitude = mix(max_amplitude, lerp_amplitude, remainder);
2048 float lerp_distance = mix(
2049 distance, math::min(distance, octave_distance / scale), amplitude);
2050 distance = mix(distance, math::min(distance, lerp_distance), remainder);
2051 }
2052 }
2053 }
2054
2055 if (params.normalize) {
2056 distance /= max_amplitude;
2057 }
2058
2059 return distance;
2060}
2061
2062/* Explicit function template instantiation */
2063
2065 const float coord,
2066 const bool calc_color);
2068 const float2 coord,
2069 const bool calc_color);
2071 const float3 coord,
2072 const bool calc_color);
2074 const float4 coord,
2075 const bool calc_color);
2076
2078 const float coord);
2080 const float2 coord);
2082 const float3 coord);
2084 const float4 coord);
2086
2087/* -------------------------------------------------------------------- */
2106
2107/* The original Gabor noise paper specifies that the impulses count for each cell should be
2108 * computed by sampling a Poisson distribution whose mean is the impulse density. However,
2109 * Tavernier's paper showed that stratified Poisson point sampling is better assuming the weights
2110 * are sampled using a Bernoulli distribution, as shown in Figure (3). By stratified sampling, they
2111 * mean a constant number of impulses per cell, so the stratification is the grid itself in that
2112 * sense, as described in the supplementary material of the paper. */
2113static constexpr int gabor_impulses_count = 8;
2114
2115/* Computes a 2D Gabor kernel based on Equation (6) in the original Gabor noise paper. Where the
2116 * frequency argument is the F_0 parameter and the orientation argument is the w_0 parameter. We
2117 * assume the Gaussian envelope has a unit magnitude, that is, K = 1. That is because we will
2118 * eventually normalize the final noise value to the unit range, so the multiplication by the
2119 * magnitude will be canceled by the normalization. Further, we also assume a unit Gaussian width,
2120 * that is, a = 1. That is because it does not provide much artistic control. It follows that the
2121 * Gaussian will be truncated at pi.
2122 *
2123 * To avoid the discontinuities caused by the aforementioned truncation, the Gaussian is windowed
2124 * using a Hann window, that is because contrary to the claim made in the original Gabor paper,
2125 * truncating the Gaussian produces significant artifacts especially when differentiated for bump
2126 * mapping. The Hann window is C1 continuous and has limited effect on the shape of the Gaussian,
2127 * so it felt like an appropriate choice.
2128 *
2129 * Finally, instead of computing the Gabor value directly, we instead use the complex phasor
2130 * formulation described in section 3.1.1 in Tricard's paper. That's done to be able to compute the
2131 * phase and intensity of the Gabor noise after summation based on equations (8) and (9). The
2132 * return value of the Gabor kernel function is then a complex number whose real value is the
2133 * value computed in the original Gabor noise paper, and whose imaginary part is the sine
2134 * counterpart of the real part, which is the only extra computation in the new formulation.
2135 *
2136 * Note that while the original Gabor noise paper uses the cosine part of the phasor, that is, the
2137 * real part of the phasor, we use the sine part instead, that is, the imaginary part of the
2138 * phasor, as suggested by Tavernier's paper in "Section 3.3. Instance stationarity and
2139 * normalization", to ensure a zero mean, which should help with normalization. */
2141 const float frequency,
2142 const float orientation)
2143{
2144 const float distance_squared = math::length_squared(position);
2145 const float hann_window = 0.5f + 0.5f * math::cos(math::numbers::pi * distance_squared);
2146 const float gaussian_envelop = math::exp(-math::numbers::pi * distance_squared);
2147 const float windowed_gaussian_envelope = gaussian_envelop * hann_window;
2148
2149 const float2 frequency_vector = frequency * float2(cos(orientation), sin(orientation));
2150 const float angle = 2.0f * math::numbers::pi * math::dot(position, frequency_vector);
2151 const float2 phasor = float2(math::cos(angle), math::sin(angle));
2152
2153 return windowed_gaussian_envelope * phasor;
2154}
2155
2192{
2193 const float integral_of_gabor_squared = 0.25f;
2194 const float second_moment = 0.5f;
2195 return math::sqrt(gabor_impulses_count * second_moment * integral_of_gabor_squared);
2196}
2197
2198/* Computes the Gabor noise value at the given position for the given cell. This is essentially the
2199 * sum in Equation (8) in the original Gabor noise paper, where we sum Gabor kernels sampled at a
2200 * random position with a random weight. The orientation of the kernel is constant for anisotropic
2201 * noise while it is random for isotropic noise. The original Gabor noise paper mentions that the
2202 * weights should be uniformly distributed in the [-1, 1] range, however, Tavernier's paper showed
2203 * that using a Bernoulli distribution yields better results, so that is what we do. */
2205 const float2 position,
2206 const float frequency,
2207 const float isotropy,
2208 const float base_orientation)
2209
2210{
2211 float2 noise(0.0f);
2212 for (const int i : IndexRange(gabor_impulses_count)) {
2213 /* Compute unique seeds for each of the needed random variables. */
2214 const float3 seed_for_orientation(cell.x, cell.y, i * 3);
2215 const float3 seed_for_kernel_center(cell.x, cell.y, i * 3 + 1);
2216 const float3 seed_for_weight(cell.x, cell.y, i * 3 + 2);
2217
2218 /* For isotropic noise, add a random orientation amount, while for anisotropic noise, use the
2219 * base orientation. Linearly interpolate between the two cases using the isotropy factor. Note
2220 * that the random orientation range spans pi as opposed to two pi, that's because the Gabor
2221 * kernel is symmetric around pi. */
2222 const float random_orientation = (noise::hash_float_to_float(seed_for_orientation) - 0.5f) *
2224 const float orientation = base_orientation + random_orientation * isotropy;
2225
2226 const float2 kernel_center = noise::hash_float_to_float2(seed_for_kernel_center);
2227 const float2 position_in_kernel_space = position - kernel_center;
2228
2229 /* The kernel is windowed beyond the unit distance, so early exit with a zero for points that
2230 * are further than a unit radius. */
2231 if (math::length_squared(position_in_kernel_space) >= 1.0f) {
2232 continue;
2233 }
2234
2235 /* We either add or subtract the Gabor kernel based on a Bernoulli distribution of equal
2236 * probability. */
2237 const float weight = noise::hash_float_to_float(seed_for_weight) < 0.5f ? -1.0f : 1.0f;
2238
2239 noise += weight * compute_2d_gabor_kernel(position_in_kernel_space, frequency, orientation);
2240 }
2241 return noise;
2242}
2243
2244/* Computes the Gabor noise value by dividing the space into a grid and evaluating the Gabor noise
2245 * in the space of each cell of the 3x3 cell neighborhood. */
2246static float2 compute_2d_gabor_noise(const float2 coordinates,
2247 const float frequency,
2248 const float isotropy,
2249 const float base_orientation)
2250{
2251 const float2 cell_position = math::floor(coordinates);
2252 const float2 local_position = coordinates - cell_position;
2253
2254 float2 sum(0.0f);
2255 for (int j = -1; j <= 1; j++) {
2256 for (int i = -1; i <= 1; i++) {
2257 const float2 cell_offset = float2(i, j);
2258 const float2 current_cell_position = cell_position + cell_offset;
2259 const float2 position_in_cell_space = local_position - cell_offset;
2261 current_cell_position, position_in_cell_space, frequency, isotropy, base_orientation);
2262 }
2263 }
2264
2265 return sum;
2266}
2267
2268/* Identical to compute_2d_gabor_kernel, except it is evaluated in 3D space. Notice that Equation
2269 * (6) in the original Gabor noise paper computes the frequency vector using (cos(w_0), sin(w_0)),
2270 * which we also do in the 2D variant, however, for 3D, the orientation is already a unit frequency
2271 * vector, so we just need to scale it by the frequency value. */
2273 const float frequency,
2274 const float3 orientation)
2275{
2276 const float distance_squared = math::length_squared(position);
2277 const float hann_window = 0.5f + 0.5f * math::cos(math::numbers::pi * distance_squared);
2278 const float gaussian_envelop = math::exp(-math::numbers::pi * distance_squared);
2279 const float windowed_gaussian_envelope = gaussian_envelop * hann_window;
2280
2281 const float3 frequency_vector = frequency * orientation;
2282 const float angle = 2.0f * math::numbers::pi * math::dot(position, frequency_vector);
2283 const float2 phasor = float2(math::cos(angle), math::sin(angle));
2284
2285 return windowed_gaussian_envelope * phasor;
2286}
2287
2288/* Identical to compute_2d_gabor_standard_deviation except we do triple integration in 3D. The only
2289 * difference is the denominator in the integral expression, which is 2^{5 / 2} for the 3D case
2290 * instead of 4 for the 2D case. Similarly, the limit evaluates to 1 / (4 * sqrt(2)). */
2292{
2293 const float integral_of_gabor_squared = 1.0f / (4.0f * math::numbers::sqrt2);
2294 const float second_moment = 0.5f;
2295 return math::sqrt(gabor_impulses_count * second_moment * integral_of_gabor_squared);
2296}
2297
2298/* Computes the orientation of the Gabor kernel such that it is constant for anisotropic
2299 * noise while it is random for isotropic noise. We randomize in spherical coordinates for a
2300 * uniform distribution. */
2301static float3 compute_3d_orientation(const float3 orientation,
2302 const float isotropy,
2303 const float4 seed)
2304{
2305 /* Return the base orientation in case we are completely anisotropic. */
2306 if (isotropy == 0.0) {
2307 return orientation;
2308 }
2309
2310 /* Compute the orientation in spherical coordinates. */
2311 float inclination = math::acos(orientation.z);
2312 float azimuth = math::sign(orientation.y) *
2313 math::acos(orientation.x / math::length(float2(orientation.x, orientation.y)));
2314
2315 /* For isotropic noise, add a random orientation amount, while for anisotropic noise, use the
2316 * base orientation. Linearly interpolate between the two cases using the isotropy factor. Note
2317 * that the random orientation range is to pi as opposed to two pi, that's because the Gabor
2318 * kernel is symmetric around pi. */
2320 inclination += random_angles.x * isotropy;
2321 azimuth += random_angles.y * isotropy;
2322
2323 /* Convert back to Cartesian coordinates, */
2324 return float3(math::sin(inclination) * math::cos(azimuth),
2325 math::sin(inclination) * math::sin(azimuth),
2326 math::cos(inclination));
2327}
2328
2330 const float3 position,
2331 const float frequency,
2332 const float isotropy,
2333 const float3 base_orientation)
2334
2335{
2336 float2 noise(0.0f);
2337 for (const int i : IndexRange(gabor_impulses_count)) {
2338 /* Compute unique seeds for each of the needed random variables. */
2339 const float4 seed_for_orientation(cell.x, cell.y, cell.z, i * 3);
2340 const float4 seed_for_kernel_center(cell.x, cell.y, cell.z, i * 3 + 1);
2341 const float4 seed_for_weight(cell.x, cell.y, cell.z, i * 3 + 2);
2342
2343 const float3 orientation = compute_3d_orientation(
2344 base_orientation, isotropy, seed_for_orientation);
2345
2346 const float3 kernel_center = noise::hash_float_to_float3(seed_for_kernel_center);
2347 const float3 position_in_kernel_space = position - kernel_center;
2348
2349 /* The kernel is windowed beyond the unit distance, so early exit with a zero for points that
2350 * are further than a unit radius. */
2351 if (math::length_squared(position_in_kernel_space) >= 1.0f) {
2352 continue;
2353 }
2354
2355 /* We either add or subtract the Gabor kernel based on a Bernoulli distribution of equal
2356 * probability. */
2357 const float weight = noise::hash_float_to_float(seed_for_weight) < 0.5f ? -1.0f : 1.0f;
2358
2359 noise += weight * compute_3d_gabor_kernel(position_in_kernel_space, frequency, orientation);
2360 }
2361 return noise;
2362}
2363
2364/* Identical to compute_2d_gabor_noise but works in the 3D neighborhood of the noise. */
2365static float2 compute_3d_gabor_noise(const float3 coordinates,
2366 const float frequency,
2367 const float isotropy,
2368 const float3 base_orientation)
2369{
2370 const float3 cell_position = math::floor(coordinates);
2371 const float3 local_position = coordinates - cell_position;
2372
2373 float2 sum(0.0f);
2374 for (int k = -1; k <= 1; k++) {
2375 for (int j = -1; j <= 1; j++) {
2376 for (int i = -1; i <= 1; i++) {
2377 const float3 cell_offset = float3(i, j, k);
2378 const float3 current_cell_position = cell_position + cell_offset;
2379 const float3 position_in_cell_space = local_position - cell_offset;
2381 current_cell_position, position_in_cell_space, frequency, isotropy, base_orientation);
2382 }
2383 }
2384 }
2385
2386 return sum;
2387}
2388
2389void gabor(const float2 coordinates,
2390 const float scale,
2391 const float frequency,
2392 const float anisotropy,
2393 const float orientation,
2394 float *r_value,
2395 float *r_phase,
2396 float *r_intensity)
2397{
2398 const float2 scaled_coordinates = coordinates * scale;
2399 const float isotropy = 1.0f - math::clamp(anisotropy, 0.0f, 1.0f);
2400 const float sanitized_frequency = math::max(0.001f, frequency);
2401
2402 const float2 phasor = compute_2d_gabor_noise(
2403 scaled_coordinates, sanitized_frequency, isotropy, orientation);
2404 const float standard_deviation = compute_2d_gabor_standard_deviation();
2405
2406 /* Normalize the noise by dividing by six times the standard deviation, which was determined
2407 * empirically. */
2408 const float normalization_factor = 6.0f * standard_deviation;
2409
2410 /* As discussed in compute_2d_gabor_kernel, we use the imaginary part of the phasor as the Gabor
2411 * value. But remap to [0, 1] from [-1, 1]. */
2412 if (r_value) {
2413 *r_value = (phasor.y / normalization_factor) * 0.5f + 0.5f;
2414 }
2415
2416 /* Compute the phase based on equation (9) in Tricard's paper. But remap the phase into the
2417 * [0, 1] range. */
2418 if (r_phase) {
2419 *r_phase = (math::atan2(phasor.y, phasor.x) + math::numbers::pi) / (2.0f * math::numbers::pi);
2420 }
2421
2422 /* Compute the intensity based on equation (8) in Tricard's paper. */
2423 if (r_intensity) {
2424 *r_intensity = math::length(phasor) / normalization_factor;
2425 }
2426}
2427
2428void gabor(const float3 coordinates,
2429 const float scale,
2430 const float frequency,
2431 const float anisotropy,
2432 const float3 orientation,
2433 float *r_value,
2434 float *r_phase,
2435 float *r_intensity)
2436{
2437 const float3 scaled_coordinates = coordinates * scale;
2438 const float isotropy = 1.0f - math::clamp(anisotropy, 0.0f, 1.0f);
2439 const float sanitized_frequency = math::max(0.001f, frequency);
2440
2441 const float3 normalized_orientation = math::normalize(orientation);
2442 const float2 phasor = compute_3d_gabor_noise(
2443 scaled_coordinates, sanitized_frequency, isotropy, normalized_orientation);
2444 const float standard_deviation = compute_3d_gabor_standard_deviation();
2445
2446 /* Normalize the noise by dividing by six times the standard deviation, which was determined
2447 * empirically. */
2448 const float normalization_factor = 6.0f * standard_deviation;
2449
2450 /* As discussed in compute_2d_gabor_kernel, we use the imaginary part of the phasor as the Gabor
2451 * value. But remap to [0, 1] from [-1, 1]. */
2452 if (r_value) {
2453 *r_value = (phasor.y / normalization_factor) * 0.5f + 0.5f;
2454 }
2455
2456 /* Compute the phase based on equation (9) in Tricard's paper. But remap the phase into the
2457 * [0, 1] range. */
2458 if (r_phase) {
2459 *r_phase = (math::atan2(phasor.y, phasor.x) + math::numbers::pi) / (2.0f * math::numbers::pi);
2460 }
2461
2462 /* Compute the intensity based on equation (8) in Tricard's paper. */
2463 if (r_intensity) {
2464 *r_intensity = math::length(phasor) / normalization_factor;
2465 }
2466}
2467
2469
2470} // namespace blender::noise
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_INLINE
#define BLI_NOINLINE
#define ELEM(...)
#define X
#define Z
#define Y
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static T sum(const btAlignedObjectArray< T > &items)
static unsigned long seed
Definition btSoftBody.h:39
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
#define sin
#define cos
VecBase< float, D > normalize(VecOp< float, D >) RET
#define output
float distance(VecOp< float, D >, VecOp< float, D >) RET
#define mix(a, b, c)
Definition hash.h:35
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
MINLINE float smoothstep(float edge0, float edge1, float x)
#define T
T length_squared(const VecBase< T, Size > &a)
T cos(const AngleRadianBase< T > &a)
T clamp(const T &a, const T &min, const T &max)
T acos(const T &a)
T sqrt(const T &a)
T sign(const T &a)
T floor(const T &a)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T exp(const T &x)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T min(const T &a, const T &b)
T atan2(const T &y, const T &x)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
T mod(const T &a, const T &b)
T abs(const T &a)
template VoronoiOutput fractal_voronoi_x_fx< float4 >(const VoronoiParams &params, const float4 coord, const bool calc_color)
template float fractal_voronoi_distance_to_edge< float >(const VoronoiParams &params, const float coord)
float3 perlin_float3_fractal_distorted(float position, float detail, float roughness, float lacunarity, float offset, float gain, float distortion, int type, bool normalize)
Definition noise.cc:903
BLI_INLINE float negate_if(float value, uint32_t condition)
Definition noise.cc:369
BLI_INLINE float uint_to_float_01(uint32_t k)
Definition noise.cc:166
float perlin_ridged_multi_fractal(T p, const float detail, const float roughness, const float lacunarity, const float offset, const float gain)
Definition noise.cc:718
@ NOISE_SHD_VORONOI_N_SPHERE_RADIUS
Definition noise.cc:1063
@ NOISE_SHD_VORONOI_SMOOTH_F1
Definition noise.cc:1061
@ NOISE_SHD_VORONOI_F2
Definition noise.cc:1060
@ NOISE_SHD_VORONOI_DISTANCE_TO_EDGE
Definition noise.cc:1062
@ NOISE_SHD_VORONOI_F1
Definition noise.cc:1059
static float2 compute_3d_gabor_noise(const float3 coordinates, const float frequency, const float isotropy, const float3 base_orientation)
Definition noise.cc:2365
uint32_t hash_float(float kx)
Definition noise.cc:139
static float compute_3d_gabor_standard_deviation()
Definition noise.cc:2291
template float perlin_fbm< float3 >(float3 p, const float detail, const float roughness, const float lacunarity, const bool normalize)
static float2 compute_2d_gabor_noise_cell(const float2 cell, const float2 position, const float frequency, const float isotropy, const float base_orientation)
Definition noise.cc:2204
BLI_INLINE float perlin_distortion(float position, float strength)
Definition noise.cc:820
float hash_float_to_float(float k)
Definition noise.cc:193
float perlin_signed(float position)
Definition noise.cc:516
void gabor(const float2 coordinates, const float scale, const float frequency, const float anisotropy, const float orientation, float *r_value, float *r_phase, float *r_intensity)
Definition noise.cc:2389
template float perlin_fractal_distorted< float >(float position, float detail, float roughness, float lacunarity, float offset, float gain, float distortion, int type, bool normalize)
BLI_INLINE void hash_bit_mix(uint32_t &a, uint32_t &b, uint32_t &c)
Definition noise.cc:37
float4 voronoi_position(const float coord)
Definition noise.cc:1150
BLI_INLINE float fade(float t)
Definition noise.cc:364
VoronoiOutput voronoi_smooth_f1(const VoronoiParams &params, const float coord, const bool calc_color)
Definition noise.cc:1182
float voronoi_n_sphere_radius(const VoronoiParams &params, const float coord)
Definition noise.cc:1272
static float2 compute_2d_gabor_noise(const float2 coordinates, const float frequency, const float isotropy, const float base_orientation)
Definition noise.cc:2246
float perlin(float position)
Definition noise.cc:567
BLI_INLINE float random_float_offset(float seed)
Definition noise.cc:792
static float voronoi_distance_bound(const T a, const T b, const VoronoiParams &params)
Definition noise.cc:1140
BLI_INLINE float floor_fraction(float x, int &i)
Definition noise.cc:407
float voronoi_distance_to_edge(const VoronoiParams &params, const float coord)
Definition noise.cc:1258
BLI_INLINE uint32_t float_as_uint(float f)
Definition noise.cc:129
template VoronoiOutput fractal_voronoi_x_fx< float3 >(const VoronoiParams &params, const float3 coord, const bool calc_color)
@ NOISE_SHD_PERLIN_MULTIFRACTAL
Definition noise.cc:746
@ NOISE_SHD_PERLIN_FBM
Definition noise.cc:747
@ NOISE_SHD_PERLIN_RIDGED_MULTIFRACTAL
Definition noise.cc:749
@ NOISE_SHD_PERLIN_HYBRID_MULTIFRACTAL
Definition noise.cc:748
@ NOISE_SHD_PERLIN_HETERO_TERRAIN
Definition noise.cc:750
template float perlin_fractal_distorted< float2 >(float2 position, float detail, float roughness, float lacunarity, float offset, float gain, float distortion, int type, bool normalize)
BLI_INLINE float3 random_float3_offset(float seed)
Definition noise.cc:803
static float2 compute_3d_gabor_noise_cell(const float3 cell, const float3 position, const float frequency, const float isotropy, const float3 base_orientation)
Definition noise.cc:2329
static float2 compute_2d_gabor_kernel(const float2 position, const float frequency, const float orientation)
Definition noise.cc:2140
VoronoiOutput fractal_voronoi_x_fx(const VoronoiParams &params, const T coord, const bool calc_color)
Definition noise.cc:1962
float voronoi_distance(const float a, const float b)
Definition noise.cc:1068
static float2 compute_3d_gabor_kernel(const float3 position, const float frequency, const float3 orientation)
Definition noise.cc:2272
@ NOISE_SHD_VORONOI_MANHATTAN
Definition noise.cc:1053
@ NOISE_SHD_VORONOI_MINKOWSKI
Definition noise.cc:1055
@ NOISE_SHD_VORONOI_EUCLIDEAN
Definition noise.cc:1052
@ NOISE_SHD_VORONOI_CHEBYCHEV
Definition noise.cc:1054
template float perlin_fractal_distorted< float4 >(float4 position, float detail, float roughness, float lacunarity, float offset, float gain, float distortion, int type, bool normalize)
BLI_INLINE float2 random_float2_offset(float seed)
Definition noise.cc:797
float perlin_fbm(T p, float detail, float roughness, float lacunarity, bool normalize)
Definition noise.cc:598
VoronoiOutput voronoi_f2(const VoronoiParams &params, const float coord)
Definition noise.cc:1220
float3 hash_float_to_float3(float k)
Definition noise.cc:230
uint32_t hash(uint32_t kx)
Definition noise.cc:77
template VoronoiOutput fractal_voronoi_x_fx< float >(const VoronoiParams &params, const float coord, const bool calc_color)
template float perlin_fbm< float2 >(float2 p, const float detail, const float roughness, const float lacunarity, const bool normalize)
BLI_INLINE float4 random_float4_offset(float seed)
Definition noise.cc:810
BLI_INLINE float perlin_noise(float position)
Definition noise.cc:414
float fractal_voronoi_distance_to_edge(const VoronoiParams &params, const T coord)
Definition noise.cc:2021
BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c)
Definition noise.cc:59
float4 hash_float_to_float4(float4 k)
Definition noise.cc:258
float perlin_hybrid_multi_fractal(T p, const float detail, const float roughness, const float lacunarity, const float offset, const float gain)
Definition noise.cc:686
static float compute_2d_gabor_standard_deviation()
Definition noise.cc:2191
float perlin_select(T p, float detail, float roughness, float lacunarity, float offset, float gain, int type, bool normalize)
Definition noise.cc:754
BLI_INLINE float noise_grad(uint32_t hash, float x)
Definition noise.cc:374
VoronoiOutput voronoi_f1(const VoronoiParams &params, const float coord)
Definition noise.cc:1155
float perlin_multi_fractal(T p, const float detail, const float roughness, const float lacunarity)
Definition noise.cc:640
template float fractal_voronoi_distance_to_edge< float3 >(const VoronoiParams &params, const float3 coord)
static constexpr int gabor_impulses_count
Definition noise.cc:2113
static T mix(T v0, T v1, float x)
Definition noise.cc:279
float perlin_fractal_distorted(T position, float detail, float roughness, float lacunarity, float offset, float gain, float distortion, int type, bool normalize)
Definition noise.cc:849
template float perlin_fractal_distorted< float3 >(float3 position, float detail, float roughness, float lacunarity, float offset, float gain, float distortion, int type, bool normalize)
template VoronoiOutput fractal_voronoi_x_fx< float2 >(const VoronoiParams &params, const float2 coord, const bool calc_color)
template float fractal_voronoi_distance_to_edge< float4 >(const VoronoiParams &params, const float4 coord)
BLI_INLINE uint32_t hash_bit_rotate(uint32_t x, uint32_t k)
Definition noise.cc:32
float perlin_hetero_terrain(T p, const float detail, const float roughness, const float lacunarity, const float offset)
Definition noise.cc:660
float2 hash_float_to_float2(float2 k)
Definition noise.cc:213
static float3 compute_3d_orientation(const float3 orientation, const float isotropy, const float4 seed)
Definition noise.cc:2301
float hash_to_float(uint32_t kx)
Definition noise.cc:171
template float fractal_voronoi_distance_to_edge< float2 >(const VoronoiParams &params, const float2 coord)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
#define hash
Definition noise_c.cc:154
#define FLT_MAX
Definition stdcycles.h:14
i
Definition text_draw.cc:230