Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib/intern/noise.cc')
-rw-r--r--source/blender/blenlib/intern/noise.cc1611
1 files changed, 1610 insertions, 1 deletions
diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc
index ce2e9594059..5fa2746d07f 100644
--- a/source/blender/blenlib/intern/noise.cc
+++ b/source/blender/blenlib/intern/noise.cc
@@ -46,12 +46,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <algorithm>
#include <cmath>
#include <cstdint>
#include "BLI_float2.hh"
#include "BLI_float3.hh"
#include "BLI_float4.hh"
+#include "BLI_math_base_safe.h"
#include "BLI_noise.hh"
#include "BLI_utildefines.h"
@@ -408,7 +410,7 @@ BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z)
{
uint32_t h = hash & 15u;
float u = h < 8u ? x : y;
- float vt = ((h == 12u) || (h == 14u)) ? x : z;
+ float vt = ELEM(h, 12u, 14u) ? x : z;
float v = h < 4u ? y : vt;
return negate_if(u, h & 1u) + negate_if(v, h & 2u);
}
@@ -755,4 +757,1611 @@ float3 perlin_float3_fractal_distorted(float4 position,
perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness));
}
+/* --------------
+ * Musgrave Noise
+ * --------------
+ */
+
+/* 1D Musgrave fBm
+ *
+ * H: fractal increment parameter
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ *
+ * from "Texturing and Modelling: A procedural approach"
+ */
+
+float musgrave_fBm(const float co, const float H, const float lacunarity, const float octaves)
+{
+ float p = co;
+ float value = 0.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value += perlin_signed(p) * pwr;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * perlin_signed(p) * pwr;
+ }
+
+ return value;
+}
+
+/* 1D Musgrave Multifractal
+ *
+ * H: highest fractal dimension
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ */
+
+float musgrave_multi_fractal(const float co,
+ const float H,
+ const float lacunarity,
+ const float octaves)
+{
+ float p = co;
+ float value = 1.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value *= (pwr * perlin_signed(p) + 1.0f);
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */
+ }
+
+ return value;
+}
+
+/* 1D Musgrave Heterogeneous Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hetero_terrain(
+ const float co, const float H, const float lacunarity, const float octaves, const float offset)
+{
+ float p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ /* first unscaled octave of function; later octaves are scaled */
+ float value = offset + perlin_signed(p);
+ p *= lacunarity;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += rmd * increment;
+ }
+
+ return value;
+}
+
+/* 1D Hybrid Additive/Multiplicative Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hybrid_multi_fractal(const float co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float value = perlin_signed(p) + offset;
+ float weight = gain * value;
+ p *= lacunarity;
+
+ for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+
+ float signal = (perlin_signed(p) + offset) * pwr;
+ pwr *= pwHL;
+ value += weight * signal;
+ weight *= gain * signal;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * ((perlin_signed(p) + offset) * pwr);
+ }
+
+ return value;
+}
+
+/* 1D Ridged Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_ridged_multi_fractal(const float co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ float value = signal;
+ float weight = 1.0f;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ p *= lacunarity;
+ weight = CLAMPIS(signal * gain, 0.0f, 1.0f);
+ signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ signal *= weight;
+ value += signal * pwr;
+ pwr *= pwHL;
+ }
+
+ return value;
+}
+
+/* 2D Musgrave fBm
+ *
+ * H: fractal increment parameter
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ *
+ * from "Texturing and Modelling: A procedural approach"
+ */
+
+float musgrave_fBm(const float2 co, const float H, const float lacunarity, const float octaves)
+{
+ float2 p = co;
+ float value = 0.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value += perlin_signed(p) * pwr;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * perlin_signed(p) * pwr;
+ }
+
+ return value;
+}
+
+/* 2D Musgrave Multifractal
+ *
+ * H: highest fractal dimension
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ */
+
+float musgrave_multi_fractal(const float2 co,
+ const float H,
+ const float lacunarity,
+ const float octaves)
+{
+ float2 p = co;
+ float value = 1.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value *= (pwr * perlin_signed(p) + 1.0f);
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */
+ }
+
+ return value;
+}
+
+/* 2D Musgrave Heterogeneous Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hetero_terrain(const float2 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset)
+{
+ float2 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ /* first unscaled octave of function; later octaves are scaled */
+ float value = offset + perlin_signed(p);
+ p *= lacunarity;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += rmd * increment;
+ }
+
+ return value;
+}
+
+/* 2D Hybrid Additive/Multiplicative Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hybrid_multi_fractal(const float2 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float2 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float value = perlin_signed(p) + offset;
+ float weight = gain * value;
+ p *= lacunarity;
+
+ for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+
+ float signal = (perlin_signed(p) + offset) * pwr;
+ pwr *= pwHL;
+ value += weight * signal;
+ weight *= gain * signal;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * ((perlin_signed(p) + offset) * pwr);
+ }
+
+ return value;
+}
+
+/* 2D Ridged Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_ridged_multi_fractal(const float2 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float2 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ float value = signal;
+ float weight = 1.0f;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ p *= lacunarity;
+ weight = CLAMPIS(signal * gain, 0.0f, 1.0f);
+ signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ signal *= weight;
+ value += signal * pwr;
+ pwr *= pwHL;
+ }
+
+ return value;
+}
+
+/* 3D Musgrave fBm
+ *
+ * H: fractal increment parameter
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ *
+ * from "Texturing and Modelling: A procedural approach"
+ */
+
+float musgrave_fBm(const float3 co, const float H, const float lacunarity, const float octaves)
+{
+ float3 p = co;
+ float value = 0.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value += perlin_signed(p) * pwr;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * perlin_signed(p) * pwr;
+ }
+
+ return value;
+}
+
+/* 3D Musgrave Multifractal
+ *
+ * H: highest fractal dimension
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ */
+
+float musgrave_multi_fractal(const float3 co,
+ const float H,
+ const float lacunarity,
+ const float octaves)
+{
+ float3 p = co;
+ float value = 1.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value *= (pwr * perlin_signed(p) + 1.0f);
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */
+ }
+
+ return value;
+}
+
+/* 3D Musgrave Heterogeneous Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hetero_terrain(const float3 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset)
+{
+ float3 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ /* first unscaled octave of function; later octaves are scaled */
+ float value = offset + perlin_signed(p);
+ p *= lacunarity;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += rmd * increment;
+ }
+
+ return value;
+}
+
+/* 3D Hybrid Additive/Multiplicative Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hybrid_multi_fractal(const float3 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float3 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float value = perlin_signed(p) + offset;
+ float weight = gain * value;
+ p *= lacunarity;
+
+ for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+
+ float signal = (perlin_signed(p) + offset) * pwr;
+ pwr *= pwHL;
+ value += weight * signal;
+ weight *= gain * signal;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * ((perlin_signed(p) + offset) * pwr);
+ }
+
+ return value;
+}
+
+/* 3D Ridged Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_ridged_multi_fractal(const float3 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float3 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ float value = signal;
+ float weight = 1.0f;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ p *= lacunarity;
+ weight = CLAMPIS(signal * gain, 0.0f, 1.0f);
+ signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ signal *= weight;
+ value += signal * pwr;
+ pwr *= pwHL;
+ }
+
+ return value;
+}
+
+/* 4D Musgrave fBm
+ *
+ * H: fractal increment parameter
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ *
+ * from "Texturing and Modelling: A procedural approach"
+ */
+
+float musgrave_fBm(const float4 co, const float H, const float lacunarity, const float octaves)
+{
+ float4 p = co;
+ float value = 0.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value += perlin_signed(p) * pwr;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * perlin_signed(p) * pwr;
+ }
+
+ return value;
+}
+
+/* 4D Musgrave Multifractal
+ *
+ * H: highest fractal dimension
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ */
+
+float musgrave_multi_fractal(const float4 co,
+ const float H,
+ const float lacunarity,
+ const float octaves)
+{
+ float4 p = co;
+ float value = 1.0f;
+ float pwr = 1.0f;
+ const float pwHL = powf(lacunarity, -H);
+
+ for (int i = 0; i < (int)octaves; i++) {
+ value *= (pwr * perlin_signed(p) + 1.0f);
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */
+ }
+
+ return value;
+}
+
+/* 4D Musgrave Heterogeneous Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hetero_terrain(const float4 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset)
+{
+ float4 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ /* first unscaled octave of function; later octaves are scaled */
+ float value = offset + perlin_signed(p);
+ p *= lacunarity;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ float increment = (perlin_signed(p) + offset) * pwr * value;
+ value += rmd * increment;
+ }
+
+ return value;
+}
+
+/* 4D Hybrid Additive/Multiplicative Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_hybrid_multi_fractal(const float4 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float4 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float value = perlin_signed(p) + offset;
+ float weight = gain * value;
+ p *= lacunarity;
+
+ for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+
+ float signal = (perlin_signed(p) + offset) * pwr;
+ pwr *= pwHL;
+ value += weight * signal;
+ weight *= gain * signal;
+ p *= lacunarity;
+ }
+
+ const float rmd = octaves - floorf(octaves);
+ if (rmd != 0.0f) {
+ value += rmd * ((perlin_signed(p) + offset) * pwr);
+ }
+
+ return value;
+}
+
+/* 4D Ridged Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float musgrave_ridged_multi_fractal(const float4 co,
+ const float H,
+ const float lacunarity,
+ const float octaves,
+ const float offset,
+ const float gain)
+{
+ float4 p = co;
+ const float pwHL = powf(lacunarity, -H);
+ float pwr = pwHL;
+
+ float signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ float value = signal;
+ float weight = 1.0f;
+
+ for (int i = 1; i < (int)octaves; i++) {
+ p *= lacunarity;
+ weight = CLAMPIS(signal * gain, 0.0f, 1.0f);
+ signal = offset - fabsf(perlin_signed(p));
+ signal *= signal;
+ signal *= weight;
+ value += signal * pwr;
+ pwr *= pwHL;
+ }
+
+ return value;
+}
+
+/*
+ * Voronoi: Ported from Cycles code.
+ *
+ * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
+ *
+ * Smooth Voronoi:
+ *
+ * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
+ *
+ * Distance To Edge based on:
+ *
+ * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
+ * - https://www.shadertoy.com/view/ldl3W8
+ *
+ * With optimization to change -2..2 scan window to -1..1 for better performance,
+ * as explained in https://www.shadertoy.com/view/llG3zy.
+ */
+
+/* **** 1D Voronoi **** */
+
+/* Ensure to align with DNA. */
+enum {
+ NOISE_SHD_VORONOI_EUCLIDEAN = 0,
+ NOISE_SHD_VORONOI_MANHATTAN = 1,
+ NOISE_SHD_VORONOI_CHEBYCHEV = 2,
+ NOISE_SHD_VORONOI_MINKOWSKI = 3,
+};
+
+BLI_INLINE float voronoi_distance(const float a, const float b)
+{
+ return fabsf(b - a);
+}
+
+void voronoi_f1(
+ const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w)
+{
+ const float cellPosition = floorf(w);
+ const float localPosition = w - cellPosition;
+
+ float minDistance = 8.0f;
+ float targetOffset = 0.0f;
+ float targetPosition = 0.0f;
+ for (int i = -1; i <= 1; i++) {
+ const float cellOffset = i;
+ const float pointPosition = cellOffset +
+ hash_float_to_float(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(pointPosition, localPosition);
+ if (distanceToPoint < minDistance) {
+ targetOffset = cellOffset;
+ minDistance = distanceToPoint;
+ targetPosition = pointPosition;
+ }
+ }
+ *r_distance = minDistance;
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ *r_w = targetPosition + cellPosition;
+}
+
+void voronoi_smooth_f1(const float w,
+ const float smoothness,
+ const float randomness,
+ float *r_distance,
+ float3 *r_color,
+ float *r_w)
+{
+ const float cellPosition = floorf(w);
+ const float localPosition = w - cellPosition;
+
+ float smoothDistance = 8.0f;
+ float smoothPosition = 0.0f;
+ float3 smoothColor = float3(0.0f, 0.0f, 0.0f);
+ for (int i = -2; i <= 2; i++) {
+ const float cellOffset = i;
+ const float pointPosition = cellOffset +
+ hash_float_to_float(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(pointPosition, localPosition);
+ const float h = smoothstep(
+ 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
+ float correctionFactor = smoothness * h * (1.0f - h);
+ smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+ }
+ *r_distance = smoothDistance;
+ *r_color = smoothColor;
+ *r_w = cellPosition + smoothPosition;
+}
+
+void voronoi_f2(
+ const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w)
+{
+ const float cellPosition = floorf(w);
+ const float localPosition = w - cellPosition;
+
+ float distanceF1 = 8.0f;
+ float distanceF2 = 8.0f;
+ float offsetF1 = 0.0f;
+ float positionF1 = 0.0f;
+ float offsetF2 = 0.0f;
+ float positionF2 = 0.0f;
+ for (int i = -1; i <= 1; i++) {
+ const float cellOffset = i;
+ const float pointPosition = cellOffset +
+ hash_float_to_float(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(pointPosition, localPosition);
+ if (distanceToPoint < distanceF1) {
+ distanceF2 = distanceF1;
+ distanceF1 = distanceToPoint;
+ offsetF2 = offsetF1;
+ offsetF1 = cellOffset;
+ positionF2 = positionF1;
+ positionF1 = pointPosition;
+ }
+ else if (distanceToPoint < distanceF2) {
+ distanceF2 = distanceToPoint;
+ offsetF2 = cellOffset;
+ positionF2 = pointPosition;
+ }
+ }
+ *r_distance = distanceF2;
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ *r_w = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance)
+{
+ const float cellPosition = floorf(w);
+ const float localPosition = w - cellPosition;
+
+ const float midPointPosition = hash_float_to_float(cellPosition) * randomness;
+ const float leftPointPosition = -1.0f + hash_float_to_float(cellPosition - 1.0f) * randomness;
+ const float rightPointPosition = 1.0f + hash_float_to_float(cellPosition + 1.0f) * randomness;
+ const float distanceToMidLeft = fabsf((midPointPosition + leftPointPosition) / 2.0f -
+ localPosition);
+ const float distanceToMidRight = fabsf((midPointPosition + rightPointPosition) / 2.0f -
+ localPosition);
+
+ *r_distance = std::min(distanceToMidLeft, distanceToMidRight);
+}
+
+void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius)
+{
+ const float cellPosition = floorf(w);
+ const float localPosition = w - cellPosition;
+
+ float closestPoint = 0.0f;
+ float closestPointOffset = 0.0f;
+ float minDistance = 8.0f;
+ for (int i = -1; i <= 1; i++) {
+ const float cellOffset = i;
+ const float pointPosition = cellOffset +
+ hash_float_to_float(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = fabsf(pointPosition - localPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPoint = pointPosition;
+ closestPointOffset = cellOffset;
+ }
+ }
+
+ minDistance = 8.0f;
+ float closestPointToClosestPoint = 0.0f;
+ for (int i = -1; i <= 1; i++) {
+ if (i == 0) {
+ continue;
+ }
+ const float cellOffset = i + closestPointOffset;
+ const float pointPosition = cellOffset +
+ hash_float_to_float(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = fabsf(closestPoint - pointPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPointToClosestPoint = pointPosition;
+ }
+ }
+ *r_radius = fabsf(closestPointToClosestPoint - closestPoint) / 2.0f;
+}
+
+/* **** 2D Voronoi **** */
+
+static float voronoi_distance(const float2 a,
+ const float2 b,
+ const int metric,
+ const float exponent)
+{
+ switch (metric) {
+ case NOISE_SHD_VORONOI_EUCLIDEAN:
+ return float2::distance(a, b);
+ case NOISE_SHD_VORONOI_MANHATTAN:
+ return fabsf(a.x - b.x) + fabsf(a.y - b.y);
+ case NOISE_SHD_VORONOI_CHEBYCHEV:
+ return std::max(fabsf(a.x - b.x), fabsf(a.y - b.y));
+ case NOISE_SHD_VORONOI_MINKOWSKI:
+ return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent),
+ 1.0f / exponent);
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ return 0.0f;
+}
+
+void voronoi_f1(const float2 coord,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float2 *r_position)
+{
+ const float2 cellPosition = float2::floor(coord);
+ const float2 localPosition = coord - cellPosition;
+
+ float minDistance = 8.0f;
+ float2 targetOffset = float2(0.0f, 0.0f);
+ float2 targetPosition = float2(0.0f, 0.0f);
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float2 cellOffset = float2(i, j);
+ const float2 pointPosition = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness;
+ float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+ if (distanceToPoint < minDistance) {
+ targetOffset = cellOffset;
+ minDistance = distanceToPoint;
+ targetPosition = pointPosition;
+ }
+ }
+ }
+ *r_distance = minDistance;
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ *r_position = targetPosition + cellPosition;
+}
+
+void voronoi_smooth_f1(const float2 coord,
+ const float smoothness,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float2 *r_position)
+{
+ const float2 cellPosition = float2::floor(coord);
+ const float2 localPosition = coord - cellPosition;
+
+ float smoothDistance = 8.0f;
+ float3 smoothColor = float3(0.0f, 0.0f, 0.0f);
+ float2 smoothPosition = float2(0.0f, 0.0f);
+ for (int j = -2; j <= 2; j++) {
+ for (int i = -2; i <= 2; i++) {
+ const float2 cellOffset = float2(i, j);
+ const float2 pointPosition = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ const float h = smoothstep(
+ 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
+ float correctionFactor = smoothness * h * (1.0f - h);
+ smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - correctionFactor;
+ }
+ }
+ *r_distance = smoothDistance;
+ *r_color = smoothColor;
+ *r_position = cellPosition + smoothPosition;
+}
+
+void voronoi_f2(const float2 coord,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float2 *r_position)
+{
+ const float2 cellPosition = float2::floor(coord);
+ const float2 localPosition = coord - cellPosition;
+
+ float distanceF1 = 8.0f;
+ float distanceF2 = 8.0f;
+ float2 offsetF1 = float2(0.0f, 0.0f);
+ float2 positionF1 = float2(0.0f, 0.0f);
+ float2 offsetF2 = float2(0.0f, 0.0f);
+ float2 positionF2 = float2(0.0f, 0.0f);
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float2 cellOffset = float2(i, j);
+ const float2 pointPosition = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ if (distanceToPoint < distanceF1) {
+ distanceF2 = distanceF1;
+ distanceF1 = distanceToPoint;
+ offsetF2 = offsetF1;
+ offsetF1 = cellOffset;
+ positionF2 = positionF1;
+ positionF1 = pointPosition;
+ }
+ else if (distanceToPoint < distanceF2) {
+ distanceF2 = distanceToPoint;
+ offsetF2 = cellOffset;
+ positionF2 = pointPosition;
+ }
+ }
+ }
+ *r_distance = distanceF2;
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ *r_position = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance)
+{
+ const float2 cellPosition = float2::floor(coord);
+ const float2 localPosition = coord - cellPosition;
+
+ float2 vectorToClosest = float2(0.0f, 0.0f);
+ float minDistance = 8.0f;
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float2 cellOffset = float2(i, j);
+ const float2 vectorToPoint = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness -
+ localPosition;
+ const float distanceToPoint = dot_v2v2(vectorToPoint, vectorToPoint);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ vectorToClosest = vectorToPoint;
+ }
+ }
+ }
+
+ minDistance = 8.0f;
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float2 cellOffset = float2(i, j);
+ const float2 vectorToPoint = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness -
+ localPosition;
+ const float2 perpendicularToEdge = vectorToPoint - vectorToClosest;
+ if (dot_v2v2(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
+ const float distanceToEdge = dot_v2v2((vectorToClosest + vectorToPoint) / 2.0f,
+ perpendicularToEdge.normalized());
+ minDistance = std::min(minDistance, distanceToEdge);
+ }
+ }
+ }
+ *r_distance = minDistance;
+}
+
+void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius)
+{
+ const float2 cellPosition = float2::floor(coord);
+ const float2 localPosition = coord - cellPosition;
+
+ float2 closestPoint = float2(0.0f, 0.0f);
+ float2 closestPointOffset = float2(0.0f, 0.0f);
+ float minDistance = 8.0f;
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float2 cellOffset = float2(i, j);
+ const float2 pointPosition = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = float2::distance(pointPosition, localPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPoint = pointPosition;
+ closestPointOffset = cellOffset;
+ }
+ }
+ }
+
+ minDistance = 8.0f;
+ float2 closestPointToClosestPoint = float2(0.0f, 0.0f);
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ if (i == 0 && j == 0) {
+ continue;
+ }
+ const float2 cellOffset = float2(i, j) + closestPointOffset;
+ const float2 pointPosition = cellOffset +
+ hash_float_to_float2(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = float2::distance(closestPoint, pointPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPointToClosestPoint = pointPosition;
+ }
+ }
+ }
+ *r_radius = float2::distance(closestPointToClosestPoint, closestPoint) / 2.0f;
+}
+
+/* **** 3D Voronoi **** */
+
+static float voronoi_distance(const float3 a,
+ const float3 b,
+ const int metric,
+ const float exponent)
+{
+ switch (metric) {
+ case NOISE_SHD_VORONOI_EUCLIDEAN:
+ return float3::distance(a, b);
+ case NOISE_SHD_VORONOI_MANHATTAN:
+ return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z);
+ case NOISE_SHD_VORONOI_CHEBYCHEV:
+ return std::max(fabsf(a.x - b.x), std::max(fabsf(a.y - b.y), fabsf(a.z - b.z)));
+ case NOISE_SHD_VORONOI_MINKOWSKI:
+ return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) +
+ powf(fabsf(a.z - b.z), exponent),
+ 1.0f / exponent);
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ return 0.0f;
+}
+
+void voronoi_f1(const float3 coord,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float3 *r_position)
+{
+ const float3 cellPosition = float3::floor(coord);
+ const float3 localPosition = coord - cellPosition;
+
+ float minDistance = 8.0f;
+ float3 targetOffset = float3(0.0f, 0.0f, 0.0f);
+ float3 targetPosition = float3(0.0f, 0.0f, 0.0f);
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float3 cellOffset = float3(i, j, k);
+ const float3 pointPosition = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ if (distanceToPoint < minDistance) {
+ targetOffset = cellOffset;
+ minDistance = distanceToPoint;
+ targetPosition = pointPosition;
+ }
+ }
+ }
+ }
+ *r_distance = minDistance;
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ *r_position = targetPosition + cellPosition;
+}
+
+void voronoi_smooth_f1(const float3 coord,
+ const float smoothness,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float3 *r_position)
+{
+ const float3 cellPosition = float3::floor(coord);
+ const float3 localPosition = coord - cellPosition;
+
+ float smoothDistance = 8.0f;
+ float3 smoothColor = float3(0.0f, 0.0f, 0.0f);
+ float3 smoothPosition = float3(0.0f, 0.0f, 0.0f);
+ for (int k = -2; k <= 2; k++) {
+ for (int j = -2; j <= 2; j++) {
+ for (int i = -2; i <= 2; i++) {
+ const float3 cellOffset = float3(i, j, k);
+ const float3 pointPosition = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ const float h = smoothstep(
+ 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
+ float correctionFactor = smoothness * h * (1.0f - h);
+ smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - correctionFactor;
+ }
+ }
+ }
+ *r_distance = smoothDistance;
+ *r_color = smoothColor;
+ *r_position = cellPosition + smoothPosition;
+}
+
+void voronoi_f2(const float3 coord,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float3 *r_position)
+{
+ const float3 cellPosition = float3::floor(coord);
+ const float3 localPosition = coord - cellPosition;
+
+ float distanceF1 = 8.0f;
+ float distanceF2 = 8.0f;
+ float3 offsetF1 = float3(0.0f, 0.0f, 0.0f);
+ float3 positionF1 = float3(0.0f, 0.0f, 0.0f);
+ float3 offsetF2 = float3(0.0f, 0.0f, 0.0f);
+ float3 positionF2 = float3(0.0f, 0.0f, 0.0f);
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float3 cellOffset = float3(i, j, k);
+ const float3 pointPosition = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ if (distanceToPoint < distanceF1) {
+ distanceF2 = distanceF1;
+ distanceF1 = distanceToPoint;
+ offsetF2 = offsetF1;
+ offsetF1 = cellOffset;
+ positionF2 = positionF1;
+ positionF1 = pointPosition;
+ }
+ else if (distanceToPoint < distanceF2) {
+ distanceF2 = distanceToPoint;
+ offsetF2 = cellOffset;
+ positionF2 = pointPosition;
+ }
+ }
+ }
+ }
+ *r_distance = distanceF2;
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ *r_position = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance)
+{
+ const float3 cellPosition = float3::floor(coord);
+ const float3 localPosition = coord - cellPosition;
+
+ float3 vectorToClosest = float3(0.0f, 0.0f, 0.0f);
+ float minDistance = 8.0f;
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float3 cellOffset = float3(i, j, k);
+ const float3 vectorToPoint = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness -
+ localPosition;
+ const float distanceToPoint = dot_v3v3(vectorToPoint, vectorToPoint);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ vectorToClosest = vectorToPoint;
+ }
+ }
+ }
+ }
+
+ minDistance = 8.0f;
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float3 cellOffset = float3(i, j, k);
+ const float3 vectorToPoint = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness -
+ localPosition;
+ const float3 perpendicularToEdge = vectorToPoint - vectorToClosest;
+ if (dot_v3v3(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
+ const float distanceToEdge = dot_v3v3((vectorToClosest + vectorToPoint) / 2.0f,
+ perpendicularToEdge.normalized());
+ minDistance = std::min(minDistance, distanceToEdge);
+ }
+ }
+ }
+ }
+ *r_distance = minDistance;
+}
+
+void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius)
+{
+ const float3 cellPosition = float3::floor(coord);
+ const float3 localPosition = coord - cellPosition;
+
+ float3 closestPoint = float3(0.0f, 0.0f, 0.0f);
+ float3 closestPointOffset = float3(0.0f, 0.0f, 0.0f);
+ float minDistance = 8.0f;
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float3 cellOffset = float3(i, j, k);
+ const float3 pointPosition = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = float3::distance(pointPosition, localPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPoint = pointPosition;
+ closestPointOffset = cellOffset;
+ }
+ }
+ }
+ }
+
+ minDistance = 8.0f;
+ float3 closestPointToClosestPoint = float3(0.0f, 0.0f, 0.0f);
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ if (i == 0 && j == 0 && k == 0) {
+ continue;
+ }
+ const float3 cellOffset = float3(i, j, k) + closestPointOffset;
+ const float3 pointPosition = cellOffset +
+ hash_float_to_float3(cellPosition + cellOffset) * randomness;
+ const float distanceToPoint = float3::distance(closestPoint, pointPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPointToClosestPoint = pointPosition;
+ }
+ }
+ }
+ }
+ *r_radius = float3::distance(closestPointToClosestPoint, closestPoint) / 2.0f;
+}
+
+/* **** 4D Voronoi **** */
+
+static float voronoi_distance(const float4 a,
+ const float4 b,
+ const int metric,
+ const float exponent)
+{
+ switch (metric) {
+ case NOISE_SHD_VORONOI_EUCLIDEAN:
+ return float4::distance(a, b);
+ case NOISE_SHD_VORONOI_MANHATTAN:
+ return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z) + fabsf(a.w - b.w);
+ case NOISE_SHD_VORONOI_CHEBYCHEV:
+ return std::max(fabsf(a.x - b.x),
+ std::max(fabsf(a.y - b.y), std::max(fabsf(a.z - b.z), fabsf(a.w - b.w))));
+ case NOISE_SHD_VORONOI_MINKOWSKI:
+ return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) +
+ powf(fabsf(a.z - b.z), exponent) + powf(fabsf(a.w - b.w), exponent),
+ 1.0f / exponent);
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ return 0.0f;
+}
+
+void voronoi_f1(const float4 coord,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float4 *r_position)
+{
+ const float4 cellPosition = float4::floor(coord);
+ const float4 localPosition = coord - cellPosition;
+
+ float minDistance = 8.0f;
+ float4 targetOffset = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float4 targetPosition = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ for (int u = -1; u <= 1; u++) {
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float4 cellOffset = float4(i, j, k, u);
+ const float4 pointPosition = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ if (distanceToPoint < minDistance) {
+ targetOffset = cellOffset;
+ minDistance = distanceToPoint;
+ targetPosition = pointPosition;
+ }
+ }
+ }
+ }
+ }
+ *r_distance = minDistance;
+ *r_color = hash_float_to_float3(cellPosition + targetOffset);
+ *r_position = targetPosition + cellPosition;
+}
+
+void voronoi_smooth_f1(const float4 coord,
+ const float smoothness,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float4 *r_position)
+{
+ const float4 cellPosition = float4::floor(coord);
+ const float4 localPosition = coord - cellPosition;
+
+ float smoothDistance = 8.0f;
+ float3 smoothColor = float3(0.0f, 0.0f, 0.0f);
+ float4 smoothPosition = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ for (int u = -2; u <= 2; u++) {
+ for (int k = -2; k <= 2; k++) {
+ for (int j = -2; j <= 2; j++) {
+ for (int i = -2; i <= 2; i++) {
+ const float4 cellOffset = float4(i, j, k, u);
+ const float4 pointPosition = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ const float h = smoothstep(
+ 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness);
+ float correctionFactor = smoothness * h * (1.0f - h);
+ smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+ correctionFactor /= 1.0f + 3.0f * smoothness;
+ const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+ smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor;
+ smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) -
+ correctionFactor;
+ }
+ }
+ }
+ }
+ *r_distance = smoothDistance;
+ *r_color = smoothColor;
+ *r_position = cellPosition + smoothPosition;
+}
+
+void voronoi_f2(const float4 coord,
+ const float exponent,
+ const float randomness,
+ const int metric,
+ float *r_distance,
+ float3 *r_color,
+ float4 *r_position)
+{
+ const float4 cellPosition = float4::floor(coord);
+ const float4 localPosition = coord - cellPosition;
+
+ float distanceF1 = 8.0f;
+ float distanceF2 = 8.0f;
+ float4 offsetF1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float4 positionF1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float4 offsetF2 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float4 positionF2 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ for (int u = -1; u <= 1; u++) {
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float4 cellOffset = float4(i, j, k, u);
+ const float4 pointPosition = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness;
+ const float distanceToPoint = voronoi_distance(
+ pointPosition, localPosition, metric, exponent);
+ if (distanceToPoint < distanceF1) {
+ distanceF2 = distanceF1;
+ distanceF1 = distanceToPoint;
+ offsetF2 = offsetF1;
+ offsetF1 = cellOffset;
+ positionF2 = positionF1;
+ positionF1 = pointPosition;
+ }
+ else if (distanceToPoint < distanceF2) {
+ distanceF2 = distanceToPoint;
+ offsetF2 = cellOffset;
+ positionF2 = pointPosition;
+ }
+ }
+ }
+ }
+ }
+ *r_distance = distanceF2;
+ *r_color = hash_float_to_float3(cellPosition + offsetF2);
+ *r_position = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance)
+{
+ const float4 cellPosition = float4::floor(coord);
+ const float4 localPosition = coord - cellPosition;
+
+ float4 vectorToClosest = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float minDistance = 8.0f;
+ for (int u = -1; u <= 1; u++) {
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float4 cellOffset = float4(i, j, k, u);
+ const float4 vectorToPoint = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness -
+ localPosition;
+ const float distanceToPoint = dot_v4v4(vectorToPoint, vectorToPoint);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ vectorToClosest = vectorToPoint;
+ }
+ }
+ }
+ }
+ }
+
+ minDistance = 8.0f;
+ for (int u = -1; u <= 1; u++) {
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float4 cellOffset = float4(i, j, k, u);
+ const float4 vectorToPoint = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness -
+ localPosition;
+ const float4 perpendicularToEdge = vectorToPoint - vectorToClosest;
+ if (dot_v4v4(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
+ const float distanceToEdge = dot_v4v4((vectorToClosest + vectorToPoint) / 2.0f,
+ float4::normalize(perpendicularToEdge));
+ minDistance = std::min(minDistance, distanceToEdge);
+ }
+ }
+ }
+ }
+ }
+ *r_distance = minDistance;
+}
+
+void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius)
+{
+ const float4 cellPosition = float4::floor(coord);
+ const float4 localPosition = coord - cellPosition;
+
+ float4 closestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float4 closestPointOffset = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float minDistance = 8.0f;
+ for (int u = -1; u <= 1; u++) {
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ const float4 cellOffset = float4(i, j, k, u);
+ const float4 pointPosition = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness;
+ const float distanceToPoint = float4::distance(pointPosition, localPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPoint = pointPosition;
+ closestPointOffset = cellOffset;
+ }
+ }
+ }
+ }
+ }
+
+ minDistance = 8.0f;
+ float4 closestPointToClosestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ for (int u = -1; u <= 1; u++) {
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ if (i == 0 && j == 0 && k == 0 && u == 0) {
+ continue;
+ }
+ const float4 cellOffset = float4(i, j, k, u) + closestPointOffset;
+ const float4 pointPosition = cellOffset +
+ hash_float_to_float4(cellPosition + cellOffset) *
+ randomness;
+ const float distanceToPoint = float4::distance(closestPoint, pointPosition);
+ if (distanceToPoint < minDistance) {
+ minDistance = distanceToPoint;
+ closestPointToClosestPoint = pointPosition;
+ }
+ }
+ }
+ }
+ }
+ *r_radius = float4::distance(closestPointToClosestPoint, closestPoint) / 2.0f;
+}
+
} // namespace blender::noise