Shading: Add More Features To The Voronoi Node.
authorOmarSquircleArt <omar.squircleart@gmail.com>
Thu, 12 Sep 2019 11:09:31 +0000 (13:09 +0200)
committerOmarSquircleArt <omar.squircleart@gmail.com>
Thu, 12 Sep 2019 11:09:31 +0000 (13:09 +0200)
This patch allows the Voronoi node to operate in 1D, 2D, and 4D space.
It also adds a Randomness input to control the randomness of the texture.
Additionally, it adds three new modes of operation:

- Smooth F1: A smooth version of F1 Voronoi with no discontinuities.
- Distance To Edge: Returns the distance to the edges of the cells.
- N-Sphere Radius: Returns the radius of the n-sphere inscribed in
the cells. In other words, it is half the distance between the
closest feature point and the feature point closest to it.

And it removes the following three modes of operation:

- F3.
- F4.
- Cracks.

The Distance metric is now called Euclidean, and it computes the actual
euclidean distance as opposed to the old method of computing the squared
euclidean distance.

This breaks backward compatibility in many ways, including the base case.

Reviewers: brecht, JacquesLucke

Differential Revision: https://developer.blender.org/D5743

26 files changed:
intern/cycles/blender/blender_shader.cpp
intern/cycles/kernel/closure/bsdf_util.h
intern/cycles/kernel/shaders/CMakeLists.txt
intern/cycles/kernel/shaders/node_hash.h [new file with mode: 0644]
intern/cycles/kernel/shaders/node_voronoi_texture.osl
intern/cycles/kernel/svm/svm.h
intern/cycles/kernel/svm/svm_noise.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/kernel/svm/svm_voronoi.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
intern/cycles/util/util_math.h
intern/cycles/util/util_math_float2.h
intern/cycles/util/util_math_float3.h
intern/cycles/util/util_math_float4.h
source/blender/blenkernel/BKE_blender_version.h
source/blender/blenloader/intern/versioning_cycles.c
source/blender/editors/space_node/drawnode.c
source/blender/gpu/CMakeLists.txt
source/blender/gpu/intern/gpu_material_library.h
source/blender/gpu/shaders/material/gpu_shader_material_cell_noise.glsl [deleted file]
source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c

index db01167cc2a62e487f73503fcf5cba4fcc57b938..257c799a3e0eca084cb1303b7f0266abef13455d 100644 (file)
@@ -733,9 +733,9 @@ static ShaderNode *add_node(Scene *scene,
   else if (b_node.is_a(&RNA_ShaderNodeTexVoronoi)) {
     BL::ShaderNodeTexVoronoi b_voronoi_node(b_node);
     VoronoiTextureNode *voronoi = new VoronoiTextureNode();
-    voronoi->coloring = (NodeVoronoiColoring)b_voronoi_node.coloring();
-    voronoi->metric = (NodeVoronoiDistanceMetric)b_voronoi_node.distance();
+    voronoi->dimensions = b_voronoi_node.voronoi_dimensions();
     voronoi->feature = (NodeVoronoiFeature)b_voronoi_node.feature();
+    voronoi->metric = (NodeVoronoiDistanceMetric)b_voronoi_node.distance();
     BL::TexMapping b_texture_mapping(b_voronoi_node.texture_mapping());
     get_tex_mapping(&voronoi->tex_mapping, b_texture_mapping);
     node = voronoi;
index 3bce47caedb4fc81e8e219c995d70c0c76edbe74..a73dee1b04563eff7dbcf42bcd2e63bac50f073a 100644 (file)
@@ -134,20 +134,6 @@ ccl_device float schlick_fresnel(float u)
   return m2 * m2 * m;  // pow(m, 5)
 }
 
-ccl_device float smooth_step(float edge0, float edge1, float x)
-{
-  float result;
-  if (x < edge0)
-    result = 0.0f;
-  else if (x >= edge1)
-    result = 1.0f;
-  else {
-    float t = (x - edge0) / (edge1 - edge0);
-    result = (3.0f - 2.0f * t) * (t * t);
-  }
-  return result;
-}
-
 /* Calculate the fresnel color which is a blend between white and the F0 color (cspec0) */
 ccl_device_forceinline float3
 interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0)
index a45c43e01edb1ba794506202f98a0c31b20a73d8..38ba113a18404aa8a7e949238d23fea4e8870f65 100644 (file)
@@ -97,6 +97,7 @@ set(SRC_OSL
 set(SRC_OSL_HEADERS
   node_color.h
   node_fresnel.h
+  node_hash.h
   node_noise.h
   node_ramp_util.h
   stdosl.h
diff --git a/intern/cycles/kernel/shaders/node_hash.h b/intern/cycles/kernel/shaders/node_hash.h
new file mode 100644 (file)
index 0000000..7affe43
--- /dev/null
@@ -0,0 +1,81 @@
+#include "stdosl.h"
+#include "vector2.h"
+#include "vector4.h"
+
+#define vector3 point
+
+/* **** Hash a float or vector[234] into a float [0, 1] **** */
+
+float hash_float_to_float(float k)
+{
+  return hashnoise(k);
+}
+
+float hash_vector2_to_float(vector2 k)
+{
+  return hashnoise(k.x, k.y);
+}
+
+float hash_vector3_to_float(vector3 k)
+{
+  return hashnoise(k);
+}
+
+float hash_vector4_to_float(vector4 k)
+{
+  return hashnoise(vector3(k.x, k.y, k.z), k.w);
+}
+
+/* **** Hash a vector[234] into a vector[234] [0, 1] **** */
+
+vector2 hash_vector2_to_vector2(vector2 k)
+{
+  return vector2(hash_vector2_to_float(k), hash_vector3_to_float(vector3(k.x, k.y, 1.0)));
+}
+
+vector3 hash_vector3_to_vector3(vector3 k)
+{
+  return vector3(hash_vector3_to_float(k),
+                 hash_vector4_to_float(vector4(k[0], k[1], k[2], 1.0)),
+                 hash_vector4_to_float(vector4(k[0], k[1], k[2], 2.0)));
+}
+
+vector4 hash_vector4_to_vector4(vector4 k)
+{
+  return vector4(hash_vector4_to_float(k),
+                 hash_vector4_to_float(vector4(k.w, k.x, k.y, k.z)),
+                 hash_vector4_to_float(vector4(k.z, k.w, k.x, k.y)),
+                 hash_vector4_to_float(vector4(k.y, k.z, k.w, k.x)));
+}
+
+/* **** Hash a float or a vec[234] into a color [0, 1] **** */
+
+color hash_float_to_color(float k)
+{
+  return color(hash_float_to_float(k),
+               hash_vector2_to_float(vector2(k, 1.0)),
+               hash_vector2_to_float(vector2(k, 2.0)));
+}
+
+color hash_vector2_to_color(vector2 k)
+{
+  return color(hash_vector2_to_float(k),
+               hash_vector3_to_float(vector3(k.x, k.y, 1.0)),
+               hash_vector3_to_float(vector3(k.x, k.y, 2.0)));
+}
+
+color hash_vector3_to_color(vector3 k)
+{
+  return color(hash_vector3_to_float(k),
+               hash_vector4_to_float(vector4(k[0], k[1], k[2], 1.0)),
+               hash_vector4_to_float(vector4(k[0], k[1], k[2], 2.0)));
+}
+
+color hash_vector4_to_color(vector4 k)
+{
+  return color(hash_vector4_to_float(k),
+               hash_vector4_to_float(vector4(k.z, k.x, k.w, k.y)),
+               hash_vector4_to_float(vector4(k.w, k.z, k.y, k.x)));
+}
+
+#undef vector3
index 0d547b4b615c8b841faab36a72875f2984e49c0d..5de4aeef943b886e881f43e1c34487e5097fb1c2 100644 (file)
  */
 
 #include "stdosl.h"
+#include "vector2.h"
+#include "vector4.h"
+#include "node_hash.h"
 
-color cellnoise_color(point p)
+#define vector3 point
+
+/* **** Distance Functions **** */
+
+float distance(float a, float b)
+{
+  return abs(a - b);
+}
+
+float distance(vector2 a, vector2 b)
+{
+  return length(a - b);
+}
+
+float distance(vector4 a, vector4 b)
+{
+  return length(a - b);
+}
+
+/* **** Safe Division **** */
+
+vector2 safe_divide(vector2 a, float b)
+{
+  return vector2((b != 0.0) ? a.x / b : 0.0, (b != 0.0) ? a.y / b : 0.0);
+}
+
+vector4 safe_divide(vector4 a, float b)
 {
-  float r = cellnoise(p);
-  float g = cellnoise(point(p[1], p[0], p[2]));
-  float b = cellnoise(point(p[1], p[2], p[0]));
+  return vector4((b != 0.0) ? a.x / b : 0.0,
+                 (b != 0.0) ? a.y / b : 0.0,
+                 (b != 0.0) ? a.z / b : 0.0,
+                 (b != 0.0) ? a.w / b : 0.0);
+}
+
+/*
+ * Smooth Voronoi:
+ *
+ * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
+ *
+ * Distance To Edge:
+ *
+ * - https://www.shadertoy.com/view/llG3zy
+ *
+ */
 
-  return color(r, g, b);
+/* **** 1D Voronoi **** */
+
+float voronoi_distance(float a, float b, string metric, float exponent)
+{
+  return abs(a - b);
 }
 
-void voronoi_m(point p, string metric, float e, float da[4], point pa[4])
+void voronoi_f1_1d(float w,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output float outW)
 {
-  /* Compute the distance to and the position of the four closest neighbors to p.
-   *
-   * The neighbors are randomly placed, 1 each in a 3x3x3 grid (Worley pattern).
-   * The distances and points are returned in ascending order, i.e. da[0] and pa[0] will
-   * contain the distance to the closest point and its coordinates respectively.
-   */
-  int xx, yy, zz, xi, yi, zi;
+  float cellPosition = floor(w);
+  float localPosition = w - cellPosition;
 
-  xi = (int)floor(p[0]);
-  yi = (int)floor(p[1]);
-  zi = (int)floor(p[2]);
+  float minDistance = 8.0;
+  float targetOffset, targetPosition;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+    if (distanceToPoint < minDistance) {
+      targetOffset = cellOffset;
+      minDistance = distanceToPoint;
+      targetPosition = pointPosition;
+    }
+  }
+  outDistance = minDistance;
+  outColor = hash_float_to_color(cellPosition + targetOffset);
+  outW = targetPosition + cellPosition;
+}
 
-  da[0] = 1e10;
-  da[1] = 1e10;
-  da[2] = 1e10;
-  da[3] = 1e10;
+void voronoi_smooth_f1_1d(float w,
+                          float smoothness,
+                          float exponent,
+                          float randomness,
+                          string metric,
+                          output float outDistance,
+                          output color outColor,
+                          output float outW)
+{
+  float cellPosition = floor(w);
+  float localPosition = w - cellPosition;
 
-  for (xx = xi - 1; xx <= xi + 1; xx++) {
-    for (yy = yi - 1; yy <= yi + 1; yy++) {
-      for (zz = zi - 1; zz <= zi + 1; zz++) {
-        point ip = point(xx, yy, zz);
-        point vp = (point)cellnoise_color(ip);
-        point pd = p - (vp + ip);
+  float smoothDistance = 8.0;
+  float smoothPosition = 0.0;
+  color smoothColor = color(0.0);
+  for (int i = -2; i <= 2; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+    float h = smoothstep(0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+    float correctionFactor = smoothness * h * (1.0 - h);
+    smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+    correctionFactor /= 1.0 + 3.0 * smoothness;
+    color cellColor = hash_float_to_color(cellPosition + cellOffset);
+    smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+    smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+  }
+  outDistance = smoothDistance;
+  outColor = smoothColor;
+  outW = cellPosition + smoothPosition;
+}
+
+void voronoi_f2_1d(float w,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output float outW)
+{
+  float cellPosition = floor(w);
+  float localPosition = w - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  float offsetF1 = 0.0;
+  float positionF1 = 0.0;
+  float offsetF2, positionF2;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    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;
+    }
+  }
+  outDistance = distanceF2;
+  outColor = hash_float_to_color(cellPosition + offsetF2);
+  outW = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge_1d(float w, float randomness, output float outDistance)
+{
+  float cellPosition = floor(w);
+  float localPosition = w - cellPosition;
+
+  float minDistance = 8.0;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = distance(pointPosition, localPosition);
+    minDistance = min(distanceToPoint, minDistance);
+  }
+  outDistance = minDistance;
+}
+
+void voronoi_n_sphere_radius_1d(float w, float randomness, output float outRadius)
+{
+  float cellPosition = floor(w);
+  float localPosition = w - cellPosition;
+
+  float closestPoint;
+  float closestPointOffset;
+  float minDistance = 8.0;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = distance(pointPosition, localPosition);
+    if (distanceToPoint < minDistance) {
+      minDistance = distanceToPoint;
+      closestPoint = pointPosition;
+      closestPointOffset = cellOffset;
+    }
+  }
+
+  minDistance = 8.0;
+  float closestPointToClosestPoint;
+  for (int i = -1; i <= 1; i++) {
+    if (i == 0) {
+      continue;
+    }
+    float cellOffset = float(i) + closestPointOffset;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = distance(closestPoint, pointPosition);
+    if (distanceToPoint < minDistance) {
+      minDistance = distanceToPoint;
+      closestPointToClosestPoint = pointPosition;
+    }
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
+
+/* **** 2D Voronoi **** */
+
+float voronoi_distance(vector2 a, vector2 b, string metric, float exponent)
+{
+  if (metric == "euclidean") {
+    return distance(a, b);
+  }
+  else if (metric == "manhattan") {
+    return abs(a.x - b.x) + abs(a.y - b.y);
+  }
+  else if (metric == "chebychev") {
+    return max(abs(a.x - b.x), abs(a.y - b.y));
+  }
+  else if (metric == "minkowski") {
+    return pow(pow(abs(a.x - b.x), exponent) + pow(abs(a.y - b.y), exponent), 1.0 / exponent);
+  }
+  else {
+    return 0.0;
+  }
+}
+
+void voronoi_f1_2d(vector2 coord,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output vector2 outPosition)
+{
+  vector2 cellPosition = floor(coord);
+  vector2 localPosition = coord - cellPosition;
+
+  float minDistance = 8.0;
+  vector2 targetOffset, targetPosition;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vector2 cellOffset = vector2(i, j);
+      vector2 pointPosition = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+      if (distanceToPoint < minDistance) {
+        targetOffset = cellOffset;
+        minDistance = distanceToPoint;
+        targetPosition = pointPosition;
+      }
+    }
+  }
+  outDistance = minDistance;
+  outColor = hash_vector2_to_color(cellPosition + targetOffset);
+  outPosition = targetPosition + cellPosition;
+}
+
+void voronoi_smooth_f1_2d(vector2 coord,
+                          float smoothness,
+                          float exponent,
+                          float randomness,
+                          string metric,
+                          output float outDistance,
+                          output color outColor,
+                          output vector2 outPosition)
+{
+  vector2 cellPosition = floor(coord);
+  vector2 localPosition = coord - cellPosition;
+
+  float smoothDistance = 8.0;
+  color smoothColor = color(0.0);
+  vector2 smoothPosition = vector2(0.0, 0.0);
+  for (int j = -2; j <= 2; j++) {
+    for (int i = -2; i <= 2; i++) {
+      vector2 cellOffset = vector2(i, j);
+      vector2 pointPosition = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+      float h = smoothstep(0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+      float correctionFactor = smoothness * h * (1.0 - h);
+      smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+      correctionFactor /= 1.0 + 3.0 * smoothness;
+      color cellColor = hash_vector2_to_color(cellPosition + cellOffset);
+      smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+      smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+    }
+  }
+  outDistance = smoothDistance;
+  outColor = smoothColor;
+  outPosition = cellPosition + smoothPosition;
+}
+
+void voronoi_f2_2d(vector2 coord,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output vector2 outPosition)
+{
+  vector2 cellPosition = floor(coord);
+  vector2 localPosition = coord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  vector2 offsetF1 = vector2(0.0, 0.0);
+  vector2 positionF1 = vector2(0.0, 0.0);
+  vector2 offsetF2, positionF2;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vector2 cellOffset = vector2(i, j);
+      vector2 pointPosition = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness;
+      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;
+      }
+    }
+  }
+  outDistance = distanceF2;
+  outColor = hash_vector2_to_color(cellPosition + offsetF2);
+  outPosition = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge_2d(vector2 coord, float randomness, output float outDistance)
+{
+  vector2 cellPosition = floor(coord);
+  vector2 localPosition = coord - cellPosition;
+
+  vector2 vectorToClosest;
+  float minDistance = 8.0;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vector2 cellOffset = vector2(i, j);
+      vector2 vectorToPoint = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness -
+                              localPosition;
+      float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        vectorToClosest = vectorToPoint;
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vector2 cellOffset = vector2(i, j);
+      vector2 vectorToPoint = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness -
+                              localPosition;
+      vector2 perpendicularToEdge = vectorToPoint - vectorToClosest;
+      if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
+        float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
+                                   normalize(perpendicularToEdge));
+        minDistance = min(minDistance, distanceToEdge);
+      }
+    }
+  }
+  outDistance = minDistance;
+}
+
+void voronoi_n_sphere_radius_2d(vector2 coord, float randomness, output float outRadius)
+{
+  vector2 cellPosition = floor(coord);
+  vector2 localPosition = coord - cellPosition;
+
+  vector2 closestPoint;
+  vector2 closestPointOffset;
+  float minDistance = 8.0;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vector2 cellOffset = vector2(i, j);
+      vector2 pointPosition = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = distance(pointPosition, localPosition);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        closestPoint = pointPosition;
+        closestPointOffset = cellOffset;
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  vector2 closestPointToClosestPoint;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      if (i == 0 && j == 0) {
+        continue;
+      }
+      vector2 cellOffset = vector2(i, j) + closestPointOffset;
+      vector2 pointPosition = cellOffset +
+                              hash_vector2_to_vector2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = distance(closestPoint, pointPosition);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        closestPointToClosestPoint = pointPosition;
+      }
+    }
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
+
+/* **** 3D Voronoi **** */
+
+float voronoi_distance(vector3 a, vector3 b, string metric, float exponent)
+{
+  if (metric == "euclidean") {
+    return distance(a, b);
+  }
+  else if (metric == "manhattan") {
+    return abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2]);
+  }
+  else if (metric == "chebychev") {
+    return max(abs(a[0] - b[0]), max(abs(a[1] - b[1]), abs(a[2] - b[2])));
+  }
+  else if (metric == "minkowski") {
+    return pow(pow(abs(a[0] - b[0]), exponent) + pow(abs(a[1] - b[1]), exponent) +
+                   pow(abs(a[2] - b[2]), exponent),
+               1.0 / exponent);
+  }
+  else {
+    return 0.0;
+  }
+}
 
