diff options
26 files changed, 3779 insertions, 524 deletions
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index db01167cc2a..257c799a3e0 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -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; diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index 3bce47caedb..a73dee1b045 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -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) diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt index a45c43e01ed..38ba113a184 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/shaders/CMakeLists.txt @@ -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 index 00000000000..7affe432bf2 --- /dev/null +++ b/intern/cycles/kernel/shaders/node_hash.h @@ -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 diff --git a/intern/cycles/kernel/shaders/node_voronoi_texture.osl b/intern/cycles/kernel/shaders/node_voronoi_texture.osl index 0d547b4b615..5de4aeef943 100644 --- a/intern/cycles/kernel/shaders/node_voronoi_texture.osl +++ b/intern/cycles/kernel/shaders/node_voronoi_texture.osl @@ -15,158 +15,1013 @@ */ #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!"); } } diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index a192930937f..8de66a04963 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -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); diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/svm_noise.h index 8d832247b18..b67c1e9cb7e 100644 --- a/intern/cycles/kernel/svm/svm_noise.h +++ b/intern/cycles/kernel/svm/svm_noise.h @@ -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 diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index de7114566b3..65e2dacedae 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -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 { diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index 3d7fa523968..4a479d9bc71 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -16,170 +16,1101 @@ 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 diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 01661a6d1e0..1d98fabbc9f 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -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"); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 5d7e074f62b..171568644fb 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -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; }; diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 17bb766445d..53e528de66e 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -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__ diff --git a/intern/cycles/util/util_math_float2.h b/intern/cycles/util/util_math_float2.h index 9feaf042d19..bf21430af3c 100644 --- a/intern/cycles/util/util_math_float2.h +++ b/intern/cycles/util/util_math_float2.h @@ -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__ */ diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/util_math_float3.h index c9a5b34aa58..dd2010715ba 100644 --- a/intern/cycles/util/util_math_float3.h +++ b/intern/cycles/util/util_math_float3.h @@ -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__ diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/util_math_float4.h index 1fb886572e3..bc5322a22bb 100644 --- a/intern/cycles/util/util_math_float4.h +++ b/intern/cycles/util/util_math_float4.h @@ -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__ */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index b9ea85e7a00..89f83c44c11 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -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 diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index 9c00d829e46..52d62725ef8 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -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; + } } diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index c707a1eed92..6055a503b7b 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -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) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index f11dcc9bcf0..aea96dac2e3 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -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) diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index 9c0cf3e6bea..06544d27af9 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -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 index 881f2386cd4..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_cell_noise.glsl +++ /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); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl index fd9aaf4ae86..d4f7866b206 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl @@ -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)); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl index 018b74fd1a5..0d8847176c9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl @@ -1,116 +1,1064 @@ -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; } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index b4e12484bdc..6b46c5887b4 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -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 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index c2c937a75f8..3d90d566315 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -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) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c index 7000a8a6dae..adcb93d7775 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c @@ -23,12 +23,26 @@ 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; |