diff options
Diffstat (limited to 'intern/cycles/kernel/svm/svm_voronoi.h')
-rw-r--r-- | intern/cycles/kernel/svm/svm_voronoi.h | 1226 |
1 files changed, 1083 insertions, 143 deletions
diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index 3d7fa523968..bdfaed8845f 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -16,170 +16,1110 @@ 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 = 0.0f; + float targetPosition = 0.0f; + 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 = 0.0f; + float positionF2 = 0.0f; + 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 = 0.0f; + float closestPointOffset = 0.0f; + 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 = 0.0f; + 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 = make_float2(0.0f, 0.0f); + float2 targetPosition = make_float2(0.0f, 0.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 = 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 = make_float2(0.0f, 0.0f); + float2 positionF2 = make_float2(0.0f, 0.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 = 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 = make_float2(0.0f, 0.0f); + 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 = make_float2(0.0f, 0.0f); + float2 closestPointOffset = make_float2(0.0f, 0.0f); + 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 = make_float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0) { + continue; + } + 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 = make_float3(0.0f, 0.0f, 0.0f); + float3 targetPosition = make_float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float3(0.0f, 0.0f, 0.0f); + float3 positionF2 = make_float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float3(0.0f, 0.0f, 0.0f); + float3 closestPointOffset = make_float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0) { + continue; + } + 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 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 targetPosition = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF2 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 closestPointOffset = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + 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 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0 && u == 0) { + continue; + } + 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 = 0.0f, w_out = 0.0f, radius_out = 0.0f; + float3 color_out = make_float3(0.0f, 0.0f, 0.0f); + float3 position_out = make_float3(0.0f, 0.0f, 0.0f); + + 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 |