-        float d = 0.0;
-        if (metric == "distance") {
-          d = dot(pd, pd);
+void voronoi_f1_3d(vector3 coord,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output vector3 outPosition)
+{
+  vector3 cellPosition = floor(coord);
+  vector3 localPosition = coord - cellPosition;
+
+  float minDistance = 8.0;
+  vector3 targetOffset, targetPosition;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vector3 cellOffset = vector3(i, j, k);
+        vector3 pointPosition = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+        if (distanceToPoint < minDistance) {
+          targetOffset = cellOffset;
+          minDistance = distanceToPoint;
+          targetPosition = pointPosition;
+        }
+      }
+    }
+  }
+  outDistance = minDistance;
+  outColor = hash_vector3_to_color(cellPosition + targetOffset);
+  outPosition = targetPosition + cellPosition;
+}
+
+void voronoi_smooth_f1_3d(vector3 coord,
+                          float smoothness,
+                          float exponent,
+                          float randomness,
+                          string metric,
+                          output float outDistance,
+                          output color outColor,
+                          output vector3 outPosition)
+{
+  vector3 cellPosition = floor(coord);
+  vector3 localPosition = coord - cellPosition;
+
+  float smoothDistance = 8.0;
+  color smoothColor = color(0.0);
+  vector3 smoothPosition = vector3(0.0);
+  for (int k = -2; k <= 2; k++) {
+    for (int j = -2; j <= 2; j++) {
+      for (int i = -2; i <= 2; i++) {
+        vector3 cellOffset = vector3(i, j, k);
+        vector3 pointPosition = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+        float h = smoothstep(
+            0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+        float correctionFactor = smoothness * h * (1.0 - h);
+        smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+        correctionFactor /= 1.0 + 3.0 * smoothness;
+        color cellColor = hash_vector3_to_color(cellPosition + cellOffset);
+        smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+        smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+      }
+    }
+  }
+  outDistance = smoothDistance;
+  outColor = smoothColor;
+  outPosition = cellPosition + smoothPosition;
+}
+
+void voronoi_f2_3d(vector3 coord,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output vector3 outPosition)
+{
+  vector3 cellPosition = floor(coord);
+  vector3 localPosition = coord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  vector3 offsetF1 = vector3(0.0);
+  vector3 positionF1 = vector3(0.0);
+  vector3 offsetF2, positionF2;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vector3 cellOffset = vector3(i, j, k);
+        vector3 pointPosition = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness;
+        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;
+        }
+      }
+    }
+  }
+  outDistance = distanceF2;
+  outColor = hash_vector3_to_color(cellPosition + offsetF2);
+  outPosition = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge_3d(vector3 coord, float randomness, output float outDistance)
+{
+  vector3 cellPosition = floor(coord);
+  vector3 localPosition = coord - cellPosition;
+
+  vector3 vectorToClosest;
+  float minDistance = 8.0;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vector3 cellOffset = vector3(i, j, k);
+        vector3 vectorToPoint = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness -
+                                localPosition;
+        float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          vectorToClosest = vectorToPoint;
+        }
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vector3 cellOffset = vector3(i, j, k);
+        vector3 vectorToPoint = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness -
+                                localPosition;
+        vector3 perpendicularToEdge = vectorToPoint - vectorToClosest;
+        if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
+          float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
+                                     normalize(perpendicularToEdge));
+          minDistance = min(minDistance, distanceToEdge);
         }
-        else if (metric == "manhattan") {
-          d = fabs(pd[0]) + fabs(pd[1]) + fabs(pd[2]);
+      }
+    }
+  }
+  outDistance = minDistance;
+}
+
+void voronoi_n_sphere_radius_3d(vector3 coord, float randomness, output float outRadius)
+{
+  vector3 cellPosition = floor(coord);
+  vector3 localPosition = coord - cellPosition;
+
+  vector3 closestPoint;
+  vector3 closestPointOffset;
+  float minDistance = 8.0;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vector3 cellOffset = vector3(i, j, k);
+        vector3 pointPosition = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = distance(pointPosition, localPosition);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          closestPoint = pointPosition;
+          closestPointOffset = cellOffset;
         }
-        else if (metric == "chebychev") {
-          d = max(fabs(pd[0]), max(fabs(pd[1]), fabs(pd[2])));
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  vector3 closestPointToClosestPoint;
+  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;
         }
-        else if (metric == "minkowski") {
-          d = pow(pow(fabs(pd[0]), e) + pow(fabs(pd[1]), e) + pow(fabs(pd[2]), e), 1.0 / e);
+        vector3 cellOffset = vector3(i, j, k) + closestPointOffset;
+        vector3 pointPosition = cellOffset +
+                                hash_vector3_to_vector3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = distance(closestPoint, pointPosition);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          closestPointToClosestPoint = pointPosition;
         }
+      }
+    }
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
+
+/* **** 4D Voronoi **** */
+
+float voronoi_distance(vector4 a, vector4 b, string metric, float exponent)
+{
+  if (metric == "euclidean") {
+    return distance(a, b);
+  }
+  else if (metric == "manhattan") {
+    return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) + abs(a.w - b.w);
+  }
+  else if (metric == "chebychev") {
+    return max(abs(a.x - b.x), max(abs(a.y - b.y), max(abs(a.z - b.z), abs(a.w - b.w))));
+  }
+  else if (metric == "minkowski") {
+    return pow(pow(abs(a.x - b.x), exponent) + pow(abs(a.y - b.y), exponent) +
+                   pow(abs(a.z - b.z), exponent) + pow(abs(a.w - b.w), exponent),
+               1.0 / exponent);
+  }
+  else {
+    return 0.0;
+  }
+}
+
+void voronoi_f1_4d(vector4 coord,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output vector4 outPosition)
+{
+  vector4 cellPosition = floor(coord);
+  vector4 localPosition = coord - cellPosition;
 
-        vp += point(xx, yy, zz);
+  float minDistance = 8.0;
+  vector4 targetOffset, targetPosition;
+  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++) {
+          vector4 cellOffset = vector4(i, j, k, u);
+          vector4 pointPosition = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+          if (distanceToPoint < minDistance) {
+            targetOffset = cellOffset;
+            minDistance = distanceToPoint;
+            targetPosition = pointPosition;
+          }
+        }
+      }
+    }
+  }
+  outDistance = minDistance;
+  outColor = hash_vector4_to_color(cellPosition + targetOffset);
+  outPosition = targetPosition + cellPosition;
+}
 
-        if (d < da[0]) {
-          da[3] = da[2];
-          da[2] = da[1];
-          da[1] = da[0];
-          da[0] = d;
+void voronoi_smooth_f1_4d(vector4 coord,
+                          float smoothness,
+                          float exponent,
+                          float randomness,
+                          string metric,
+                          output float outDistance,
+                          output color outColor,
+                          output vector4 outPosition)
+{
+  vector4 cellPosition = floor(coord);
+  vector4 localPosition = coord - cellPosition;
 
-          pa[3] = pa[2];
-          pa[2] = pa[1];
-          pa[1] = pa[0];
-          pa[0] = vp;
+  float smoothDistance = 8.0;
+  color smoothColor = color(0.0);
+  vector4 smoothPosition = vector4(0.0, 0.0, 0.0, 0.0);
+  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++) {
+          vector4 cellOffset = vector4(i, j, k, u);
+          vector4 pointPosition = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+          float h = smoothstep(
+              0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+          float correctionFactor = smoothness * h * (1.0 - h);
+          smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+          correctionFactor /= 1.0 + 3.0 * smoothness;
+          color cellColor = hash_vector4_to_color(cellPosition + cellOffset);
+          smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+          smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
         }
-        else if (d < da[1]) {
-          da[3] = da[2];
-          da[2] = da[1];
-          da[1] = d;
-
-          pa[3] = pa[2];
-          pa[2] = pa[1];
-          pa[1] = vp;
+      }
+    }
+  }
+  outDistance = smoothDistance;
+  outColor = smoothColor;
+  outPosition = cellPosition + smoothPosition;
+}
+
+void voronoi_f2_4d(vector4 coord,
+                   float exponent,
+                   float randomness,
+                   string metric,
+                   output float outDistance,
+                   output color outColor,
+                   output vector4 outPosition)
+{
+  vector4 cellPosition = floor(coord);
+  vector4 localPosition = coord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  vector4 offsetF1 = vector4(0.0, 0.0, 0.0, 0.0);
+  vector4 positionF1 = vector4(0.0, 0.0, 0.0, 0.0);
+  vector4 offsetF2, positionF2;
+  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++) {
+          vector4 cellOffset = vector4(i, j, k, u);
+          vector4 pointPosition = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness;
+          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;
+          }
         }
-        else if (d < da[2]) {
-          da[3] = da[2];
-          da[2] = d;
+      }
+    }
+  }
+  outDistance = distanceF2;
+  outColor = hash_vector4_to_color(cellPosition + offsetF2);
+  outPosition = positionF2 + cellPosition;
+}
+
+void voronoi_distance_to_edge_4d(vector4 coord, float randomness, output float outDistance)
+{
+  vector4 cellPosition = floor(coord);
+  vector4 localPosition = coord - cellPosition;
 
-          pa[3] = pa[2];
-          pa[2] = vp;
+  vector4 vectorToClosest;
+  float minDistance = 8.0;
+  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++) {
+          vector4 cellOffset = vector4(i, j, k, u);
+          vector4 vectorToPoint = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness -
+                                  localPosition;
+          float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            vectorToClosest = vectorToPoint;
+          }
         }
-        else if (d < da[3]) {
-          da[3] = d;
-          pa[3] = vp;
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  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++) {
+          vector4 cellOffset = vector4(i, j, k, u);
+          vector4 vectorToPoint = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness -
+                                  localPosition;
+          vector4 perpendicularToEdge = vectorToPoint - vectorToClosest;
+          if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
+            float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
+                                       normalize(perpendicularToEdge));
+            minDistance = min(minDistance, distanceToEdge);
+          }
         }
       }
     }
   }
+  outDistance = minDistance;
 }
 
