
This patch allows the Voronoi node to operate in 1D, 2D, and 4D space. It also adds a Randomness input to control the randomness of the texture. Additionally, it adds three new modes of operation: - Smooth F1: A smooth version of F1 Voronoi with no discontinuities. - Distance To Edge: Returns the distance to the edges of the cells. - N-Sphere Radius: Returns the radius of the n-sphere inscribed in the cells. In other words, it is half the distance between the closest feature point and the feature point closest to it. And it removes the following three modes of operation: - F3. - F4. - Cracks. The Distance metric is now called Euclidean, and it computes the actual euclidean distance as opposed to the old method of computing the squared euclidean distance. This breaks backward compatibility in many ways, including the base case. Reviewers: brecht, JacquesLucke Differential Revision: https://developer.blender.org/D5743
1028 lines
35 KiB
Plaintext
1028 lines
35 KiB
Plaintext
/*
|
|
* Copyright 2011-2013 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "stdosl.h"
|
|
#include "vector2.h"
|
|
#include "vector4.h"
|
|
#include "node_hash.h"
|
|
|
|
#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)
|
|
{
|
|
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
|
|
*
|
|
*/
|
|
|
|
/* **** 1D Voronoi **** */
|
|
|
|
float voronoi_distance(float a, float b, string metric, float exponent)
|
|
{
|
|
return abs(a - b);
|
|
}
|
|
|
|
void voronoi_f1_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 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;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 dimensions = "3D",
|
|
string feature = "f1",
|
|
string metric = "euclidean",
|
|
vector3 Vector = P,
|
|
float WIn = 0.0,
|
|
float Scale = 5.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)
|
|
{
|
|
float randomness = clamp(Randomness, 0.0, 1.0);
|
|
float smoothness = clamp(Smoothness / 2.0, 0.0, 0.5);
|
|
|
|
vector3 coord = Vector;
|
|
if (use_mapping)
|
|
coord = transform(mapping, coord);
|
|
|
|
float w = WIn * Scale;
|
|
coord *= Scale;
|
|
|
|
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") {
|
|
voronoi_f2_1d(w, Exponent, randomness, metric, Distance, Color, WOut);
|
|
}
|
|
else if (feature == "distance_to_edge") {
|
|
voronoi_distance_to_edge_1d(w, randomness, Distance);
|
|
}
|
|
else if (feature == "n_sphere_radius") {
|
|
voronoi_n_sphere_radius_1d(w, randomness, Radius);
|
|
}
|
|
else {
|
|
error("Unknown feature!");
|
|
}
|
|
WOut = (Scale != 0.0) ? WOut / Scale : 0.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 == "smooth_f1") {
|
|
voronoi_smooth_f1_2d(
|
|
coord2D, smoothness, Exponent, randomness, metric, Distance, Color, outPosition2D);
|
|
}
|
|
else if (feature == "f2") {
|
|
voronoi_f2_2d(coord2D, Exponent, randomness, metric, Distance, Color, outPosition2D);
|
|
}
|
|
else if (feature == "distance_to_edge") {
|
|
voronoi_distance_to_edge_2d(coord2D, randomness, Distance);
|
|
}
|
|
else if (feature == "n_sphere_radius") {
|
|
voronoi_n_sphere_radius_2d(coord2D, randomness, Radius);
|
|
}
|
|
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!");
|
|
}
|
|
}
|