-/* Voronoi */
+void voronoi_n_sphere_radius_4d(vector4 coord, float randomness, output float outRadius)
+{
+  vector4 cellPosition = floor(coord);
+  vector4 localPosition = coord - cellPosition;
+
+  vector4 closestPoint;
+  vector4 closestPointOffset;
+  float minDistance = 8.0;
+  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++) {
+          vector4 cellOffset = vector4(i, j, k, u);
+          vector4 pointPosition = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = distance(pointPosition, localPosition);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            closestPoint = pointPosition;
+            closestPointOffset = cellOffset;
+          }
+        }
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  vector4 closestPointToClosestPoint;
+  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;
+          }
+          vector4 cellOffset = vector4(i, j, k, u) + closestPointOffset;
+          vector4 pointPosition = cellOffset +
+                                  hash_vector4_to_vector4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = distance(closestPoint, pointPosition);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            closestPointToClosestPoint = pointPosition;
+          }
+        }
+      }
+    }
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
 
 shader node_voronoi_texture(
     int use_mapping = 0,
     matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
-    string coloring = "intensity",
-    string metric = "distance",
-    string feature = "F1",
-    float Exponent = 1.0,
+    string dimensions = "3D",
+    string feature = "f1",
+    string metric = "euclidean",
+    vector3 Vector = P,
+    float WIn = 0.0,
     float Scale = 5.0,
-    point Vector = P,
-    output float Fac = 0.0,
-    output color Color = 0.0)
+    float Smoothness = 5.0,
+    float Exponent = 1.0,
+    float Randomness = 1.0,
+    output float Distance = 0.0,
+    output color Color = 0.0,
+    output vector3 Position = P,
+    output float WOut = 0.0,
+    output float Radius = 0.0)
 {
-  point p = Vector;
+  float randomness = clamp(Randomness, 0.0, 1.0);
+  float smoothness = clamp(Smoothness / 2.0, 0.0, 0.5);
 
+  vector3 coord = Vector;
   if (use_mapping)
-    p = transform(mapping, p);
+    coord = transform(mapping, coord);
 
-  /* compute distance and point coordinate of 4 nearest neighbours */
-  float da[4];
-  point pa[4];
+  float w = WIn * Scale;
+  coord *= Scale;
 
-  /* compute distance and point coordinate of 4 nearest neighbours */
-  voronoi_m(p * Scale, metric, Exponent, da, pa);
-
-  if (coloring == "intensity") {
-    /* Intensity output */
-    if (feature == "F1") {
-      Fac = fabs(da[0]);
+  if (dimensions == "1D") {
+    if (feature == "f1") {
+      voronoi_f1_1d(w, Exponent, randomness, metric, Distance, Color, WOut);
+    }
+    else if (feature == "smooth_f1") {
+      voronoi_smooth_f1_1d(w, smoothness, Exponent, randomness, metric, Distance, Color, WOut);
     }
-    else if (feature == "F2") {
-      Fac = fabs(da[1]);
+    else if (feature == "f2") {
+      voronoi_f2_1d(w, Exponent, randomness, metric, Distance, Color, WOut);
     }
-    else if (feature == "F3") {
-      Fac = fabs(da[2]);
+    else if (feature == "distance_to_edge") {
+      voronoi_distance_to_edge_1d(w, randomness, Distance);
     }
-    else if (feature == "F4") {
-      Fac = fabs(da[3]);
+    else if (feature == "n_sphere_radius") {
+      voronoi_n_sphere_radius_1d(w, randomness, Radius);
     }
-    else if (feature == "F2F1") {
-      Fac = fabs(da[1] - da[0]);
+    else {
+      error("Unknown feature!");
     }
-    Color = color(Fac);
+    WOut = (Scale != 0.0) ? WOut / Scale : 0.0;
   }
-  else {
-    /* Color output */
-    if (feature == "F1") {
-      Color = pa[0];
+  else if (dimensions == "2D") {
+    vector2 coord2D = vector2(coord[0], coord[1]);
+    vector2 outPosition2D;
+    if (feature == "f1") {
+      voronoi_f1_2d(coord2D, Exponent, randomness, metric, Distance, Color, outPosition2D);
     }
-    else if (feature == "F2") {
-      Color = pa[1];
+    else if (feature == "smooth_f1") {
+      voronoi_smooth_f1_2d(
+          coord2D, smoothness, Exponent, randomness, metric, Distance, Color, outPosition2D);
     }
-    else if (feature == "F3") {
-      Color = pa[2];
+    else if (feature == "f2") {
+      voronoi_f2_2d(coord2D, Exponent, randomness, metric, Distance, Color, outPosition2D);
     }
-    else if (feature == "F4") {
-      Color = pa[3];
+    else if (feature == "distance_to_edge") {
+      voronoi_distance_to_edge_2d(coord2D, randomness, Distance);
     }
-    else if (feature == "F2F1") {
-      Color = fabs(pa[1] - pa[0]);
+    else if (feature == "n_sphere_radius") {
+      voronoi_n_sphere_radius_2d(coord2D, randomness, Radius);
     }
-
-    Color = cellnoise_color(Color);
-    Fac = (Color[0] + Color[1] + Color[2]) * (1.0 / 3.0);
+    else {
+      error("Unknown feature!");
+    }
+    outPosition2D = safe_divide(outPosition2D, Scale);
+    Position = vector3(outPosition2D.x, outPosition2D.y, 0.0);
+  }
+  else if (dimensions == "3D") {
+    if (feature == "f1") {
+      voronoi_f1_3d(coord, Exponent, randomness, metric, Distance, Color, Position);
+    }
+    else if (feature == "smooth_f1") {
+      voronoi_smooth_f1_3d(
+          coord, smoothness, Exponent, randomness, metric, Distance, Color, Position);
+    }
+    else if (feature == "f2") {
+      voronoi_f2_3d(coord, Exponent, randomness, metric, Distance, Color, Position);
+    }
+    else if (feature == "distance_to_edge") {
+      voronoi_distance_to_edge_3d(coord, randomness, Distance);
+    }
+    else if (feature == "n_sphere_radius") {
+      voronoi_n_sphere_radius_3d(coord, randomness, Radius);
+    }
+    else {
+      error("Unknown feature!");
+    }
+    Position = (Scale != 0.0) ? Position / Scale : vector3(0.0);
+  }
+  else if (dimensions == "4D") {
+    vector4 coord4D = vector4(coord[0], coord[1], coord[2], w);
+    vector4 outPosition4D;
+    if (feature == "f1") {
+      voronoi_f1_4d(coord4D, Exponent, randomness, metric, Distance, Color, outPosition4D);
+    }
+    else if (feature == "smooth_f1") {
+      voronoi_smooth_f1_4d(
+          coord4D, smoothness, Exponent, randomness, metric, Distance, Color, outPosition4D);
+    }
+    else if (feature == "f2") {
+      voronoi_f2_4d(coord4D, Exponent, randomness, metric, Distance, Color, outPosition4D);
+    }
+    else if (feature == "distance_to_edge") {
+      voronoi_distance_to_edge_4d(coord4D, randomness, Distance);
+    }
+    else if (feature == "n_sphere_radius") {
+      voronoi_n_sphere_radius_4d(coord4D, randomness, Radius);
+    }
+    else {
+      error("Unknown feature!");
+    }
+    outPosition4D = safe_divide(outPosition4D, Scale);
+    Position = vector3(outPosition4D.x, outPosition4D.y, outPosition4D.z);
+    WOut = outPosition4D.w;
+  }
+  else {
+    error("Unknown dimension!");
   }
 }
index a192930937fc4c6c0331fc241c64949804d5951e..8de66a0496349febcafae9f30da1c0783dd9151a 100644 (file)
@@ -429,7 +429,7 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg,
         svm_node_tex_gradient(sd, stack, node);
         break;
       case NODE_TEX_VORONOI:
-        svm_node_tex_voronoi(kg, sd, stack, node, &offset);
+        svm_node_tex_voronoi(kg, sd, stack, node.y, node.z, node.w, &offset);
         break;
       case NODE_TEX_MUSGRAVE:
         svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, &offset);
index 8d832247b189d91b43f4e7e174eb2ff4baf04b7f..b67c1e9cb7e88f22ab299eeb8cbac5d7fe5c3e1f 100644 (file)
@@ -590,28 +590,4 @@ ccl_device_inline float noise_4d(float4 p)
   return 0.5f * snoise_4d(p) + 0.5f;
 }
 
-/* cell noise */
-ccl_device float cellnoise(float3 p)
-{
-  int3 ip = quick_floor_to_int3(p);
-  return hash_uint3_to_float(ip.x, ip.y, ip.z);
-}
-
-ccl_device float3 cellnoise3(float3 p)
-{
-  int3 ip = quick_floor_to_int3(p);
-#ifndef __KERNEL_SSE__
-  float r = hash_uint3_to_float(ip.x, ip.y, ip.z);
-  float g = hash_uint3_to_float(ip.y, ip.x, ip.z);
-  float b = hash_uint3_to_float(ip.y, ip.z, ip.x);
-  return make_float3(r, g, b);
-#else
-  ssei ip_yxz = shuffle<1, 0, 2, 3>(ssei(ip.m128));
-  ssei ip_xyy = shuffle<0, 1, 1, 3>(ssei(ip.m128));
-  ssei ip_zzx = shuffle<2, 2, 0, 3>(ssei(ip.m128));
-  ssei bits = hash_ssei3(ip_xyy, ip_yxz, ip_zzx);
-  return float3(uint32_to_float(bits) * ssef(1.0f / (float)0xFFFFFFFF));
-#endif
-}
-
 CCL_NAMESPACE_END
index de7114566b3ab8e85023add80b1f928b68ae61cc..65e2dacedaeb111612cb15f3a8d92e34c9a9a8c1 100644 (file)
@@ -357,24 +357,19 @@ typedef enum NodeGradientType {
   NODE_BLEND_SPHERICAL
 } NodeGradientType;
 
-typedef enum NodeVoronoiColoring {
-  NODE_VORONOI_INTENSITY,
-  NODE_VORONOI_CELLS
-} NodeVoronoiColoring;
-
 typedef enum NodeVoronoiDistanceMetric {
-  NODE_VORONOI_DISTANCE,
+  NODE_VORONOI_EUCLIDEAN,
   NODE_VORONOI_MANHATTAN,
   NODE_VORONOI_CHEBYCHEV,
-  NODE_VORONOI_MINKOWSKI
+  NODE_VORONOI_MINKOWSKI,
 } NodeVoronoiDistanceMetric;
 
 typedef enum NodeVoronoiFeature {
   NODE_VORONOI_F1,
   NODE_VORONOI_F2,
-  NODE_VORONOI_F3,
-  NODE_VORONOI_F4,
-  NODE_VORONOI_F2F1
+  NODE_VORONOI_SMOOTH_F1,
+  NODE_VORONOI_DISTANCE_TO_EDGE,
+  NODE_VORONOI_N_SPHERE_RADIUS,
 } NodeVoronoiFeature;
 
 typedef enum NodeBlendWeightType {
index 3d7fa52396853ee196b670e2082d5e16e99090c2..4a479d9bc719bf0413af11bf66b46b7531b3ab74 100644 (file)
 
 CCL_NAMESPACE_BEGIN
 
-/* Voronoi */
-
-ccl_device void voronoi_neighbors(
-    float3 p, NodeVoronoiDistanceMetric distance, float e, float da[4], float3 pa[4])
-{
-  /* Compute the distance to and the position of the closest neighbors to p.
-   *
-   * The neighbors are randomly placed, 1 each in a 3x3x3 grid (Worley pattern).
-   * The distances and points are returned in ascending order, i.e. da[0] and pa[0] will
-   * contain the distance to the closest point and its coordinates respectively.
-   */
-
-  da[0] = 1e10f;
-  da[1] = 1e10f;
-  da[2] = 1e10f;
-  da[3] = 1e10f;
-
-  pa[0] = make_float3(0.0f, 0.0f, 0.0f);
-  pa[1] = make_float3(0.0f, 0.0f, 0.0f);
-  pa[2] = make_float3(0.0f, 0.0f, 0.0f);
-  pa[3] = make_float3(0.0f, 0.0f, 0.0f);
-
-  int3 xyzi = quick_floor_to_int3(p);
-
-  for (int xx = -1; xx <= 1; xx++) {
-    for (int yy = -1; yy <= 1; yy++) {
-      for (int zz = -1; zz <= 1; zz++) {
-        int3 ip = xyzi + make_int3(xx, yy, zz);
-        float3 fp = make_float3(ip.x, ip.y, ip.z);
-        float3 vp = fp + cellnoise3(fp);
-
-        float d;
-        switch (distance) {
-          case NODE_VORONOI_DISTANCE:
-            d = len_squared(p - vp);
-            break;
-          case NODE_VORONOI_MANHATTAN:
-            d = reduce_add(fabs(vp - p));
-            break;
-          case NODE_VORONOI_CHEBYCHEV:
-            d = max3(fabs(vp - p));
-            break;
-          case NODE_VORONOI_MINKOWSKI: {
-            float3 n = fabs(vp - p);
-            if (e == 0.5f) {
-              d = sqr(reduce_add(sqrt(n)));
-            }
-            else {
-              d = powf(reduce_add(pow3(n, e)), 1.0f / e);
-            }
-            break;
-          }
+/*
+ * Smooth Voronoi:
+ *
+ * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
+ *
+ * Distance To Edge:
+ *
+ * - https://www.shadertoy.com/view/llG3zy
+ *
+ */
+
+/* **** 1D Voronoi **** */
+
+ccl_device float voronoi_distance_1d(float a,
+                                     float b,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float exponent)
+{
+  return fabsf(b - a);
+}
+
+ccl_device void voronoi_f1_1d(float w,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float *outW)
+{
+  float cellPosition = floorf(w);
+  float localPosition = w - cellPosition;
+
+  float minDistance = 8.0f;
+  float targetOffset, targetPosition;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = i;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance_1d(pointPosition, localPosition, metric, exponent);
+    if (distanceToPoint < minDistance) {
+      targetOffset = cellOffset;
+      minDistance = distanceToPoint;
+      targetPosition = pointPosition;
+    }
+  }
+  *outDistance = minDistance;
+  *outColor = hash_float_to_float3(cellPosition + targetOffset);
+  *outW = targetPosition + cellPosition;
+}
+
+ccl_device void voronoi_smooth_f1_1d(float w,
+                                     float smoothness,
+                                     float exponent,
+                                     float randomness,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float *outDistance,
+                                     float3 *outColor,
+                                     float *outW)
+{
+  float cellPosition = floorf(w);
+  float localPosition = w - cellPosition;
+
+  float smoothDistance = 8.0f;
+  float smoothPosition = 0.0f;
+  float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
+  for (int i = -2; i <= 2; i++) {
+    float cellOffset = i;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance_1d(pointPosition, localPosition, metric, exponent);
+    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;
+    float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
+    smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+    smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+  }
+  *outDistance = smoothDistance;
+  *outColor = smoothColor;
+  *outW = cellPosition + smoothPosition;
+}
+
+ccl_device void voronoi_f2_1d(float w,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float *outW)
+{
+  float cellPosition = floorf(w);
+  float localPosition = w - cellPosition;
+
+  float distanceF1 = 8.0f;
+  float distanceF2 = 8.0f;
+  float offsetF1 = 0.0f;
+  float positionF1 = 0.0f;
+  float offsetF2, positionF2;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = i;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance_1d(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;
+    }
+  }
+  *outDistance = distanceF2;
+  *outColor = hash_float_to_float3(cellPosition + offsetF2);
+  *outW = positionF2 + cellPosition;
+}
+
+ccl_device void voronoi_distance_to_edge_1d(float w, float randomness, float *outDistance)
+{
+  float cellPosition = floorf(w);
+  float localPosition = w - cellPosition;
+
+  float minDistance = 8.0f;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = i;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = fabsf(pointPosition - localPosition);
+    minDistance = min(distanceToPoint, minDistance);
+  }
+  *outDistance = minDistance;
+}
+
+ccl_device void voronoi_n_sphere_radius_1d(float w, float randomness, float *outRadius)
+{
+  float cellPosition = floorf(w);
+  float localPosition = w - cellPosition;
+
+  float closestPoint;
+  float closestPointOffset;
+  float minDistance = 8.0f;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = i;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = fabsf(pointPosition - localPosition);
+    if (distanceToPoint < minDistance) {
+      minDistance = distanceToPoint;
+      closestPoint = pointPosition;
+      closestPointOffset = cellOffset;
+    }
+  }
+
+  minDistance = 8.0f;
+  float closestPointToClosestPoint;
+  for (int i = -1; i <= 1; i++) {
+    if (i == 0) {
+      continue;
+    }
+    float cellOffset = i + closestPointOffset;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = fabsf(closestPoint - pointPosition);
+    if (distanceToPoint < minDistance) {
+      minDistance = distanceToPoint;
+      closestPointToClosestPoint = pointPosition;
+    }
+  }
+  *outRadius = fabsf(closestPointToClosestPoint - closestPoint) / 2.0f;
+}
+
+/* **** 2D Voronoi **** */
+
+ccl_device float voronoi_distance_2d(float2 a,
+                                     float2 b,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float exponent)
+{
+  if (metric == NODE_VORONOI_EUCLIDEAN) {
+    return distance(a, b);
+  }
+  else if (metric == NODE_VORONOI_MANHATTAN) {
+    return fabsf(a.x - b.x) + fabsf(a.y - b.y);
+  }
+  else if (metric == NODE_VORONOI_CHEBYCHEV) {
+    return max(fabsf(a.x - b.x), fabsf(a.y - b.y));
+  }
+  else if (metric == NODE_VORONOI_MINKOWSKI) {
+    return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent),
+                1.0f / exponent);
+  }
+  else {
+    return 0.0f;
+  }
+}
+
+ccl_device void voronoi_f1_2d(float2 coord,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float2 *outPosition)
+{
+  float2 cellPosition = floor(coord);
+  float2 localPosition = coord - cellPosition;
+
+  float minDistance = 8.0f;
+  float2 targetOffset, targetPosition;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      float2 cellOffset = make_float2(i, j);
+      float2 pointPosition = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance_2d(pointPosition, localPosition, metric, exponent);
+      if (distanceToPoint < minDistance) {
+        targetOffset = cellOffset;
+        minDistance = distanceToPoint;
+        targetPosition = pointPosition;
+      }
+    }
+  }
+  *outDistance = minDistance;
+  *outColor = hash_float2_to_float3(cellPosition + targetOffset);
+  *outPosition = targetPosition + cellPosition;
+}
+
+ccl_device void voronoi_smooth_f1_2d(float2 coord,
+                                     float smoothness,
+                                     float exponent,
+                                     float randomness,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float *outDistance,
+                                     float3 *outColor,
+                                     float2 *outPosition)
+{
+  float2 cellPosition = floor(coord);
+  float2 localPosition = coord - cellPosition;
+
+  float smoothDistance = 8.0f;
+  float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
+  float2 smoothPosition = make_float2(0.0f, 0.0f);
+  for (int j = -2; j <= 2; j++) {
+    for (int i = -2; i <= 2; i++) {
+      float2 cellOffset = make_float2(i, j);
+      float2 pointPosition = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance_2d(pointPosition, localPosition, metric, exponent);
+      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;
+      float3 cellColor = hash_float2_to_float3(cellPosition + cellOffset);
+      smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+      smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+    }
+  }
+  *outDistance = smoothDistance;
+  *outColor = smoothColor;
+  *outPosition = cellPosition + smoothPosition;
+}
+
+ccl_device void voronoi_f2_2d(float2 coord,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float2 *outPosition)
+{
+  float2 cellPosition = floor(coord);
+  float2 localPosition = coord - cellPosition;
+
+  float distanceF1 = 8.0f;
+  float distanceF2 = 8.0f;
+  float2 offsetF1 = make_float2(0.0f, 0.0f);
+  float2 positionF1 = make_float2(0.0f, 0.0f);
+  float2 offsetF2, positionF2;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      float2 cellOffset = make_float2(i, j);
+      float2 pointPosition = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance_2d(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;
+      }
+    }
+  }
+  *outDistance = distanceF2;
+  *outColor = hash_float2_to_float3(cellPosition + offsetF2);
+  *outPosition = positionF2 + cellPosition;
+}
+
+ccl_device void voronoi_distance_to_edge_2d(float2 coord, float randomness, float *outDistance)
+{
+  float2 cellPosition = floor(coord);
+  float2 localPosition = coord - cellPosition;
+
+  float2 vectorToClosest;
+  float minDistance = 8.0f;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      float2 cellOffset = make_float2(i, j);
+      float2 vectorToPoint = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness -
+                             localPosition;
+      float distanceToPoint = dot(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++) {
+      float2 cellOffset = make_float2(i, j);
+      float2 vectorToPoint = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness -
+                             localPosition;
+      float2 perpendicularToEdge = vectorToPoint - vectorToClosest;
+      if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
+        float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0f,
+                                   normalize(perpendicularToEdge));
+        minDistance = min(minDistance, distanceToEdge);
+      }
+    }
+  }
+  *outDistance = minDistance;
+}
+
+ccl_device void voronoi_n_sphere_radius_2d(float2 coord, float randomness, float *outRadius)
+{
+  float2 cellPosition = floor(coord);
+  float2 localPosition = coord - cellPosition;
+
+  float2 closestPoint;
+  float2 closestPointOffset;
+  float minDistance = 8.0f;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      float2 cellOffset = make_float2(i, j);
+      float2 pointPosition = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = distance(pointPosition, localPosition);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        closestPoint = pointPosition;
+        closestPointOffset = cellOffset;
+      }
+    }
+  }
+
+  minDistance = 8.0f;
+  float2 closestPointToClosestPoint;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      if (i == 0 && j == 0) {
+        continue;
+      }
+      float2 cellOffset = make_float2(i, j) + closestPointOffset;
+      float2 pointPosition = cellOffset +
+                             hash_float2_to_float2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = distance(closestPoint, pointPosition);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        closestPointToClosestPoint = pointPosition;
+      }
+    }
+  }
+  *outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0f;
+}
+
+/* **** 3D Voronoi **** */
+
+ccl_device float voronoi_distance_3d(float3 a,
+                                     float3 b,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float exponent)
+{
+  if (metric == NODE_VORONOI_EUCLIDEAN) {
+    return distance(a, b);
+  }
+  else if (metric == NODE_VORONOI_MANHATTAN) {
+    return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z);
+  }
+  else if (metric == NODE_VORONOI_CHEBYCHEV) {
+    return max(fabsf(a.x - b.x), max(fabsf(a.y - b.y), fabsf(a.z - b.z)));
+  }
+  else if (metric == NODE_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);
+  }
+  else {
+    return 0.0f;
+  }
+}
+
+ccl_device void voronoi_f1_3d(float3 coord,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float3 *outPosition)
+{
+  float3 cellPosition = floor(coord);
+  float3 localPosition = coord - cellPosition;
+
+  float minDistance = 8.0f;
+  float3 targetOffset, targetPosition;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        float3 cellOffset = make_float3(i, j, k);
+        float3 pointPosition = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance_3d(
+            pointPosition, localPosition, metric, exponent);
+        if (distanceToPoint < minDistance) {
+          targetOffset = cellOffset;
+          minDistance = distanceToPoint;
+          targetPosition = pointPosition;
         }
+      }
+    }
+  }
+  *outDistance = minDistance;
+  *outColor = hash_float3_to_float3(cellPosition + targetOffset);
+  *outPosition = targetPosition + cellPosition;
+}
+
+ccl_device void voronoi_smooth_f1_3d(float3 coord,
+                                     float smoothness,
+                                     float exponent,
+                                     float randomness,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float *outDistance,
+                                     float3 *outColor,
+                                     float3 *outPosition)
+{
+  float3 cellPosition = floor(coord);
+  float3 localPosition = coord - cellPosition;
+
+  float smoothDistance = 8.0f;
+  float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
+  float3 smoothPosition = make_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++) {
+        float3 cellOffset = make_float3(i, j, k);
+        float3 pointPosition = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance_3d(
+            pointPosition, localPosition, metric, exponent);
+        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;
+        float3 cellColor = hash_float3_to_float3(cellPosition + cellOffset);
+        smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+        smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+      }
+    }
+  }
+  *outDistance = smoothDistance;
+  *outColor = smoothColor;
+  *outPosition = cellPosition + smoothPosition;
+}
 
-        /* To keep the shortest four distances and associated points we have to keep them in sorted
-         * order. */
-        if (d < da[0]) {
-          da[3] = da[2];
-          da[2] = da[1];
-          da[1] = da[0];
-          da[0] = d;
-
-          pa[3] = pa[2];
-          pa[2] = pa[1];
-          pa[1] = pa[0];
-          pa[0] = vp;
+ccl_device void voronoi_f2_3d(float3 coord,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float3 *outPosition)
+{
+  float3 cellPosition = floor(coord);
+  float3 localPosition = coord - cellPosition;
+
+  float distanceF1 = 8.0f;
+  float distanceF2 = 8.0f;
+  float3 offsetF1 = make_float3(0.0f, 0.0f, 0.0f);
+  float3 positionF1 = make_float3(0.0f, 0.0f, 0.0f);
+  float3 offsetF2, positionF2;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        float3 cellOffset = make_float3(i, j, k);
+        float3 pointPosition = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance_3d(
+            pointPosition, localPosition, metric, exponent);
+        if (distanceToPoint < distanceF1) {
+          distanceF2 = distanceF1;
+          distanceF1 = distanceToPoint;
+          offsetF2 = offsetF1;
+          offsetF1 = cellOffset;
+          positionF2 = positionF1;
+          positionF1 = pointPosition;
         }
-        else if (d < da[1]) {
-          da[3] = da[2];
-          da[2] = da[1];
-          da[1] = d;
-
-          pa[3] = pa[2];
-          pa[2] = pa[1];
-          pa[1] = vp;
+        else if (distanceToPoint < distanceF2) {
+          distanceF2 = distanceToPoint;
+          offsetF2 = cellOffset;
+          positionF2 = pointPosition;
         }
-        else if (d < da[2]) {
-          da[3] = da[2];
-          da[2] = d;
+      }
+    }
+  }
+  *outDistance = distanceF2;
+  *outColor = hash_float3_to_float3(cellPosition + offsetF2);
+  *outPosition = positionF2 + cellPosition;
+}
 
-          pa[3] = pa[2];
-          pa[2] = vp;
+ccl_device void voronoi_distance_to_edge_3d(float3 coord, float randomness, float *outDistance)
+{
+  float3 cellPosition = floor(coord);
+  float3 localPosition = coord - cellPosition;
+
+  float3 vectorToClosest;
+  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++) {
+        float3 cellOffset = make_float3(i, j, k);
+        float3 vectorToPoint = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness -
+                               localPosition;
+        float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          vectorToClosest = vectorToPoint;
         }
-        else if (d < da[3]) {
-          da[3] = d;
-          pa[3] = vp;
+      }
+    }
+  }
+
+  minDistance = 8.0f;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        float3 cellOffset = make_float3(i, j, k);
+        float3 vectorToPoint = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness -
+                               localPosition;
+        float3 perpendicularToEdge = vectorToPoint - vectorToClosest;
+        if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
+          float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0f,
+                                     normalize(perpendicularToEdge));
+          minDistance = min(minDistance, distanceToEdge);
         }
       }
     }
   }
+  *outDistance = minDistance;
 }
 
-ccl_device void svm_node_tex_voronoi(
-    KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
+ccl_device void voronoi_n_sphere_radius_3d(float3 coord, float randomness, float *outRadius)
 {
-  uint4 node2 = read_node(kg, offset);
+  float3 cellPosition = floor(coord);
+  float3 localPosition = coord - cellPosition;
 
-  uint co_offset, coloring, distance, feature;
-  uint scale_offset, e_offset, fac_offset, color_offset;
+  float3 closestPoint;
+  float3 closestPointOffset;
+  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++) {
+        float3 cellOffset = make_float3(i, j, k);
+        float3 pointPosition = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = distance(pointPosition, localPosition);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          closestPoint = pointPosition;
+          closestPointOffset = cellOffset;
+        }
+      }
+    }
+  }
 
-  svm_unpack_node_uchar4(node.y, &co_offset, &coloring, &distance, &feature);
-  svm_unpack_node_uchar4(node.z, &scale_offset, &e_offset, &fac_offset, &color_offset);
+  minDistance = 8.0f;
+  float3 closestPointToClosestPoint;
+  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;
+        }
+        float3 cellOffset = make_float3(i, j, k) + closestPointOffset;
+        float3 pointPosition = cellOffset +
+                               hash_float3_to_float3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = distance(closestPoint, pointPosition);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          closestPointToClosestPoint = pointPosition;
+        }
+      }
+    }
+  }
+  *outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0f;
+}
+
+/* **** 4D Voronoi **** */
 
-  float3 co = stack_load_float3(stack, co_offset);
-  float scale = stack_load_float_default(stack, scale_offset, node2.x);
-  float exponent = stack_load_float_default(stack, e_offset, node2.y);
+ccl_device float voronoi_distance_4d(float4 a,
+                                     float4 b,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float exponent)
+{
+  if (metric == NODE_VORONOI_EUCLIDEAN) {
+    return distance(a, b);
+  }
+  else if (metric == NODE_VORONOI_MANHATTAN) {
+    return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z) + fabsf(a.w - b.w);
+  }
+  else if (metric == NODE_VORONOI_CHEBYCHEV) {
+    return max(fabsf(a.x - b.x), max(fabsf(a.y - b.y), max(fabsf(a.z - b.z), fabsf(a.w - b.w))));
+  }
+  else if (metric == NODE_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);
+  }
+  else {
+    return 0.0f;
+  }
+}
 
-  float dist[4];
-  float3 neighbor[4];
-  voronoi_neighbors(co * scale, (NodeVoronoiDistanceMetric)distance, exponent, dist, neighbor);
+ccl_device void voronoi_f1_4d(float4 coord,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float4 *outPosition)
+{
+  float4 cellPosition = floor(coord);
+  float4 localPosition = coord - cellPosition;
 
-  float3 color;
-  float fac;
-  if (coloring == NODE_VORONOI_INTENSITY) {
-    switch (feature) {
-      case NODE_VORONOI_F1:
-        fac = dist[0];
-        break;
-      case NODE_VORONOI_F2:
-        fac = dist[1];
-        break;
-      case NODE_VORONOI_F3:
-        fac = dist[2];
-        break;
-      case NODE_VORONOI_F4:
-        fac = dist[3];
-        break;
-      case NODE_VORONOI_F2F1:
-        fac = dist[1] - dist[0];
-        break;
+  float minDistance = 8.0f;
+  float4 targetOffset, targetPosition;
+  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++) {
+          float4 cellOffset = make_float4(i, j, k, u);
+          float4 pointPosition = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance_4d(
+              pointPosition, localPosition, metric, exponent);
+          if (distanceToPoint < minDistance) {
+            targetOffset = cellOffset;
+            minDistance = distanceToPoint;
+            targetPosition = pointPosition;
+          }
+        }
+      }
     }
+  }
+  *outDistance = minDistance;
+  *outColor = hash_float4_to_float3(cellPosition + targetOffset);
+  *outPosition = targetPosition + cellPosition;
+}
+
+ccl_device void voronoi_smooth_f1_4d(float4 coord,
+                                     float smoothness,
+                                     float exponent,
+                                     float randomness,
+                                     NodeVoronoiDistanceMetric metric,
+                                     float *outDistance,
+                                     float3 *outColor,
+                                     float4 *outPosition)
+{
+  float4 cellPosition = floor(coord);
+  float4 localPosition = coord - cellPosition;
 
-    color = make_float3(fac, fac, fac);
+  float smoothDistance = 8.0f;
+  float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
+  float4 smoothPosition = make_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++) {
+          float4 cellOffset = make_float4(i, j, k, u);
+          float4 pointPosition = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance_4d(
+              pointPosition, localPosition, metric, exponent);
+          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;
+          float3 cellColor = hash_float4_to_float3(cellPosition + cellOffset);
+          smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+          smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+        }
+      }
+    }
   }
-  else {
-    /* NODE_VORONOI_CELLS */
-    switch (feature) {
-      case NODE_VORONOI_F1:
-        color = neighbor[0];
-        break;
-      case NODE_VORONOI_F2:
-        color = neighbor[1];
-        break;
-      case NODE_VORONOI_F3:
-        color = neighbor[2];
-        break;
-      case NODE_VORONOI_F4:
-        color = neighbor[3];
-        break;
-      /* Usefulness of this vector is questionable. Note F2 >= F1 but the
-       * individual vector components might not be. */
-      case NODE_VORONOI_F2F1:
-        color = fabs(neighbor[1] - neighbor[0]);
-        break;
-    }
-
-    color = cellnoise3(color);
-    fac = average(color);
-  }
-
-  if (stack_valid(fac_offset))
-    stack_store_float(stack, fac_offset, fac);
-  if (stack_valid(color_offset))
-    stack_store_float3(stack, color_offset, color);
+  *outDistance = smoothDistance;
+  *outColor = smoothColor;
+  *outPosition = cellPosition + smoothPosition;
+}
+
+ccl_device void voronoi_f2_4d(float4 coord,
+                              float exponent,
+                              float randomness,
+                              NodeVoronoiDistanceMetric metric,
+                              float *outDistance,
+                              float3 *outColor,
+                              float4 *outPosition)
+{
+  float4 cellPosition = floor(coord);
+  float4 localPosition = coord - cellPosition;
+
+  float distanceF1 = 8.0f;
+  float distanceF2 = 8.0f;
+  float4 offsetF1 = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+  float4 positionF1 = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+  float4 offsetF2, positionF2;
+  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++) {
+          float4 cellOffset = make_float4(i, j, k, u);
+          float4 pointPosition = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance_4d(
+              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;
+          }
+        }
+      }
+    }
+  }
+  *outDistance = distanceF2;
+  *outColor = hash_float4_to_float3(cellPosition + offsetF2);
+  *outPosition = positionF2 + cellPosition;
+}
+
+ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, float *outDistance)
+{
+  float4 cellPosition = floor(coord);
+  float4 localPosition = coord - cellPosition;
+
+  float4 vectorToClosest;
+  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++) {
+          float4 cellOffset = make_float4(i, j, k, u);
+          float4 vectorToPoint = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness -
+                                 localPosition;
+          float distanceToPoint = dot(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++) {
+          float4 cellOffset = make_float4(i, j, k, u);
+          float4 vectorToPoint = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness -
+                                 localPosition;
+          float4 perpendicularToEdge = vectorToPoint - vectorToClosest;
+          if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
+            float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0f,
+                                       normalize(perpendicularToEdge));
+            minDistance = min(minDistance, distanceToEdge);
+          }
+        }
+      }
+    }
+  }
+  *outDistance = minDistance;
+}
+
+ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float *outRadius)
+{
+  float4 cellPosition = floor(coord);
+  float4 localPosition = coord - cellPosition;
+
+  float4 closestPoint;
+  float4 closestPointOffset;
+  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++) {
+          float4 cellOffset = make_float4(i, j, k, u);
+          float4 pointPosition = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = distance(pointPosition, localPosition);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            closestPoint = pointPosition;
+            closestPointOffset = cellOffset;
+          }
+        }
+      }
+    }
+  }
+
+  minDistance = 8.0f;
+  float4 closestPointToClosestPoint;
+  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;
+          }
+          float4 cellOffset = make_float4(i, j, k, u) + closestPointOffset;
+          float4 pointPosition = cellOffset +
+                                 hash_float4_to_float4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = distance(closestPoint, pointPosition);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            closestPointToClosestPoint = pointPosition;
+          }
+        }
+      }
+    }
+  }
+  *outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0f;
+}
+
+ccl_device void svm_node_tex_voronoi(KernelGlobals *kg,
+                                     ShaderData *sd,
+                                     float *stack,
+                                     uint dimensions,
+                                     uint feature,
+                                     uint metric,
+                                     int *offset)
+{
+  uint4 stack_offsets = read_node(kg, offset);
+  uint4 defaults = read_node(kg, offset);
+
+  uint coord_stack_offset, w_stack_offset, scale_stack_offset, smoothness_stack_offset;
+  uint exponent_stack_offset, randomness_stack_offset, distance_out_stack_offset,
+      color_out_stack_offset;
+  uint position_out_stack_offset, w_out_stack_offset, radius_out_stack_offset;
+
+  svm_unpack_node_uchar4(stack_offsets.x,
+                         &coord_stack_offset,
+                         &w_stack_offset,
+                         &scale_stack_offset,
+                         &smoothness_stack_offset);
+  svm_unpack_node_uchar4(stack_offsets.y,
+                         &exponent_stack_offset,
+                         &randomness_stack_offset,
+                         &distance_out_stack_offset,
+                         &color_out_stack_offset);
+  svm_unpack_node_uchar3(
+      stack_offsets.z, &position_out_stack_offset, &w_out_stack_offset, &radius_out_stack_offset);
+
+  float3 coord = stack_load_float3(stack, coord_stack_offset);
+  float w = stack_load_float_default(stack, w_stack_offset, stack_offsets.w);
+  float scale = stack_load_float_default(stack, scale_stack_offset, defaults.x);
+  float smoothness = stack_load_float_default(stack, smoothness_stack_offset, defaults.y);
+  float exponent = stack_load_float_default(stack, exponent_stack_offset, defaults.z);
+  float randomness = stack_load_float_default(stack, randomness_stack_offset, defaults.w);
+
+  NodeVoronoiFeature voronoi_feature = (NodeVoronoiFeature)feature;
+  NodeVoronoiDistanceMetric voronoi_metric = (NodeVoronoiDistanceMetric)metric;
+
+  float distance_out, w_out, radius_out;
+  float3 color_out, position_out;
+
+  randomness = clamp(randomness, 0.0f, 1.0f);
+  smoothness = clamp(smoothness / 2.0f, 0.0f, 0.5f);
+
+  w *= scale;
+  coord *= scale;
+
+  switch (dimensions) {
+    case 1: {
+      switch (voronoi_feature) {
+        case NODE_VORONOI_F1:
+          voronoi_f1_1d(
+              w, exponent, randomness, voronoi_metric, &distance_out, &color_out, &w_out);
+          break;
+        case NODE_VORONOI_SMOOTH_F1:
+          voronoi_smooth_f1_1d(w,
+                               smoothness,
+                               exponent,
+                               randomness,
+                               voronoi_metric,
+                               &distance_out,
+                               &color_out,
+                               &w_out);
+          break;
+        case NODE_VORONOI_F2:
+          voronoi_f2_1d(
+              w, exponent, randomness, voronoi_metric, &distance_out, &color_out, &w_out);
+          break;
+        case NODE_VORONOI_DISTANCE_TO_EDGE:
+          voronoi_distance_to_edge_1d(w, randomness, &distance_out);
+          break;
+        case NODE_VORONOI_N_SPHERE_RADIUS:
+          voronoi_n_sphere_radius_1d(w, randomness, &radius_out);
+          break;
+        default:
+          kernel_assert(0);
+      }
+      w_out = safe_divide(w_out, scale);
+      break;
+    }
+    case 2: {
+      float2 coord_2d = make_float2(coord.x, coord.y);
+      float2 position_out_2d;
+      switch (voronoi_feature) {
+        case NODE_VORONOI_F1:
+          voronoi_f1_2d(coord_2d,
+                        exponent,
+                        randomness,
+                        voronoi_metric,
+                        &distance_out,
+                        &color_out,
+                        &position_out_2d);
+          break;
+        case NODE_VORONOI_SMOOTH_F1:
+          voronoi_smooth_f1_2d(coord_2d,
+                               smoothness,
+                               exponent,
+                               randomness,
+                               voronoi_metric,
+                               &distance_out,
+                               &color_out,
+                               &position_out_2d);
+          break;
+        case NODE_VORONOI_F2:
+          voronoi_f2_2d(coord_2d,
+                        exponent,
+                        randomness,
+                        voronoi_metric,
+                        &distance_out,
+                        &color_out,
+                        &position_out_2d);
+          break;
+        case NODE_VORONOI_DISTANCE_TO_EDGE:
+          voronoi_distance_to_edge_2d(coord_2d, randomness, &distance_out);
+          break;
+        case NODE_VORONOI_N_SPHERE_RADIUS:
+          voronoi_n_sphere_radius_2d(coord_2d, randomness, &radius_out);
+          break;
+        default:
+          kernel_assert(0);
+      }
+      position_out_2d = safe_divide_float2_float(position_out_2d, scale);
+      position_out = make_float3(position_out_2d.x, position_out_2d.y, 0.0f);
+      break;
+    }
+    case 3: {
+      switch (voronoi_feature) {
+        case NODE_VORONOI_F1:
+          voronoi_f1_3d(coord,
+                        exponent,
+                        randomness,
+                        voronoi_metric,
+                        &distance_out,
+                        &color_out,
+                        &position_out);
+          break;
+        case NODE_VORONOI_SMOOTH_F1:
+          voronoi_smooth_f1_3d(coord,
+                               smoothness,
+                               exponent,
+                               randomness,
+                               voronoi_metric,
+                               &distance_out,
+                               &color_out,
+                               &position_out);
+          break;
+        case NODE_VORONOI_F2:
+          voronoi_f2_3d(coord,
+                        exponent,
+                        randomness,
+                        voronoi_metric,
+                        &distance_out,
+                        &color_out,
+                        &position_out);
+          break;
+        case NODE_VORONOI_DISTANCE_TO_EDGE:
+          voronoi_distance_to_edge_3d(coord, randomness, &distance_out);
+          break;
+        case NODE_VORONOI_N_SPHERE_RADIUS:
+          voronoi_n_sphere_radius_3d(coord, randomness, &radius_out);
+          break;
+        default:
+          kernel_assert(0);
+      }
+      position_out = safe_divide_float3_float(position_out, scale);
+      break;
+    }
+    case 4: {
+      float4 coord_4d = make_float4(coord.x, coord.y, coord.z, w);
+      float4 position_out_4d;
+      switch (voronoi_feature) {
+        case NODE_VORONOI_F1:
+          voronoi_f1_4d(coord_4d,
+                        exponent,
+                        randomness,
+                        voronoi_metric,
+                        &distance_out,
+                        &color_out,
+                        &position_out_4d);
+          break;
+        case NODE_VORONOI_SMOOTH_F1:
+          voronoi_smooth_f1_4d(coord_4d,
+                               smoothness,
+                               exponent,
+                               randomness,
+                               voronoi_metric,
+                               &distance_out,
+                               &color_out,
+                               &position_out_4d);
+          break;
+        case NODE_VORONOI_F2:
+          voronoi_f2_4d(coord_4d,
+                        exponent,
+                        randomness,
+                        voronoi_metric,
+                        &distance_out,
+                        &color_out,
+                        &position_out_4d);
+          break;
+        case NODE_VORONOI_DISTANCE_TO_EDGE:
+          voronoi_distance_to_edge_4d(coord_4d, randomness, &distance_out);
+          break;
+        case NODE_VORONOI_N_SPHERE_RADIUS:
+          voronoi_n_sphere_radius_4d(coord_4d, randomness, &radius_out);
+          break;
+        default:
+          kernel_assert(0);
+      }
+      position_out_4d = safe_divide_float4_float(position_out_4d, scale);
+      position_out = make_float3(position_out_4d.x, position_out_4d.y, position_out_4d.z);
+      w_out = position_out_4d.w;
+      break;
+    }
+    default:
+      kernel_assert(0);
+  }
+
+  if (stack_valid(distance_out_stack_offset))
+    stack_store_float(stack, distance_out_stack_offset, distance_out);
+  if (stack_valid(color_out_stack_offset))
+    stack_store_float3(stack, color_out_stack_offset, color_out);
+  if (stack_valid(position_out_stack_offset))
+    stack_store_float3(stack, position_out_stack_offset, position_out);
+  if (stack_valid(w_out_stack_offset))
+    stack_store_float(stack, w_out_stack_offset, w_out);
+  if (stack_valid(radius_out_stack_offset))
+    stack_store_float(stack, radius_out_stack_offset, radius_out);
 }
 
 CCL_NAMESPACE_END
index 01661a6d1e0c7622f87f729e768b1a0da20c1092..1d98fabbc9f9dc14faf715a6588b9ea5524986de 100644 (file)
@@ -965,33 +965,41 @@ NODE_DEFINE(VoronoiTextureNode)
 
   TEXTURE_MAPPING_DEFINE(VoronoiTextureNode);
 
-  static NodeEnum coloring_enum;
-  coloring_enum.insert("intensity", NODE_VORONOI_INTENSITY);
-  coloring_enum.insert("cells", NODE_VORONOI_CELLS);
-  SOCKET_ENUM(coloring, "Coloring", coloring_enum, NODE_VORONOI_INTENSITY);
-
-  static NodeEnum metric;
-  metric.insert("distance", NODE_VORONOI_DISTANCE);
-  metric.insert("manhattan", NODE_VORONOI_MANHATTAN);
-  metric.insert("chebychev", NODE_VORONOI_CHEBYCHEV);
-  metric.insert("minkowski", NODE_VORONOI_MINKOWSKI);
-  SOCKET_ENUM(metric, "Distance Metric", metric, NODE_VORONOI_INTENSITY);
+  static NodeEnum dimensions_enum;
+  dimensions_enum.insert("1D", 1);
+  dimensions_enum.insert("2D", 2);
+  dimensions_enum.insert("3D", 3);
+  dimensions_enum.insert("4D", 4);
+  SOCKET_ENUM(dimensions, "Dimensions", dimensions_enum, 3);
+
+  static NodeEnum metric_enum;
+  metric_enum.insert("euclidean", NODE_VORONOI_EUCLIDEAN);
+  metric_enum.insert("manhattan", NODE_VORONOI_MANHATTAN);
+  metric_enum.insert("chebychev", NODE_VORONOI_CHEBYCHEV);
+  metric_enum.insert("minkowski", NODE_VORONOI_MINKOWSKI);
+  SOCKET_ENUM(metric, "Distance Metric", metric_enum, NODE_VORONOI_EUCLIDEAN);
 
   static NodeEnum feature_enum;
-  feature_enum.insert("F1", NODE_VORONOI_F1);
-  feature_enum.insert("F2", NODE_VORONOI_F2);
-  feature_enum.insert("F3", NODE_VORONOI_F3);
-  feature_enum.insert("F4", NODE_VORONOI_F4);
-  feature_enum.insert("F2F1", NODE_VORONOI_F2F1);
-  SOCKET_ENUM(feature, "Feature", feature_enum, NODE_VORONOI_INTENSITY);
+  feature_enum.insert("f1", NODE_VORONOI_F1);
+  feature_enum.insert("f2", NODE_VORONOI_F2);
+  feature_enum.insert("smooth_f1", NODE_VORONOI_SMOOTH_F1);
+  feature_enum.insert("distance_to_edge", NODE_VORONOI_DISTANCE_TO_EDGE);
+  feature_enum.insert("n_sphere_radius", NODE_VORONOI_N_SPHERE_RADIUS);
+  SOCKET_ENUM(feature, "Feature", feature_enum, NODE_VORONOI_F1);
 
-  SOCKET_IN_FLOAT(scale, "Scale", 1.0f);
-  SOCKET_IN_FLOAT(exponent, "Exponent", 0.5f);
   SOCKET_IN_POINT(
       vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED);
+  SOCKET_IN_FLOAT(w, "W", 0.0f);
+  SOCKET_IN_FLOAT(scale, "Scale", 5.0f);
+  SOCKET_IN_FLOAT(smoothness, "Smoothness", 5.0f);
+  SOCKET_IN_FLOAT(exponent, "Exponent", 0.5f);
+  SOCKET_IN_FLOAT(randomness, "Randomness", 1.0f);
 
+  SOCKET_OUT_FLOAT(distance, "Distance");
   SOCKET_OUT_COLOR(color, "Color");
-  SOCKET_OUT_FLOAT(fac, "Fac");
+  SOCKET_OUT_POINT(position, "Position");
+  SOCKET_OUT_FLOAT(w, "W");
+  SOCKET_OUT_FLOAT(radius, "Radius");
 
   return type;
 }
@@ -1002,39 +1010,57 @@ VoronoiTextureNode::VoronoiTextureNode() : TextureNode(node_type)
 
 void VoronoiTextureNode::compile(SVMCompiler &compiler)
 {
-  ShaderInput *scale_in = input("Scale");
   ShaderInput *vector_in = input("Vector");
+  ShaderInput *w_in = input("W");
+  ShaderInput *scale_in = input("Scale");
+  ShaderInput *smoothness_in = input("Smoothness");
   ShaderInput *exponent_in = input("Exponent");
+  ShaderInput *randomness_in = input("Randomness");
+
+  ShaderOutput *distance_out = output("Distance");
   ShaderOutput *color_out = output("Color");
-  ShaderOutput *fac_out = output("Fac");
+  ShaderOutput *position_out = output("Position");
+  ShaderOutput *w_out = output("W");
+  ShaderOutput *radius_out = output("Radius");
 
-  if (vector_in->link)
-    compiler.stack_assign(vector_in);
-  if (scale_in->link)
-    compiler.stack_assign(scale_in);
-  if (exponent_in->link)
-    compiler.stack_assign(exponent_in);
+  int vector_stack_offset = tex_mapping.compile_begin(compiler, vector_in);
+  int w_in_stack_offset = compiler.stack_assign_if_linked(w_in);
+  int scale_stack_offset = compiler.stack_assign_if_linked(scale_in);
+  int smoothness_stack_offset = compiler.stack_assign_if_linked(smoothness_in);
+  int exponent_stack_offset = compiler.stack_assign_if_linked(exponent_in);
+  int randomness_stack_offset = compiler.stack_assign_if_linked(randomness_in);
+  int distance_stack_offset = compiler.stack_assign_if_linked(distance_out);
+  int color_stack_offset = compiler.stack_assign_if_linked(color_out);
+  int position_stack_offset = compiler.stack_assign_if_linked(position_out);
+  int w_out_stack_offset = compiler.stack_assign_if_linked(w_out);
+  int radius_stack_offset = compiler.stack_assign_if_linked(radius_out);
 
-  int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
+  compiler.add_node(NODE_TEX_VORONOI, dimensions, feature, metric);
+  compiler.add_node(
+      compiler.encode_uchar4(
+          vector_stack_offset, w_in_stack_offset, scale_stack_offset, smoothness_stack_offset),
+      compiler.encode_uchar4(exponent_stack_offset,
+                             randomness_stack_offset,
+                             distance_stack_offset,
+                             color_stack_offset),
+      compiler.encode_uchar4(position_stack_offset, w_out_stack_offset, radius_stack_offset),
+      __float_as_int(w));
 
-  compiler.add_node(NODE_TEX_VORONOI,
-                    compiler.encode_uchar4(vector_offset, coloring, metric, feature),
-                    compiler.encode_uchar4(compiler.stack_assign_if_linked(scale_in),
-                                           compiler.stack_assign_if_linked(exponent_in),
-                                           compiler.stack_assign(fac_out),
-                                           compiler.stack_assign(color_out)));
-  compiler.add_node(__float_as_int(scale), __float_as_int(exponent));
+  compiler.add_node(__float_as_int(scale),
+                    __float_as_int(smoothness),
+                    __float_as_int(exponent),
+                    __float_as_int(randomness));
 
-  tex_mapping.compile_end(compiler, vector_in, vector_offset);
+  tex_mapping.compile_end(compiler, vector_in, vector_stack_offset);
 }
 
 void VoronoiTextureNode::compile(OSLCompiler &compiler)
 {
   tex_mapping.compile(compiler);
 
-  compiler.parameter(this, "coloring");
-  compiler.parameter(this, "metric");
+  compiler.parameter(this, "dimensions");
   compiler.parameter(this, "feature");
+  compiler.parameter(this, "metric");
   compiler.add(this, "node_voronoi_texture");
 }
 
index 5d7e074f62b19ee1b24088fd80268157cbd3c086..171568644fb45a59cc6e4e78f45ee70477ace32e 100644 (file)
@@ -220,10 +220,10 @@ class VoronoiTextureNode : public TextureNode {
     return NODE_GROUP_LEVEL_2;
   }
 
-  NodeVoronoiColoring coloring;
+  int dimensions;
   NodeVoronoiDistanceMetric metric;
   NodeVoronoiFeature feature;
-  float scale, exponent;
+  float w, scale, exponent, smoothness, randomness;
   float3 vector;
 };
 
index 17bb766445d63ce211c3e821d7f1c486e339affb..53e528de66ea0f1c12657ec24cdcb8040ca29eea 100644 (file)
@@ -294,6 +294,21 @@ ccl_device_inline float mix(float a, float b, float t)
 {
   return a + t * (b - a);
 }
+
+ccl_device_inline float smoothstep(float edge0, float edge1, float x)
+{
+  float result;
+  if (x < edge0)
+    result = 0.0f;
+  else if (x >= edge1)
+    result = 1.0f;
+  else {
+    float t = (x - edge0) / (edge1 - edge0);
+    result = (3.0f - 2.0f * t) * (t * t);
+  }
+  return result;
+}
+
 #endif /* __KERNEL_OPENCL__ */
 
 #ifndef __KERNEL_CUDA__
index 9feaf042d1979aa1056c402cf0ff089a54f80bfb..bf21430af3ca0f3c3b88f7daa390adfd8d796331 100644 (file)
@@ -35,7 +35,9 @@ ccl_device_inline float2 operator*(float f, const float2 &a);
 ccl_device_inline float2 operator/(float f, const float2 &a);
 ccl_device_inline float2 operator/(const float2 &a, float f);
 ccl_device_inline float2 operator/(const float2 &a, const float2 &b);
+ccl_device_inline float2 operator+(const float2 &a, const float f);
 ccl_device_inline float2 operator+(const float2 &a, const float2 &b);
+ccl_device_inline float2 operator-(const float2 &a, const float f);
 ccl_device_inline float2 operator-(const float2 &a, const float2 &b);
 ccl_device_inline float2 operator+=(float2 &a, const float2 &b);
 ccl_device_inline float2 operator*=(float2 &a, const float2 &b);
@@ -48,6 +50,7 @@ ccl_device_inline bool operator!=(const float2 &a, const float2 &b);
 
 ccl_device_inline bool is_zero(const float2 &a);
 ccl_device_inline float average(const float2 &a);
+ccl_device_inline float distance(const float2 &a, const float2 &b);
 ccl_device_inline float dot(const float2 &a, const float2 &b);
 ccl_device_inline float cross(const float2 &a, const float2 &b);
 ccl_device_inline float len(const float2 &a);
@@ -60,8 +63,11 @@ ccl_device_inline float2 clamp(const float2 &a, const float2 &mn, const float2 &
 ccl_device_inline float2 fabs(const float2 &a);
 ccl_device_inline float2 as_float2(const float4 &a);
 ccl_device_inline float2 interp(const float2 &a, const float2 &b, float t);
+ccl_device_inline float2 floor(const float2 &a);
 #endif /* !__KERNEL_OPENCL__ */
 
+ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b);
+
 /*******************************************************************************
  * Definition.
  */
@@ -103,11 +109,21 @@ ccl_device_inline float2 operator/(const float2 &a, const float2 &b)
   return make_float2(a.x / b.x, a.y / b.y);
 }
 
+ccl_device_inline float2 operator+(const float2 &a, const float f)
+{
+  return a + make_float2(f, f);
+}
+
 ccl_device_inline float2 operator+(const float2 &a, const float2 &b)
 {
   return make_float2(a.x + b.x, a.y + b.y);
 }
 
+ccl_device_inline float2 operator-(const float2 &a, const float f)
+{
+  return a - make_float2(f, f);
+}
+
 ccl_device_inline float2 operator-(const float2 &a, const float2 &b)
 {
   return make_float2(a.x - b.x, a.y - b.y);
@@ -159,6 +175,11 @@ ccl_device_inline float average(const float2 &a)
   return (a.x + a.y) * (1.0f / 2.0f);
 }
 
+ccl_device_inline float distance(const float2 &a, const float2 &b)
+{
+  return len(a - b);
+}
+
 ccl_device_inline float dot(const float2 &a, const float2 &b)
 {
   return a.x * b.x + a.y * b.y;
@@ -226,8 +247,18 @@ ccl_device_inline float2 mix(const float2 &a, const float2 &b, float t)
   return a + t * (b - a);
 }
 
+ccl_device_inline float2 floor(const float2 &a)
+{
+  return make_float2(floorf(a.x), floorf(a.y));
+}
+
 #endif /* !__KERNEL_OPENCL__ */
 
+ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b)
+{
+  return (b != 0.0f) ? a / b : make_float2(0.0f, 0.0f);
+}
+
 CCL_NAMESPACE_END
 
 #endif /* __UTIL_MATH_FLOAT2_H__ */
index c9a5b34aa58d53ec13f3e912024268aee4266f42..dd2010715babc61e116eac3267eb2da5c250c1fb 100644 (file)
@@ -35,7 +35,9 @@ ccl_device_inline float3 operator*(const float f, const float3 &a);
 ccl_device_inline float3 operator/(const float f, const float3 &a);
 ccl_device_inline float3 operator/(const float3 &a, const float f);
 ccl_device_inline float3 operator/(const float3 &a, const float3 &b);
+ccl_device_inline float3 operator+(const float3 &a, const float f);
 ccl_device_inline float3 operator+(const float3 &a, const float3 &b);
+ccl_device_inline float3 operator-(const float3 &a, const float f);
 ccl_device_inline float3 operator-(const float3 &a, const float3 &b);
 ccl_device_inline float3 operator+=(float3 &a, const float3 &b);
 ccl_device_inline float3 operator-=(float3 &a, const float3 &b);
@@ -150,6 +152,11 @@ ccl_device_inline float3 operator/(const float3 &a, const float3 &b)
 #  endif
 }
 
+ccl_device_inline float3 operator+(const float3 &a, const float f)
+{
+  return a + make_float3(f, f, f);
+}
+
 ccl_device_inline float3 operator+(const float3 &a, const float3 &b)
 {
 #  ifdef __KERNEL_SSE__
@@ -159,6 +166,11 @@ ccl_device_inline float3 operator+(const float3 &a, const float3 &b)
 #  endif
 }
 
+ccl_device_inline float3 operator-(const float3 &a, const float f)
+{
+  return a - make_float3(f, f, f);
+}
+
 ccl_device_inline float3 operator-(const float3 &a, const float3 &b)
 {
 #  ifdef __KERNEL_SSE__
index 1fb886572e3548e777b634b1255261857b044b1d..bc5322a22bbd45abb70b47c21eedf4a2ddad98be 100644 (file)
@@ -34,7 +34,9 @@ ccl_device_inline float4 operator*(const float4 &a, float f);
 ccl_device_inline float4 operator*(float f, const float4 &a);
 ccl_device_inline float4 operator/(const float4 &a, float f);
 ccl_device_inline float4 operator/(const float4 &a, const float4 &b);
+ccl_device_inline float4 operator+(const float4 &a, const float f);
 ccl_device_inline float4 operator+(const float4 &a, const float4 &b);
+ccl_device_inline float4 operator-(const float4 &a, const float f);
 ccl_device_inline float4 operator-(const float4 &a, const float4 &b);
 ccl_device_inline float4 operator+=(float4 &a, const float4 &b);
 ccl_device_inline float4 operator*=(float4 &a, const float4 &b);
@@ -46,6 +48,7 @@ ccl_device_inline int4 operator>=(const float4 &a, const float4 &b);
 ccl_device_inline int4 operator<=(const float4 &a, const float4 &b);
 ccl_device_inline bool operator==(const float4 &a, const float4 &b);
 
+ccl_device_inline float distance(const float4 &a, const float4 &b);
 ccl_device_inline float dot(const float4 &a, const float4 &b);
 ccl_device_inline float len_squared(const float4 &a);
 ccl_device_inline float4 rcp(const float4 &a);
@@ -61,8 +64,12 @@ ccl_device_inline float4 min(const float4 &a, const float4 &b);
 ccl_device_inline float4 max(const float4 &a, const float4 &b);
 ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 &mx);
 ccl_device_inline float4 fabs(const float4 &a);
+ccl_device_inline float4 floor(const float4 &a);
+ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t);
 #endif /* !__KERNEL_OPENCL__*/
 
+ccl_device_inline float4 safe_divide_float4_float(const float4 a, const float b);
+
 #ifdef __KERNEL_SSE__
 template<size_t index_0, size_t index_1, size_t index_2, size_t index_3>
 __forceinline const float4 shuffle(const float4 &b);
@@ -139,6 +146,11 @@ ccl_device_inline float4 operator/(const float4 &a, const float4 &b)
 #  endif
 }
 
+ccl_device_inline float4 operator+(const float4 &a, const float f)
+{
+  return a + make_float4(f, f, f, f);
+}
+
 ccl_device_inline float4 operator+(const float4 &a, const float4 &b)
 {
 #  ifdef __KERNEL_SSE__
@@ -148,6 +160,11 @@ ccl_device_inline float4 operator+(const float4 &a, const float4 &b)
 #  endif
 }
 
+ccl_device_inline float4 operator-(const float4 &a, const float f)
+{
+  return a - make_float4(f, f, f, f);
+}
+
 ccl_device_inline float4 operator-(const float4 &a, const float4 &b)
 {
 #  ifdef __KERNEL_SSE__
@@ -213,6 +230,11 @@ ccl_device_inline bool operator==(const float4 &a, const float4 &b)
 #  endif
 }
 
+ccl_device_inline float distance(const float4 &a, const float4 &b)
+{
+  return len(a - b);
+}
+
 ccl_device_inline float dot(const float4 &a, const float4 &b)
 {
 #  if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__)
@@ -338,6 +360,21 @@ ccl_device_inline float4 fabs(const float4 &a)
   return make_float4(fabsf(a.x), fabsf(a.y), fabsf(a.z), fabsf(a.w));
 #  endif
 }
+
+ccl_device_inline float4 floor(const float4 &a)
+{
+#  ifdef __KERNEL_SSE__
+  return float4(_mm_floor_ps(a));
+#  else
+  return make_float4(floorf(a.x), floorf(a.y), floorf(a.z), floorf(a.w));
+#  endif
+}
+
+ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t)
+{
+  return a + t * (b - a);
+}
+
 #endif /* !__KERNEL_OPENCL__*/
 
 #ifdef __KERNEL_SSE__
@@ -430,6 +467,11 @@ ccl_device_inline float4 load_float4(const float *v)
 
 #endif /* !__KERNEL_GPU__ */
 
+ccl_device_inline float4 safe_divide_float4_float(const float4 a, const float b)
+{
+  return (b != 0.0f) ? a / b : make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
 CCL_NAMESPACE_END
 
 #endif /* __UTIL_MATH_FLOAT4_H__ */
index b9ea85e7a00c778d380ae2875b6f8e85a1ed180e..89f83c44c11fbbff2a99dbcc207f619944a38447 100644 (file)
@@ -27,7 +27,7 @@
  * \note Use #STRINGIFY() rather than defining with quotes.
  */
 #define BLENDER_VERSION 281
-#define BLENDER_SUBVERSION 10
+#define BLENDER_SUBVERSION 11
 /** Several breakages with 280, e.g. collections vs layers. */
 #define BLENDER_MINVERSION 280
 #define BLENDER_MINSUBVERSION 0
index 9c00d829e46ec5a79c70ca2a70fa8a2775ecd14d..52d62725ef8c8cb9a5ecb3228c531c09cab2e674 100644 (file)
@@ -946,6 +946,238 @@ static void update_musgrave_node_color_output(bNodeTree *ntree)
   }
 }
 
+/* The Voronoi node now have a dimension property. This property should be
+ * initialized to 3 by default.
+ */
+static void update_voronoi_node_dimensions(bNodeTree *ntree)
+{
+  for (bNode *node = ntree->nodes.first; node; node = node->next) {
+    if (node->type == SH_NODE_TEX_VORONOI) {
+      NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
+      tex->dimensions = 3;
+    }
+  }
+}
+
+/* The F3 and F4 features of the Voronoi node have been removed.
+ * To correct this, we set the feature type to be F2 if it is F3
+ * or F4. The SHD_VORONOI_F3 and SHD_VORONOI_F4 enum values were
+ * 2 and 3 respectively.
+ */
+static void update_voronoi_node_f3_and_f4(bNodeTree *ntree)
+{
+  for (bNode *node = ntree->nodes.first; node; node = node->next) {
+    if (node->type == SH_NODE_TEX_VORONOI) {
+      NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
+      if (ELEM(tex->feature, 2, 3)) {
+        tex->feature = SHD_VORONOI_F2;
+      }
+    }
+  }
+}
+
+/* The Fac output of the Voronoi node has been removed. Previously, this
+ * output was the voronoi distance in the Intensity mode and the Cell ID
+ * in the Cell mode. To correct this, we update the identifier and name
+ * of the Fac socket such that it gets mapped to the Distance socket.
+ * This is supposed to work with update_voronoi_node_coloring.
+ */
+static void update_voronoi_node_fac_output(bNodeTree *ntree)
+{
+  for (bNode *node = ntree->nodes.first; node; node = node->next) {
+    if (node->type == SH_NODE_TEX_VORONOI) {
+      bNodeSocket *facOutput = BLI_findlink(&node->outputs, 1);
+      strcpy(facOutput->identifier, "Distance");
+      strcpy(facOutput->name, "Distance");
+    }
+  }
+}
+
+/* The Crackle feature of the Voronoi node has been removed. Previously,
+ * this feature returned the F2 distance minus the F1 distance. The
+ * crackle feature had an enum value of 4. To fix this we do the
+ * following:
+ *
+ * 1. The node feature is set to F1.
+ * 2. A new Voronoi node is added and its feature is set to F2.
+ * 3. The properties, input values, and connections are copied
+ *    from the node to the new Voronoi node so that they match
+ *    exactly.
+ * 4. A Subtract node is added.
+ * 5. The outputs of the F1 and F2 voronoi are connected to
+ *    the inputs of the subtract node.
+ * 6. The output of the subtract node is connected to the
+ *    appropriate sockets.
+ *
+ */
+static void update_voronoi_node_crackle(bNodeTree *ntree)
+{
+  bool need_update = false;
+
+  for (bNode *node = ntree->nodes.first; node; node = node->next) {
+    if (node->type == SH_NODE_TEX_VORONOI) {
+      NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
+      bNodeSocket *sockDistance = nodeFindSocket(node, SOCK_OUT, "Distance");
+      bNodeSocket *sockColor = nodeFindSocket(node, SOCK_OUT, "Color");
+      if (tex->feature == 4 && (socket_is_used(sockDistance) || socket_is_used(sockColor))) {
+        tex->feature = SHD_VORONOI_F1;
+
+        bNode *voronoiNode = nodeAddStaticNode(NULL, ntree, SH_NODE_TEX_VORONOI);
+        NodeTexVoronoi *texVoronoi = (NodeTexVoronoi *)voronoiNode->storage;
+        texVoronoi->feature = SHD_VORONOI_F2;
+        texVoronoi->distance = tex->distance;
+        texVoronoi->dimensions = 3;
+        voronoiNode->locx = node->locx + node->width + 20.0f;
+        voronoiNode->locy = node->locy;
+
+        bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector");
+        bNodeSocket *sockScale = nodeFindSocket(node, SOCK_IN, "Scale");
+        bNodeSocket *sockExponent = nodeFindSocket(node, SOCK_IN, "Exponent");
+        bNodeSocket *sockVoronoiVector = nodeFindSocket(voronoiNode, SOCK_IN, "Vector");
+        bNodeSocket *sockVoronoiScale = nodeFindSocket(voronoiNode, SOCK_IN, "Scale");
+        bNodeSocket *sockVoronoiExponent = nodeFindSocket(voronoiNode, SOCK_IN, "Exponent");
+        if (sockVector->link) {
+          nodeAddLink(ntree,
+                      sockVector->link->fromnode,
+                      sockVector->link->fromsock,
+                      voronoiNode,
+                      sockVoronoiVector);
+        }
+        *cycles_node_socket_float_value(sockVoronoiScale) = *cycles_node_socket_float_value(
+            sockScale);
+        if (sockScale->link) {
+          nodeAddLink(ntree,
+                      sockScale->link->fromnode,
+                      sockScale->link->fromsock,
+                      voronoiNode,
+                      sockVoronoiScale);
+        }
+        *cycles_node_socket_float_value(sockVoronoiExponent) = *cycles_node_socket_float_value(
+            sockExponent);
+        if (sockExponent->link) {
+          nodeAddLink(ntree,
+                      sockExponent->link->fromnode,
+                      sockExponent->link->fromsock,
+                      voronoiNode,
+                      sockVoronoiExponent);
+        }
+
+        bNode *subtractNode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+        subtractNode->custom1 = NODE_MATH_SUBTRACT;
+        subtractNode->locx = voronoiNode->locx + voronoiNode->width + 20.0f;
+        subtractNode->locy = voronoiNode->locy;
+        bNodeSocket *sockSubtractOutValue = nodeFindSocket(subtractNode, SOCK_OUT, "Value");
+
+        LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &ntree->links) {
+          if (link->fromnode == node) {
+            nodeAddLink(ntree, subtractNode, sockSubtractOutValue, link->tonode, link->tosock);
+            nodeRemLink(ntree, link);
+          }
+        }
+
+        bNodeSocket *sockDistanceF1 = nodeFindSocket(node, SOCK_OUT, "Distance");
+        bNodeSocket *sockDistanceF2 = nodeFindSocket(voronoiNode, SOCK_OUT, "Distance");
+        bNodeSocket *sockSubtractA = BLI_findlink(&subtractNode->inputs, 0);
+        bNodeSocket *sockSubtractB = BLI_findlink(&subtractNode->inputs, 1);
+
+        nodeAddLink(ntree, node, sockDistanceF1, subtractNode, sockSubtractB);
+        nodeAddLink(ntree, voronoiNode, sockDistanceF2, subtractNode, sockSubtractA);
+
+        need_update = true;
+      }
+    }
+  }
+
+  if (need_update) {
+    ntreeUpdateTree(NULL, ntree);
+  }
+}
+
+/* The coloring property of the Voronoi node was removed. Previously,
+ * if the coloring enum was set to Intensity (0), the voronoi distance
+ * was returned in all outputs, otherwise, the Cell ID was returned.
+ * Since we remapped the Fac output in update_voronoi_node_fac_output,
+ * then to fix this, we relink the Color output to the Distance
+ * output if coloring was set to 0, and the otherway around otherwise.
+ */
+static void update_voronoi_node_coloring(bNodeTree *ntree)
+{
+  bool need_update = false;
+
+  LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &ntree->links) {
+    bNode *node = link->fromnode;
+    if (node && node->type == SH_NODE_TEX_VORONOI) {
+      NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
+      if (tex->coloring == 0) {
+        bNodeSocket *sockColor = nodeFindSocket(node, SOCK_OUT, "Color");
+        if (link->fromsock == sockColor) {
+          bNodeSocket *sockDistance = nodeFindSocket(node, SOCK_OUT, "Distance");
+          nodeAddLink(ntree, node, sockDistance, link->tonode, link->tosock);
+          nodeRemLink(ntree, link);
+          need_update = true;
+        }
+      }
+      else {
+        bNodeSocket *sockDistance = nodeFindSocket(node, SOCK_OUT, "Distance");
+        if (link->fromsock == sockDistance) {
+          bNodeSocket *sockColor = nodeFindSocket(node, SOCK_OUT, "Color");
+          nodeAddLink(ntree, node, sockColor, link->tonode, link->tosock);
+          nodeRemLink(ntree, link);
+          need_update = true;
+        }
+      }
+    }
+  }
+
+  if (need_update) {
+    ntreeUpdateTree(NULL, ntree);
+  }
+}
+
+/* Previously, the output euclidean distance was actually the squared
+ * euclidean distance. To fix this, we square the the output distance
+ * socket if the distance metric is set to SHD_VORONOI_EUCLIDEAN.
+ */
+static void update_voronoi_node_square_distance(bNodeTree *ntree)
+{
+  bool need_update = false;
+
+  for (bNode *node = ntree->nodes.first; node; node = node->next) {
+    if (node->type == SH_NODE_TEX_VORONOI) {
+      NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
+      bNodeSocket *sockDistance = nodeFindSocket(node, SOCK_OUT, "Distance");
+      if (tex->distance == SHD_VORONOI_EUCLIDEAN &&
+          (tex->feature == SHD_VORONOI_F1 || tex->feature == SHD_VORONOI_F2) &&
+          socket_is_used(sockDistance)) {
+        bNode *multiplyNode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+        multiplyNode->custom1 = NODE_MATH_MULTIPLY;
+        multiplyNode->locx = node->locx + node->width + 20.0f;
+        multiplyNode->locy = node->locy;
+
+        bNodeSocket *sockValue = nodeFindSocket(multiplyNode, SOCK_OUT, "Value");
+        LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &ntree->links) {
+          if (link->fromsock == sockDistance) {
+            nodeAddLink(ntree, multiplyNode, sockValue, link->tonode, link->tosock);
+            nodeRemLink(ntree, link);
+          }
+        }
+
+        bNodeSocket *sockMultiplyA = BLI_findlink(&multiplyNode->inputs, 0);
+        bNodeSocket *sockMultiplyB = BLI_findlink(&multiplyNode->inputs, 1);
+
+        nodeAddLink(ntree, node, sockDistance, multiplyNode, sockMultiplyA);
+        nodeAddLink(ntree, node, sockDistance, multiplyNode, sockMultiplyB);
+
+        need_update = true;
+      }
+    }
+  }
+
+  if (need_update) {
+    ntreeUpdateTree(NULL, ntree);
+  }
+}
+
 void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bmain)
 {
   /* Particle shape shared with Eevee. */
@@ -996,6 +1228,16 @@ void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bm
     }
     FOREACH_NODETREE_END;
   }
+
+  if (!MAIN_VERSION_ATLEAST(bmain, 281, 11)) {
+    FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+      if (ntree->type == NTREE_SHADER) {
+        update_voronoi_node_f3_and_f4(ntree);
+        update_voronoi_node_fac_output(ntree);
+      }
+    }
+    FOREACH_NODETREE_END;
+  }
 }
 
 void do_versions_after_linking_cycles(Main *bmain)
@@ -1154,4 +1396,16 @@ void do_versions_after_linking_cycles(Main *bmain)
     }
     FOREACH_NODETREE_END;
   }
+
+  if (!MAIN_VERSION_ATLEAST(bmain, 281, 11)) {
+    FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+      if (ntree->type == NTREE_SHADER) {
+        update_voronoi_node_dimensions(ntree);
+        update_voronoi_node_crackle(ntree);
+        update_voronoi_node_coloring(ntree);
+        update_voronoi_node_square_distance(ntree);
+      }
+    }
+    FOREACH_NODETREE_END;
+  }
 }
index c707a1eed926124f29e0c4786b585fbe479720a5..6055a503b7bafcfe68530d3b03e9be4cba1dab25 100644 (file)
@@ -914,9 +914,13 @@ static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C),
 
 static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
 {
-  uiItemR(layout, ptr, "coloring", 0, "", ICON_NONE);
-  uiItemR(layout, ptr, "distance", 0, "", ICON_NONE);
+  uiItemR(layout, ptr, "voronoi_dimensions", 0, "", ICON_NONE);
   uiItemR(layout, ptr, "feature", 0, "", ICON_NONE);
+  int feature = RNA_enum_get(ptr, "feature");
+  if (!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS) &&
+      RNA_enum_get(ptr, "voronoi_dimensions") != 1) {
+    uiItemR(layout, ptr, "distance", 0, "", ICON_NONE);
+  }
 }
 
 static void node_shader_buts_tex_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
index f11dcc9bcf03fe37c6c6ee5bd3ac6269b863f84e..aea96dac2e3b5569df95a895564b72315b3bbebb 100644 (file)
@@ -243,7 +243,6 @@ data_to_c_simple(shaders/material/gpu_shader_material_blackbody.glsl SRC)
 data_to_c_simple(shaders/material/gpu_shader_material_bright_contrast.glsl SRC)
 data_to_c_simple(shaders/material/gpu_shader_material_bump.glsl SRC)
 data_to_c_simple(shaders/material/gpu_shader_material_camera.glsl SRC)
-data_to_c_simple(shaders/material/gpu_shader_material_cell_noise.glsl SRC)
 data_to_c_simple(shaders/material/gpu_shader_material_clamp.glsl SRC)
 data_to_c_simple(shaders/material/gpu_shader_material_color_ramp.glsl SRC)
 data_to_c_simple(shaders/material/gpu_shader_material_color_util.glsl SRC)
index 9c0cf3e6bea7e84d04515f154d57f9fced1e9e15..06544d27af94aaba676bb2f0929261a76d05a224 100644 (file)
@@ -44,7 +44,6 @@ extern char datatoc_gpu_shader_material_blackbody_glsl[];
 extern char datatoc_gpu_shader_material_bright_contrast_glsl[];
 extern char datatoc_gpu_shader_material_bump_glsl[];
 extern char datatoc_gpu_shader_material_camera_glsl[];
-extern char datatoc_gpu_shader_material_cell_noise_glsl[];
 extern char datatoc_gpu_shader_material_clamp_glsl[];
 extern char datatoc_gpu_shader_material_color_ramp_glsl[];
 extern char datatoc_gpu_shader_material_color_util_glsl[];
@@ -149,13 +148,6 @@ static GPUMaterialLibrary gpu_shader_material_fractal_noise_library = {
     .dependencies = {&gpu_shader_material_noise_library, NULL},
 };
 
-static GPUMaterialLibrary gpu_shader_material_cell_noise_library = {
-    .code = datatoc_gpu_shader_material_cell_noise_glsl,
-    .dependencies = {&gpu_shader_material_math_util_library,
-                     &gpu_shader_material_hash_library,
-                     NULL},
-};
-
 static GPUMaterialLibrary gpu_shader_material_add_shader_library = {
     .code = datatoc_gpu_shader_material_add_shader_glsl,
     .dependencies = {NULL},
@@ -481,7 +473,7 @@ static GPUMaterialLibrary gpu_shader_material_texture_coordinates_library = {
 static GPUMaterialLibrary gpu_shader_material_tex_voronoi_library = {
     .code = datatoc_gpu_shader_material_tex_voronoi_glsl,
     .dependencies = {&gpu_shader_material_math_util_library,
-                     &gpu_shader_material_cell_noise_library,
+                     &gpu_shader_material_hash_library,
                      NULL},
 };
 
@@ -571,7 +563,6 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
     &gpu_shader_material_hash_library,
     &gpu_shader_material_noise_library,
     &gpu_shader_material_fractal_noise_library,
-    &gpu_shader_material_cell_noise_library,
     &gpu_shader_material_add_shader_library,
     &gpu_shader_material_ambient_occlusion_library,
     &gpu_shader_material_glossy_library,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_cell_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_cell_noise.glsl
deleted file mode 100644 (file)
index 881f238..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-float cellnoise(vec3 p)
-{
-  int ix = quick_floor(p.x);
-  int iy = quick_floor(p.y);
-  int iz = quick_floor(p.z);
-
-  return hash_uint3_to_float(uint(ix), uint(iy), uint(iz));
-}
-
-vec3 cellnoise_color(vec3 p)
-{
-  float r = cellnoise(p.xyz);
-  float g = cellnoise(p.yxz);
-  float b = cellnoise(p.yzx);
-
-  return vec3(r, g, b);
-}
index fd9aaf4ae86f2685d103f4cdc50c64b824c79d28..d4f7866b206a5cd8cde3a1bb9b3f30946370d583 100644 (file)
@@ -57,11 +57,37 @@ float floorfrac(float x, out int i)
 
 /* Vector Math */
 
+vec2 safe_divide(vec2 a, vec2 b)
+{
+  return vec2(safe_divide(a.x, b.x), safe_divide(a.y, b.y));
+}
+
 vec3 safe_divide(vec3 a, vec3 b)
 {
   return vec3(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z));
 }
 
+vec4 safe_divide(vec4 a, vec4 b)
+{
+  return vec4(
+      safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z), safe_divide(a.w, b.w));
+}
+
+vec2 safe_divide(vec2 a, float b)
+{
+  return (b != 0.0) ? a / b : vec2(0.0);
+}
+
+vec3 safe_divide(vec3 a, float b)
+{
+  return (b != 0.0) ? a / b : vec3(0.0);
+}
+
+vec4 safe_divide(vec4 a, float b)
+{
+  return (b != 0.0) ? a / b : vec4(0.0);
+}
+
 vec3 c_mod(vec3 a, vec3 b)
 {
   return vec3(c_mod(a.x, b.x), c_mod(a.y, b.y), c_mod(a.z, b.z));
index 018b74fd1a575a6d30b274c7fdc6f9703800dc4f..0d8847176c95f2ba206601e22d62ab638a7c4a28 100644 (file)
-void node_tex_voronoi(vec3 co,
-                      float scale,
-                      float exponent,
-                      float coloring,
-                      float metric,
-                      float feature,
-                      out vec4 color,
-                      out float fac)
-{
-  vec3 p = co * scale;
-  int xx, yy, zz, xi, yi, zi;
-  vec4 da = vec4(1e10);
-  vec3 pa[4] = vec3[4](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
-
-  xi = floor_to_int(p[0]);
-  yi = floor_to_int(p[1]);
-  zi = floor_to_int(p[2]);
-
-  for (xx = xi - 1; xx <= xi + 1; xx++) {
-    for (yy = yi - 1; yy <= yi + 1; yy++) {
-      for (zz = zi - 1; zz <= zi + 1; zz++) {
-        vec3 ip = vec3(xx, yy, zz);
-        vec3 vp = cellnoise_color(ip);
-        vec3 pd = p - (vp + ip);
-
-        float d = 0.0;
-        if (metric == 0.0) { /* SHD_VORONOI_DISTANCE 0 */
-          d = dot(pd, pd);
-        }
-        else if (metric == 1.0) { /* SHD_VORONOI_MANHATTAN 1 */
-          d = abs(pd[0]) + abs(pd[1]) + abs(pd[2]);
-        }
-        else if (metric == 2.0) { /* SHD_VORONOI_CHEBYCHEV 2 */
-          d = max(abs(pd[0]), max(abs(pd[1]), abs(pd[2])));
-        }
-        else if (metric == 3.0) { /* SHD_VORONOI_MINKOWSKI 3 */
-          d = pow(pow(abs(pd[0]), exponent) + pow(abs(pd[1]), exponent) +
-                      pow(abs(pd[2]), exponent),
-                  1.0 / exponent);
-        }
+/*
+ * Smooth Voronoi:
+ *
+ * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
+ *
+ * Distance To Edge:
+ *
+ * - https://www.shadertoy.com/view/llG3zy
+ *
+ */
 
-        vp += vec3(xx, yy, zz);
-        if (d < da[0]) {
-          da.yzw = da.xyz;
-          da[0] = d;
+/* **** 1D Voronoi **** */
 
-          pa[3] = pa[2];
-          pa[2] = pa[1];
-          pa[1] = pa[0];
-          pa[0] = vp;
-        }
-        else if (d < da[1]) {
-          da.zw = da.yz;
-          da[1] = d;
+float voronoi_distance(float a, float b, float metric, float exponent)
+{
+  return distance(a, b);
+}
+
+void node_tex_voronoi_f1_1d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  float scaledCoord = w * scale;
+  float cellPosition = floor(scaledCoord);
+  float localPosition = scaledCoord - cellPosition;
+
+  float minDistance = 8.0;
+  float targetOffset, targetPosition;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+    if (distanceToPoint < minDistance) {
+      targetOffset = cellOffset;
+      minDistance = distanceToPoint;
+      targetPosition = pointPosition;
+    }
+  }
+  outDistance = minDistance;
+  outColor.xyz = hash_float_to_vec3(cellPosition + targetOffset);
+  outW = safe_divide(targetPosition + cellPosition, scale);
+}
+
+void node_tex_voronoi_smooth_f1_1d(vec3 coord,
+                                   float w,
+                                   float scale,
+                                   float smoothness,
+                                   float exponent,
+                                   float randomness,
+                                   float metric,
+                                   out float outDistance,
+                                   out vec4 outColor,
+                                   out vec3 outPosition,
+                                   out float outW,
+                                   out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+  smoothness = clamp(smoothness / 2.0, 0, 0.5);
+
+  float scaledCoord = w * scale;
+  float cellPosition = floor(scaledCoord);
+  float localPosition = scaledCoord - cellPosition;
+
+  float smoothDistance = 8.0;
+  float smoothPosition = 0.0;
+  vec3 smoothColor = vec3(0.0);
+  for (int i = -2; i <= 2; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+    float h = smoothstep(0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+    float correctionFactor = smoothness * h * (1.0 - h);
+    smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+    correctionFactor /= 1.0 + 3.0 * smoothness;
+    vec3 cellColor = hash_float_to_vec3(cellPosition + cellOffset);
+    smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+    smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+  }
+  outDistance = smoothDistance;
+  outColor.xyz = smoothColor;
+  outW = safe_divide(cellPosition + smoothPosition, scale);
+}
+
+void node_tex_voronoi_f2_1d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  float scaledCoord = w * scale;
+  float cellPosition = floor(scaledCoord);
+  float localPosition = scaledCoord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  float offsetF1 = 0.0;
+  float positionF1 = 0.0;
+  float offsetF2, positionF2;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    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;
+    }
+  }
+  outDistance = distanceF2;
+  outColor.xyz = hash_float_to_vec3(cellPosition + offsetF2);
+  outW = safe_divide(positionF2 + cellPosition, scale);
+}
+
+void node_tex_voronoi_distance_to_edge_1d(vec3 coord,
+                                          float w,
+                                          float scale,
+                                          float smoothness,
+                                          float exponent,
+                                          float randomness,
+                                          float metric,
+                                          out float outDistance,
+                                          out vec4 outColor,
+                                          out vec3 outPosition,
+                                          out float outW,
+                                          out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  float scaledCoord = w * scale;
+  float cellPosition = floor(scaledCoord);
+  float localPosition = scaledCoord - cellPosition;
+
+  float minDistance = 8.0;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+    minDistance = min(distanceToPoint, minDistance);
+  }
+  outDistance = minDistance;
+}
 
-          pa[3] = pa[2];
-          pa[2] = pa[1];
-          pa[1] = vp;
+void node_tex_voronoi_n_sphere_radius_1d(vec3 coord,
+                                         float w,
+                                         float scale,
+                                         float smoothness,
+                                         float exponent,
+                                         float randomness,
+                                         float metric,
+                                         out float outDistance,
+                                         out vec4 outColor,
+                                         out vec3 outPosition,
+                                         out float outW,
+                                         out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  float scaledCoord = w * scale;
+  float cellPosition = floor(scaledCoord);
+  float localPosition = scaledCoord - cellPosition;
+
+  float closestPoint;
+  float closestPointOffset;
+  float minDistance = 8.0;
+  for (int i = -1; i <= 1; i++) {
+    float cellOffset = float(i);
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = distance(pointPosition, localPosition);
+    if (distanceToPoint < minDistance) {
+      minDistance = distanceToPoint;
+      closestPoint = pointPosition;
+      closestPointOffset = cellOffset;
+    }
+  }
+
+  minDistance = 8.0;
+  float closestPointToClosestPoint;
+  for (int i = -1; i <= 1; i++) {
+    if (i == 0) {
+      continue;
+    }
+    float cellOffset = float(i) + closestPointOffset;
+    float pointPosition = cellOffset + hash_float_to_float(cellPosition + cellOffset) * randomness;
+    float distanceToPoint = distance(closestPoint, pointPosition);
+    if (distanceToPoint < minDistance) {
+      minDistance = distanceToPoint;
+      closestPointToClosestPoint = pointPosition;
+    }
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
+
+/* **** 2D Voronoi **** */
+
+float voronoi_distance(vec2 a, vec2 b, float metric, float exponent)
+{
+  if (metric == 0.0)  // SHD_VORONOI_EUCLIDEAN
+  {
+    return distance(a, b);
+  }
+  else if (metric == 1.0)  // SHD_VORONOI_MANHATTAN
+  {
+    return abs(a.x - b.x) + abs(a.y - b.y);
+  }
+  else if (metric == 2.0)  // SHD_VORONOI_CHEBYCHEV
+  {
+    return max(abs(a.x - b.x), abs(a.y - b.y));
+  }
+  else if (metric == 3.0)  // SHD_VORONOI_MINKOWSKI
+  {
+    return pow(pow(abs(a.x - b.x), exponent) + pow(abs(a.y - b.y), exponent), 1.0 / exponent);
+  }
+  else {
+    return 0.0;
+  }
+}
+
+void node_tex_voronoi_f1_2d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec2 scaledCoord = coord.xy * scale;
+  vec2 cellPosition = floor(scaledCoord);
+  vec2 localPosition = scaledCoord - cellPosition;
+
+  float minDistance = 8.0;
+  vec2 targetOffset, targetPosition;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vec2 cellOffset = vec2(i, j);
+      vec2 pointPosition = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+      if (distanceToPoint < minDistance) {
+        targetOffset = cellOffset;
+        minDistance = distanceToPoint;
+        targetPosition = pointPosition;
+      }
+    }
+  }
+  outDistance = minDistance;
+  outColor.xyz = hash_vec2_to_vec3(cellPosition + targetOffset);
+  outPosition = vec3(safe_divide(targetPosition + cellPosition, scale), 0.0);
+}
+
+void node_tex_voronoi_smooth_f1_2d(vec3 coord,
+                                   float w,
+                                   float scale,
+                                   float smoothness,
+                                   float exponent,
+                                   float randomness,
+                                   float metric,
+                                   out float outDistance,
+                                   out vec4 outColor,
+                                   out vec3 outPosition,
+                                   out float outW,
+                                   out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+  smoothness = clamp(smoothness / 2.0, 0, 0.5);
+
+  vec2 scaledCoord = coord.xy * scale;
+  vec2 cellPosition = floor(scaledCoord);
+  vec2 localPosition = scaledCoord - cellPosition;
+
+  float smoothDistance = 8.0;
+  vec3 smoothColor = vec3(0.0);
+  vec2 smoothPosition = vec2(0.0);
+  for (int j = -2; j <= 2; j++) {
+    for (int i = -2; i <= 2; i++) {
+      vec2 cellOffset = vec2(i, j);
+      vec2 pointPosition = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+      float h = smoothstep(0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+      float correctionFactor = smoothness * h * (1.0 - h);
+      smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+      correctionFactor /= 1.0 + 3.0 * smoothness;
+      vec3 cellColor = hash_vec2_to_vec3(cellPosition + cellOffset);
+      smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+      smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+    }
+  }
+  outDistance = smoothDistance;
+  outColor.xyz = smoothColor;
+  outPosition = vec3(safe_divide(cellPosition + smoothPosition, scale), 0.0);
+}
+
+void node_tex_voronoi_f2_2d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec2 scaledCoord = coord.xy * scale;
+  vec2 cellPosition = floor(scaledCoord);
+  vec2 localPosition = scaledCoord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  vec2 offsetF1 = vec2(0.0);
+  vec2 positionF1 = vec2(0.0);
+  vec2 offsetF2, positionF2;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vec2 cellOffset = vec2(i, j);
+      vec2 pointPosition = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness;
+      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;
+      }
+    }
+  }
+  outDistance = distanceF2;
+  outColor.xyz = hash_vec2_to_vec3(cellPosition + offsetF2);
+  outPosition = vec3(safe_divide(positionF2 + cellPosition, scale), 0.0);
+}
+
+void node_tex_voronoi_distance_to_edge_2d(vec3 coord,
+                                          float w,
+                                          float scale,
+                                          float smoothness,
+                                          float exponent,
+                                          float randomness,
+                                          float metric,
+                                          out float outDistance,
+                                          out vec4 outColor,
+                                          out vec3 outPosition,
+                                          out float outW,
+                                          out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec2 scaledCoord = coord.xy * scale;
+  vec2 cellPosition = floor(scaledCoord);
+  vec2 localPosition = scaledCoord - cellPosition;
+
+  vec2 vectorToClosest;
+  float minDistance = 8.0;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vec2 cellOffset = vec2(i, j);
+      vec2 vectorToPoint = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness -
+                           localPosition;
+      float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        vectorToClosest = vectorToPoint;
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vec2 cellOffset = vec2(i, j);
+      vec2 vectorToPoint = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness -
+                           localPosition;
+      vec2 perpendicularToEdge = vectorToPoint - vectorToClosest;
+      if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
+        float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
+                                   normalize(perpendicularToEdge));
+        minDistance = min(minDistance, distanceToEdge);
+      }
+    }
+  }
+  outDistance = minDistance;
+}
+
+void node_tex_voronoi_n_sphere_radius_2d(vec3 coord,
+                                         float w,
+                                         float scale,
+                                         float smoothness,
+                                         float exponent,
+                                         float randomness,
+                                         float metric,
+                                         out float outDistance,
+                                         out vec4 outColor,
+                                         out vec3 outPosition,
+                                         out float outW,
+                                         out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec2 scaledCoord = coord.xy * scale;
+  vec2 cellPosition = floor(scaledCoord);
+  vec2 localPosition = scaledCoord - cellPosition;
+
+  vec2 closestPoint;
+  vec2 closestPointOffset;
+  float minDistance = 8.0;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      vec2 cellOffset = vec2(i, j);
+      vec2 pointPosition = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = distance(pointPosition, localPosition);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        closestPoint = pointPosition;
+        closestPointOffset = cellOffset;
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  vec2 closestPointToClosestPoint;
+  for (int j = -1; j <= 1; j++) {
+    for (int i = -1; i <= 1; i++) {
+      if (i == 0 && j == 0) {
+        continue;
+      }
+      vec2 cellOffset = vec2(i, j) + closestPointOffset;
+      vec2 pointPosition = cellOffset + hash_vec2_to_vec2(cellPosition + cellOffset) * randomness;
+      float distanceToPoint = distance(closestPoint, pointPosition);
+      if (distanceToPoint < minDistance) {
+        minDistance = distanceToPoint;
+        closestPointToClosestPoint = pointPosition;
+      }
+    }
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
+
+/* **** 3D Voronoi **** */
+
+float voronoi_distance(vec3 a, vec3 b, float metric, float exponent)
+{
+  if (metric == 0.0)  // SHD_VORONOI_EUCLIDEAN
+  {
+    return distance(a, b);
+  }
+  else if (metric == 1.0)  // SHD_VORONOI_MANHATTAN
+  {
+    return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z);
+  }
+  else if (metric == 2.0)  // SHD_VORONOI_CHEBYCHEV
+  {
+    return max(abs(a.x - b.x), max(abs(a.y - b.y), abs(a.z - b.z)));
+  }
+  else if (metric == 3.0)  // SHD_VORONOI_MINKOWSKI
+  {
+    return pow(pow(abs(a.x - b.x), exponent) + pow(abs(a.y - b.y), exponent) +
+                   pow(abs(a.z - b.z), exponent),
+               1.0 / exponent);
+  }
+  else {
+    return 0.0;
+  }
+}
+
+void node_tex_voronoi_f1_3d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec3 scaledCoord = coord * scale;
+  vec3 cellPosition = floor(scaledCoord);
+  vec3 localPosition = scaledCoord - cellPosition;
+
+  float minDistance = 8.0;
+  vec3 targetOffset, targetPosition;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vec3 cellOffset = vec3(i, j, k);
+        vec3 pointPosition = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+        if (distanceToPoint < minDistance) {
+          targetOffset = cellOffset;
+          minDistance = distanceToPoint;
+          targetPosition = pointPosition;
         }
-        else if (d < da[2]) {
-          da[3] = da[2];
-          da[2] = d;
+      }
+    }
+  }
+  outDistance = minDistance;
+  outColor.xyz = hash_vec3_to_vec3(cellPosition + targetOffset);
+  outPosition = safe_divide(targetPosition + cellPosition, scale);
+}
 
-          pa[3] = pa[2];
-          pa[2] = vp;
+void node_tex_voronoi_smooth_f1_3d(vec3 coord,
+                                   float w,
+                                   float scale,
+                                   float smoothness,
+                                   float exponent,
+                                   float randomness,
+                                   float metric,
+                                   out float outDistance,
+                                   out vec4 outColor,
+                                   out vec3 outPosition,
+                                   out float outW,
+                                   out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+  smoothness = clamp(smoothness / 2.0, 0, 0.5);
+
+  vec3 scaledCoord = coord * scale;
+  vec3 cellPosition = floor(scaledCoord);
+  vec3 localPosition = scaledCoord - cellPosition;
+
+  float smoothDistance = 8.0;
+  vec3 smoothColor = vec3(0.0);
+  vec3 smoothPosition = vec3(0.0);
+  for (int k = -2; k <= 2; k++) {
+    for (int j = -2; j <= 2; j++) {
+      for (int i = -2; i <= 2; i++) {
+        vec3 cellOffset = vec3(i, j, k);
+        vec3 pointPosition = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+        float h = smoothstep(
+            0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+        float correctionFactor = smoothness * h * (1.0 - h);
+        smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+        correctionFactor /= 1.0 + 3.0 * smoothness;
+        vec3 cellColor = hash_vec3_to_vec3(cellPosition + cellOffset);
+        smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+        smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+      }
+    }
+  }
+  outDistance = smoothDistance;
+  outColor.xyz = smoothColor;
+  outPosition = safe_divide(cellPosition + smoothPosition, scale);
+}
+
+void node_tex_voronoi_f2_3d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec3 scaledCoord = coord * scale;
+  vec3 cellPosition = floor(scaledCoord);
+  vec3 localPosition = scaledCoord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  vec3 offsetF1 = vec3(0.0);
+  vec3 positionF1 = vec3(0.0);
+  vec3 offsetF2, positionF2;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vec3 cellOffset = vec3(i, j, k);
+        vec3 pointPosition = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness;
+        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 (d < da[3]) {
-          da[3] = d;
-          pa[3] = vp;
+        else if (distanceToPoint < distanceF2) {
+          distanceF2 = distanceToPoint;
+          offsetF2 = cellOffset;
+          positionF2 = pointPosition;
         }
       }
     }
   }
+  outDistance = distanceF2;
+  outColor.xyz = hash_vec3_to_vec3(cellPosition + offsetF2);
+  outPosition = safe_divide(positionF2 + cellPosition, scale);
+}
 
-  if (coloring == 0.0) {
-    /* Intensity output */
-    if (feature == 0.0) { /* F1 */
-      fac = abs(da[0]);
-    }
-    else if (feature == 1.0) { /* F2 */
-      fac = abs(da[1]);
+void node_tex_voronoi_distance_to_edge_3d(vec3 coord,
+                                          float w,
+                                          float scale,
+                                          float smoothness,
+                                          float exponent,
+                                          float randomness,
+                                          float metric,
+                                          out float outDistance,
+                                          out vec4 outColor,
+                                          out vec3 outPosition,
+                                          out float outW,
+                                          out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec3 scaledCoord = coord * scale;
+  vec3 cellPosition = floor(scaledCoord);
+  vec3 localPosition = scaledCoord - cellPosition;
+
+  vec3 vectorToClosest;
+  float minDistance = 8.0;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vec3 cellOffset = vec3(i, j, k);
+        vec3 vectorToPoint = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness -
+                             localPosition;
+        float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          vectorToClosest = vectorToPoint;
+        }
+      }
     }
-    else if (feature == 2.0) { /* F3 */
-      fac = abs(da[2]);
+  }
+
+  minDistance = 8.0;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vec3 cellOffset = vec3(i, j, k);
+        vec3 vectorToPoint = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness -
+                             localPosition;
+        vec3 perpendicularToEdge = vectorToPoint - vectorToClosest;
+        if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
+          float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
+                                     normalize(perpendicularToEdge));
+          minDistance = min(minDistance, distanceToEdge);
+        }
+      }
     }
-    else if (feature == 3.0) { /* F4 */
-      fac = abs(da[3]);
+  }
+  outDistance = minDistance;
+}
+
+void node_tex_voronoi_n_sphere_radius_3d(vec3 coord,
+                                         float w,
+                                         float scale,
+                                         float smoothness,
+                                         float exponent,
+                                         float randomness,
+                                         float metric,
+                                         out float outDistance,
+                                         out vec4 outColor,
+                                         out vec3 outPosition,
+                                         out float outW,
+                                         out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec3 scaledCoord = coord * scale;
+  vec3 cellPosition = floor(scaledCoord);
+  vec3 localPosition = scaledCoord - cellPosition;
+
+  vec3 closestPoint;
+  vec3 closestPointOffset;
+  float minDistance = 8.0;
+  for (int k = -1; k <= 1; k++) {
+    for (int j = -1; j <= 1; j++) {
+      for (int i = -1; i <= 1; i++) {
+        vec3 cellOffset = vec3(i, j, k);
+        vec3 pointPosition = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = distance(pointPosition, localPosition);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          closestPoint = pointPosition;
+          closestPointOffset = cellOffset;
+        }
+      }
     }
-    else if (feature == 4.0) { /* F2F1 */
-      fac = abs(da[1] - da[0]);
+  }
+
+  minDistance = 8.0;
+  vec3 closestPointToClosestPoint;
+  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;
+        }
+        vec3 cellOffset = vec3(i, j, k) + closestPointOffset;
+        vec3 pointPosition = cellOffset +
+                             hash_vec3_to_vec3(cellPosition + cellOffset) * randomness;
+        float distanceToPoint = distance(closestPoint, pointPosition);
+        if (distanceToPoint < minDistance) {
+          minDistance = distanceToPoint;
+          closestPointToClosestPoint = pointPosition;
+        }
+      }
     }
-    color = vec4(fac, fac, fac, 1.0);
+  }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
+}
+
+/* **** 4D Voronoi **** */
+
+float voronoi_distance(vec4 a, vec4 b, float metric, float exponent)
+{
+  if (metric == 0.0)  // SHD_VORONOI_EUCLIDEAN
+  {
+    return distance(a, b);
+  }
+  else if (metric == 1.0)  // SHD_VORONOI_MANHATTAN
+  {
+    return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) + abs(a.w - b.w);
+  }
+  else if (metric == 2.0)  // SHD_VORONOI_CHEBYCHEV
+  {
+    return max(abs(a.x - b.x), max(abs(a.y - b.y), max(abs(a.z - b.z), abs(a.w - b.w))));
+  }
+  else if (metric == 3.0)  // SHD_VORONOI_MINKOWSKI
+  {
+    return pow(pow(abs(a.x - b.x), exponent) + pow(abs(a.y - b.y), exponent) +
+                   pow(abs(a.z - b.z), exponent) + pow(abs(a.w - b.w), exponent),
+               1.0 / exponent);
   }
   else {
-    /* Color output */
-    vec3 col = vec3(fac, fac, fac);
-    if (feature == 0.0) { /* F1 */
-      col = pa[0];
+    return 0.0;
+  }
+}
+
+void node_tex_voronoi_f1_4d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec4 scaledCoord = vec4(coord, w) * scale;
+  vec4 cellPosition = floor(scaledCoord);
+  vec4 localPosition = scaledCoord - cellPosition;
+
+  float minDistance = 8.0;
+  vec4 targetOffset, targetPosition;
+  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++) {
+          vec4 cellOffset = vec4(i, j, k, u);
+          vec4 pointPosition = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+          if (distanceToPoint < minDistance) {
+            targetOffset = cellOffset;
+            minDistance = distanceToPoint;
+            targetPosition = pointPosition;
+          }
+        }
+      }
     }
-    else if (feature == 1.0) { /* F2 */
-      col = pa[1];
+  }
+  outDistance = minDistance;
+  outColor.xyz = hash_vec4_to_vec3(cellPosition + targetOffset);
+  vec4 p = safe_divide(targetPosition + cellPosition, scale);
+  outPosition = p.xyz;
+  outW = p.w;
+}
+
+void node_tex_voronoi_smooth_f1_4d(vec3 coord,
+                                   float w,
+                                   float scale,
+                                   float smoothness,
+                                   float exponent,
+                                   float randomness,
+                                   float metric,
+                                   out float outDistance,
+                                   out vec4 outColor,
+                                   out vec3 outPosition,
+                                   out float outW,
+                                   out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+  smoothness = clamp(smoothness / 2.0, 0, 0.5);
+
+  vec4 scaledCoord = vec4(coord, w) * scale;
+  vec4 cellPosition = floor(scaledCoord);
+  vec4 localPosition = scaledCoord - cellPosition;
+
+  float smoothDistance = 8.0;
+  vec3 smoothColor = vec3(0.0);
+  vec4 smoothPosition = vec4(0.0);
+  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++) {
+          vec4 cellOffset = vec4(i, j, k, u);
+          vec4 pointPosition = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent);
+          float h = smoothstep(
+              0.0, 1.0, 0.5 + 0.5 * (smoothDistance - distanceToPoint) / smoothness);
+          float correctionFactor = smoothness * h * (1.0 - h);
+          smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
+          correctionFactor /= 1.0 + 3.0 * smoothness;
+          vec3 cellColor = hash_vec4_to_vec3(cellPosition + cellOffset);
+          smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
+          smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
+        }
+      }
     }
-    else if (feature == 2.0) { /* F3 */
-      col = pa[2];
+  }
+  outDistance = smoothDistance;
+  outColor.xyz = smoothColor;
+  vec4 p = safe_divide(cellPosition + smoothPosition, scale);
+  outPosition = p.xyz;
+  outW = p.w;
+}
+
+void node_tex_voronoi_f2_4d(vec3 coord,
+                            float w,
+                            float scale,
+                            float smoothness,
+                            float exponent,
+                            float randomness,
+                            float metric,
+                            out float outDistance,
+                            out vec4 outColor,
+                            out vec3 outPosition,
+                            out float outW,
+                            out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec4 scaledCoord = vec4(coord, w) * scale;
+  vec4 cellPosition = floor(scaledCoord);
+  vec4 localPosition = scaledCoord - cellPosition;
+
+  float distanceF1 = 8.0;
+  float distanceF2 = 8.0;
+  vec4 offsetF1 = vec4(0.0);
+  vec4 positionF1 = vec4(0.0);
+  vec4 offsetF2, positionF2;
+  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++) {
+          vec4 cellOffset = vec4(i, j, k, u);
+          vec4 pointPosition = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness;
+          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;
+          }
+        }
+      }
     }
-    else if (feature == 3.0) { /* F4 */
-      col = pa[3];
+  }
+  outDistance = distanceF2;
+  outColor.xyz = hash_vec4_to_vec3(cellPosition + offsetF2);
+  vec4 p = safe_divide(positionF2 + cellPosition, scale);
+  outPosition = p.xyz;
+  outW = p.w;
+}
+
+void node_tex_voronoi_distance_to_edge_4d(vec3 coord,
+                                          float w,
+                                          float scale,
+                                          float smoothness,
+                                          float exponent,
+                                          float randomness,
+                                          float metric,
+                                          out float outDistance,
+                                          out vec4 outColor,
+                                          out vec3 outPosition,
+                                          out float outW,
+                                          out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec4 scaledCoord = vec4(coord, w) * scale;
+  vec4 cellPosition = floor(scaledCoord);
+  vec4 localPosition = scaledCoord - cellPosition;
+
+  vec4 vectorToClosest;
+  float minDistance = 8.0;
+  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++) {
+          vec4 cellOffset = vec4(i, j, k, u);
+          vec4 vectorToPoint = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness -
+                               localPosition;
+          float distanceToPoint = dot(vectorToPoint, vectorToPoint);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            vectorToClosest = vectorToPoint;
+          }
+        }
+      }
+    }
+  }
+
+  minDistance = 8.0;
+  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++) {
+          vec4 cellOffset = vec4(i, j, k, u);
+          vec4 vectorToPoint = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness -
+                               localPosition;
+          vec4 perpendicularToEdge = vectorToPoint - vectorToClosest;
+          if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001) {
+            float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0,
+                                       normalize(perpendicularToEdge));
+            minDistance = min(minDistance, distanceToEdge);
+          }
+        }
+      }
     }
-    else if (feature == 4.0) { /* F2F1 */
-      col = abs(pa[1] - pa[0]);
+  }
+  outDistance = minDistance;
+}
+
+void node_tex_voronoi_n_sphere_radius_4d(vec3 coord,
+                                         float w,
+                                         float scale,
+                                         float smoothness,
+                                         float exponent,
+                                         float randomness,
+                                         float metric,
+                                         out float outDistance,
+                                         out vec4 outColor,
+                                         out vec3 outPosition,
+                                         out float outW,
+                                         out float outRadius)
+{
+  randomness = clamp(randomness, 0.0, 1.0);
+
+  vec4 scaledCoord = vec4(coord, w) * scale;
+  vec4 cellPosition = floor(scaledCoord);
+  vec4 localPosition = scaledCoord - cellPosition;
+
+  vec4 closestPoint;
+  vec4 closestPointOffset;
+  float minDistance = 8.0;
+  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++) {
+          vec4 cellOffset = vec4(i, j, k, u);
+          vec4 pointPosition = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = distance(pointPosition, localPosition);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            closestPoint = pointPosition;
+            closestPointOffset = cellOffset;
+          }
+        }
+      }
     }
+  }
 
-    color = vec4(cellnoise_color(col), 1.0);
-    fac = (color.x + color.y + color.z) * (1.0 / 3.0);
+  minDistance = 8.0;
+  vec4 closestPointToClosestPoint;
+  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;
+          }
+          vec4 cellOffset = vec4(i, j, k, u) + closestPointOffset;
+          vec4 pointPosition = cellOffset +
+                               hash_vec4_to_vec4(cellPosition + cellOffset) * randomness;
+          float distanceToPoint = distance(closestPoint, pointPosition);
+          if (distanceToPoint < minDistance) {
+            minDistance = distanceToPoint;
+            closestPointToClosestPoint = pointPosition;
+          }
+        }
+      }
+    }
   }
+  outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0;
 }
index b4e12484bdcda8d015fa1e83e413944c50848abd..6b46c5887b43e1e7322997efb22e9df659f0d53a 100644 (file)
@@ -876,10 +876,10 @@ typedef struct NodeTexNoise {
 
 typedef struct NodeTexVoronoi {
   NodeTexBase base;
-  int coloring;
-  int distance;
+  int dimensions;
   int feature;
-  char _pad[4];
+  int distance;
+  int coloring DNA_DEPRECATED;
 } NodeTexVoronoi;
 
 typedef struct NodeTexMusgrave {
@@ -1098,20 +1098,22 @@ typedef struct NodeDenoise {
 #define SHD_NOISE_SOFT 0
 #define SHD_NOISE_HARD 1
 
-/* voronoi texture */
-#define SHD_VORONOI_DISTANCE 0
-#define SHD_VORONOI_MANHATTAN 1
-#define SHD_VORONOI_CHEBYCHEV 2
-#define SHD_VORONOI_MINKOWSKI 3
+/* Voronoi Texture */
 
-#define SHD_VORONOI_INTENSITY 0
-#define SHD_VORONOI_CELLS 1
+enum {
+  SHD_VORONOI_EUCLIDEAN = 0,
+  SHD_VORONOI_MANHATTAN = 1,
+  SHD_VORONOI_CHEBYCHEV = 2,
+  SHD_VORONOI_MINKOWSKI = 3,
+};
 
-#define SHD_VORONOI_F1 0
-#define SHD_VORONOI_F2 1
-#define SHD_VORONOI_F3 2
-#define SHD_VORONOI_F4 3
-#define SHD_VORONOI_F2F1 4
+enum {
+  SHD_VORONOI_F1 = 0,
+  SHD_VORONOI_F2 = 1,
+  SHD_VORONOI_SMOOTH_F1 = 2,
+  SHD_VORONOI_DISTANCE_TO_EDGE = 3,
+  SHD_VORONOI_N_SPHERE_RADIUS = 4,
+};
 
 /* musgrave texture */
 #define SHD_MUSGRAVE_MULTIFRACTAL 0
index c2c937a75f895fc1ceeee2ac4233b63a8c9b65e0..3d90d5663156be451c41af4e28c5b252b0cef9ac 100644 (file)
@@ -4404,25 +4404,39 @@ static void def_sh_tex_musgrave(StructRNA *srna)
 
 static void def_sh_tex_voronoi(StructRNA *srna)
 {
-  static const EnumPropertyItem prop_coloring_items[] = {
-      {SHD_VORONOI_INTENSITY, "INTENSITY", 0, "Intensity", "Only calculate intensity"},
-      {SHD_VORONOI_CELLS, "CELLS", 0, "Cells", "Color cells by position"},
-      {0, NULL, 0, NULL, NULL},
-  };
-
   static EnumPropertyItem prop_distance_items[] = {
-      {SHD_VORONOI_DISTANCE, "DISTANCE", 0, "Distance", "Distance"},
-      {SHD_VORONOI_MANHATTAN, "MANHATTAN", 0, "Manhattan", "Manhattan (city block) distance"},
+      {SHD_VORONOI_EUCLIDEAN, "EUCLIDEAN", 0, "Euclidean", "Euclidean distance"},
+      {SHD_VORONOI_MANHATTAN, "MANHATTAN", 0, "Manhattan", "Manhattan distance"},
       {SHD_VORONOI_CHEBYCHEV, "CHEBYCHEV", 0, "Chebychev", "Chebychev distance"},
       {SHD_VORONOI_MINKOWSKI, "MINKOWSKI", 0, "Minkowski", "Minkowski distance"},
       {0, NULL, 0, NULL, NULL}};
 
   static EnumPropertyItem prop_feature_items[] = {
-      {SHD_VORONOI_F1, "F1", 0, "Closest", "Closest point"},
-      {SHD_VORONOI_F2, "F2", 0, "2nd Closest", "2nd closest point"},
-      {SHD_VORONOI_F3, "F3", 0, "3rd Closest", "3rd closest point"},
-      {SHD_VORONOI_F4, "F4", 0, "4th Closest", "4th closest point"},
-      {SHD_VORONOI_F2F1, "F2F1", 0, "Crackle", "Difference between 2nd and 1st closest point"},
+      {SHD_VORONOI_F1,
+       "F1",
+       0,
+       "F1",
+       "Computes the distance to the closest point as well as its position and color"},
+      {SHD_VORONOI_F2,
+       "F2",
+       0,
+       "F2",
+       "Computes the distance to the second closest point as well as its position and color"},
+      {SHD_VORONOI_SMOOTH_F1,
+       "SMOOTH_F1",
+       0,
+       "Smooth F1",
+       "Smoothed version of F1. Weighted sum of neighbour voronoi cells"},
+      {SHD_VORONOI_DISTANCE_TO_EDGE,
+       "DISTANCE_TO_EDGE",
+       0,
+       "Distance To Edge",
+       "Computes the distance to the edge of the vornoi cell"},
+      {SHD_VORONOI_N_SPHERE_RADIUS,
+       "N_SPHERE_RADIUS",
+       0,
+       "N-Sphere Radius",
+       "Computes the radius of the n-sphere inscribed in the voronoi cell"},
       {0, NULL, 0, NULL, NULL}};
 
   PropertyRNA *prop;
@@ -4430,11 +4444,11 @@ static void def_sh_tex_voronoi(StructRNA *srna)
   RNA_def_struct_sdna_from(srna, "NodeTexVoronoi", "storage");
   def_sh_tex(srna);
 
-  prop = RNA_def_property(srna, "coloring", PROP_ENUM, PROP_NONE);
-  RNA_def_property_enum_sdna(prop, NULL, "coloring");
-  RNA_def_property_enum_items(prop, prop_coloring_items);
-  RNA_def_property_ui_text(prop, "Coloring", "");
-  RNA_def_property_update(prop, 0, "rna_Node_update");
+  prop = RNA_def_property(srna, "voronoi_dimensions", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "dimensions");
+  RNA_def_property_enum_items(prop, rna_enum_node_tex_dimensions_items);
+  RNA_def_property_ui_text(prop, "Dimensions", "");
+  RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update");
 
   prop = RNA_def_property(srna, "distance", PROP_ENUM, PROP_NONE);
   RNA_def_property_enum_sdna(prop, NULL, "distance");
@@ -4446,7 +4460,7 @@ static void def_sh_tex_voronoi(StructRNA *srna)
   RNA_def_property_enum_sdna(prop, NULL, "feature");
   RNA_def_property_enum_items(prop, prop_feature_items);
   RNA_def_property_ui_text(prop, "Feature Output", "");
-  RNA_def_property_update(prop, 0, "rna_Node_update");
+  RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update");
 }
 
 static void def_sh_tex_wave(StructRNA *srna)
index 7000a8a6daeb379cbce6dfd0e1e33e8b53c265dc..adcb93d777591a85b7300d5a8b996a382be21f41 100644 (file)
 
 static bNodeSocketTemplate sh_node_tex_voronoi_in[] = {
     {SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+    {SOCK_FLOAT, 1, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
     {SOCK_FLOAT, 1, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
+    {SOCK_FLOAT, 1, N_("Smoothness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
     {SOCK_FLOAT, 1, N_("Exponent"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 32.0f},
+    {SOCK_FLOAT, 1, N_("Randomness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
     {-1, 0, ""},
 };
 
 static bNodeSocketTemplate sh_node_tex_voronoi_out[] = {
+    {SOCK_FLOAT,
+     0,
+     N_("Distance"),
+     0.0f,
+     0.0f,
+     0.0f,
+     0.0f,
+     0.0f,
+     1.0f,
+     PROP_NONE,
+     SOCK_NO_INTERNAL_LINK},
     {SOCK_RGBA,
      0,
      N_("Color"),
@@ -40,16 +54,28 @@ static bNodeSocketTemplate sh_node_tex_voronoi_out[] = {
      1.0f,
      PROP_NONE,
      SOCK_NO_INTERNAL_LINK},
+    {SOCK_VECTOR,
+     0,
+     N_("Position"),
+     0.0f,
+     0.0f,
+     0.0f,
+     0.0f,
+     0.0f,
+     1.0f,
+     PROP_NONE,
+     SOCK_NO_INTERNAL_LINK},
+    {SOCK_FLOAT, 0, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK},
     {SOCK_FLOAT,
      0,
-     N_("Fac"),
+     N_("Radius"),
      0.0f,
      0.0f,
      0.0f,
      0.0f,
      0.0f,
      1.0f,
-     PROP_FACTOR,
+     PROP_NONE,
      SOCK_NO_INTERNAL_LINK},
     {-1, 0, ""},
 };
@@ -59,8 +85,8 @@ static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
   NodeTexVoronoi *tex = MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi");
   BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
   BKE_texture_colormapping_default(&tex->base.color_mapping);
-  tex->coloring = SHD_VORONOI_INTENSITY;
-  tex->distance = SHD_VORONOI_DISTANCE;
+  tex->dimensions = 3;
+  tex->distance = SHD_VORONOI_EUCLIDEAN;
   tex->feature = SHD_VORONOI_F1;
 
   node->storage = tex;
@@ -75,39 +101,96 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
   node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
   node_shader_gpu_tex_mapping(mat, node, in, out);
 
+  static const char *names[][5] = {
+      [SHD_VORONOI_F1] =
+          {
+              "",
+              "node_tex_voronoi_f1_1d",
+              "node_tex_voronoi_f1_2d",
+              "node_tex_voronoi_f1_3d",
+              "node_tex_voronoi_f1_4d",
+          },
+      [SHD_VORONOI_F2] =
+          {
+              "",
+              "node_tex_voronoi_f2_1d",
+              "node_tex_voronoi_f2_2d",
+              "node_tex_voronoi_f2_3d",
+              "node_tex_voronoi_f2_4d",
+          },
+      [SHD_VORONOI_SMOOTH_F1] =
+          {
+              "",
+              "node_tex_voronoi_smooth_f1_1d",
+              "node_tex_voronoi_smooth_f1_2d",
+              "node_tex_voronoi_smooth_f1_3d",
+              "node_tex_voronoi_smooth_f1_4d",
+          },
+      [SHD_VORONOI_DISTANCE_TO_EDGE] =
+          {
+              "",
+              "node_tex_voronoi_distance_to_edge_1d",
+              "node_tex_voronoi_distance_to_edge_2d",
+              "node_tex_voronoi_distance_to_edge_3d",
+              "node_tex_voronoi_distance_to_edge_4d",
+          },
+      [SHD_VORONOI_N_SPHERE_RADIUS] =
+          {
+              "",
+              "node_tex_voronoi_n_sphere_radius_1d",
+              "node_tex_voronoi_n_sphere_radius_2d",
+              "node_tex_voronoi_n_sphere_radius_3d",
+              "node_tex_voronoi_n_sphere_radius_4d",
+          },
+  };
+
   NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
-  float coloring = tex->coloring;
   float metric = tex->distance;
-  float feature = tex->feature;
-
-  return GPU_stack_link(mat,
-                        node,
-                        "node_tex_voronoi",
-                        in,
-                        out,
-                        GPU_constant(&coloring),
-                        GPU_constant(&metric),
-                        GPU_constant(&feature));
+
+  BLI_assert(tex->feature >= 0 && tex->feature < 5);
+  BLI_assert(tex->dimensions > 0 && tex->dimensions < 5);
+
+  return GPU_stack_link(
+      mat, node, names[tex->feature][tex->dimensions], in, out, GPU_constant(&metric));
 }
 
 static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node)
 {
+  bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector");
+  bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W");
+  bNodeSocket *inSmoothnessSock = nodeFindSocket(node, SOCK_IN, "Smoothness");
+  bNodeSocket *inExponentSock = nodeFindSocket(node, SOCK_IN, "Exponent");
+
+  bNodeSocket *outDistanceSock = nodeFindSocket(node, SOCK_OUT, "Distance");
+  bNodeSocket *outColorSock = nodeFindSocket(node, SOCK_OUT, "Color");
+  bNodeSocket *outPositionSock = nodeFindSocket(node, SOCK_OUT, "Position");
+  bNodeSocket *outWSock = nodeFindSocket(node, SOCK_OUT, "W");
+  bNodeSocket *outRadiusSock = nodeFindSocket(node, SOCK_OUT, "Radius");
+
   NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;
-  bNodeSocket *sock;
-
-  for (sock = node->inputs.first; sock; sock = sock->next) {
-    if (STREQ(sock->name, "Exponent")) {
-      if (tex->distance == SHD_VORONOI_MINKOWSKI) {
-        sock->flag &= ~SOCK_UNAVAIL;
-      }
-      else {
-        sock->flag |= SOCK_UNAVAIL;
-      }
-    }
-  }
+
+  nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4);
+  nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1);
+  nodeSetSocketAvailability(
+      inExponentSock,
+      tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 &&
+          !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS));
+  nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1);
+  nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS);
+  nodeSetSocketAvailability(outColorSock,
+                            tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE &&
+                                tex->feature != SHD_VORONOI_N_SPHERE_RADIUS);
+  nodeSetSocketAvailability(outPositionSock,
+                            tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE &&
+                                tex->feature != SHD_VORONOI_N_SPHERE_RADIUS &&
+                                tex->dimensions != 1);
+  nodeSetSocketAvailability(outWSock,
+                            tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE &&
+                                tex->feature != SHD_VORONOI_N_SPHERE_RADIUS &&
+                                (tex->dimensions == 1 || tex->dimensions == 4));
+  nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS);
 }
 
-/* node type definition */
 void register_node_type_sh_tex_voronoi(void)
 {
   static bNodeType ntype;