Files
test2/intern/cycles/kernel/svm/voronoi.h
Lukas Stockner 11ae08157e Revert Cycles SVM state cleanup due to Mac ARM test timeout
Not sure what is happening here, needs to be checked by someone on Mac.
Let's revert for now, it's not like this is a critical change.

Pull Request: https://projects.blender.org/blender/blender/pulls/110443
2024-10-08 00:33:56 +02:00

1172 lines
42 KiB
C++

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
CCL_NAMESPACE_BEGIN
/*
* SPDX-License-Identifier: MIT
* Original code is copyright (c) 2013 Inigo Quilez.
*
* Smooth Voronoi:
*
* - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi
*
* Distance To Edge based on:
*
* - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
* - https://www.shadertoy.com/view/ldl3W8
*
* With optimization to change -2..2 scan window to -1..1 for better performance,
* as explained in https://www.shadertoy.com/view/llG3zy.
*/
struct VoronoiParams {
float scale;
float detail;
float roughness;
float lacunarity;
float smoothness;
float exponent;
float randomness;
float max_distance;
bool normalize;
NodeVoronoiFeature feature;
NodeVoronoiDistanceMetric metric;
};
struct VoronoiOutput {
float distance = 0.0f;
float3 color = zero_float3();
float4 position = zero_float4();
};
/* ***** Distances ***** */
ccl_device float voronoi_distance(const float a, const float b)
{
return fabsf(b - a);
}
template<typename T>
ccl_device float voronoi_distance(const T a, const T b, ccl_private const VoronoiParams &params)
{
if (params.metric == NODE_VORONOI_EUCLIDEAN) {
return distance(a, b);
}
else if (params.metric == NODE_VORONOI_MANHATTAN) {
return reduce_add(fabs(a - b));
}
else if (params.metric == NODE_VORONOI_CHEBYCHEV) {
return reduce_max(fabs(a - b));
}
else if (params.metric == NODE_VORONOI_MINKOWSKI) {
return powf(reduce_add(power(fabs(a - b), params.exponent)), 1.0f / params.exponent);
}
else {
return 0.0f;
}
}
/* **** 1D Voronoi **** */
ccl_device float4 voronoi_position(const float coord)
{
return make_float4(0.0f, 0.0f, 0.0f, coord);
}
ccl_device VoronoiOutput voronoi_f1(ccl_private const VoronoiParams &params, const float coord)
{
float cellPosition = floorf(coord);
float localPosition = coord - cellPosition;
float minDistance = FLT_MAX;
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) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.color = hash_float_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_smooth_f1(ccl_private const VoronoiParams &params,
const float coord)
{
float cellPosition = floorf(coord);
float localPosition = coord - cellPosition;
float smoothDistance = 0.0f;
float smoothPosition = 0.0f;
float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
float h = -1.0f;
for (int i = -2; i <= 2; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
h = h == -1.0f ?
1.0f :
smoothstep(
0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
float3 cellColor = hash_float_to_float3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
VoronoiOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_f2(ccl_private const VoronoiParams &params, const float coord)
{
float cellPosition = floorf(coord);
float localPosition = coord - cellPosition;
float distanceF1 = FLT_MAX;
float distanceF2 = FLT_MAX;
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) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < distanceF1) {
distanceF2 = distanceF1;
distanceF1 = distanceToPoint;
offsetF2 = offsetF1;
offsetF1 = cellOffset;
positionF2 = positionF1;
positionF1 = pointPosition;
}
else if (distanceToPoint < distanceF2) {
distanceF2 = distanceToPoint;
offsetF2 = cellOffset;
positionF2 = pointPosition;
}
}
VoronoiOutput octave;
octave.distance = distanceF2;
octave.color = hash_float_to_float3(cellPosition + offsetF2);
octave.position = voronoi_position(positionF2 + cellPosition);
return octave;
}
ccl_device float voronoi_distance_to_edge(ccl_private const VoronoiParams &params,
const float coord)
{
float cellPosition = floorf(coord);
float localPosition = coord - cellPosition;
float midPointPosition = hash_float_to_float(cellPosition) * params.randomness;
float leftPointPosition = -1.0f + hash_float_to_float(cellPosition - 1.0f) * params.randomness;
float rightPointPosition = 1.0f + hash_float_to_float(cellPosition + 1.0f) * params.randomness;
float distanceToMidLeft = fabsf((midPointPosition + leftPointPosition) / 2.0f - localPosition);
float distanceToMidRight = fabsf((midPointPosition + rightPointPosition) / 2.0f - localPosition);
return min(distanceToMidLeft, distanceToMidRight);
}
ccl_device float voronoi_n_sphere_radius(ccl_private const VoronoiParams &params,
const float coord)
{
float cellPosition = floorf(coord);
float localPosition = coord - cellPosition;
float closestPoint = 0.0f;
float closestPointOffset = 0.0f;
float minDistance = FLT_MAX;
for (int i = -1; i <= 1; i++) {
float cellOffset = i;
float pointPosition = cellOffset +
hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = fabsf(pointPosition - localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
minDistance = FLT_MAX;
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) * params.randomness;
float distanceToPoint = fabsf(closestPoint - pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
return fabsf(closestPointToClosestPoint - closestPoint) / 2.0f;
}
/* **** 2D Voronoi **** */
ccl_device float4 voronoi_position(const float2 coord)
{
return make_float4(coord.x, coord.y, 0.0f, 0.0f);
}
ccl_device VoronoiOutput voronoi_f1(ccl_private const VoronoiParams &params, const float2 coord)
{
float2 cellPosition = floor(coord);
float2 localPosition = coord - cellPosition;
float minDistance = FLT_MAX;
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) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.color = hash_float2_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_smooth_f1(ccl_private const VoronoiParams &params,
const float2 coord)
{
float2 cellPosition = floor(coord);
float2 localPosition = coord - cellPosition;
float smoothDistance = 0.0f;
float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
float2 smoothPosition = make_float2(0.0f, 0.0f);
float h = -1.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) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
h = h == -1.0f ?
1.0f :
smoothstep(0.0f,
1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
float3 cellColor = hash_float2_to_float3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
VoronoiOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_f2(ccl_private const VoronoiParams &params, const float2 coord)
{
float2 cellPosition = floor(coord);
float2 localPosition = coord - cellPosition;
float distanceF1 = FLT_MAX;
float distanceF2 = FLT_MAX;
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) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
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;
}
}
}
VoronoiOutput octave;
octave.distance = distanceF2;
octave.color = hash_float2_to_float3(cellPosition + offsetF2);
octave.position = voronoi_position(positionF2 + cellPosition);
return octave;
}
ccl_device float voronoi_distance_to_edge(ccl_private const VoronoiParams &params,
const float2 coord)
{
float2 cellPosition = floor(coord);
float2 localPosition = coord - cellPosition;
float2 vectorToClosest = make_float2(0.0f, 0.0f);
float minDistance = FLT_MAX;
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) * params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
minDistance = FLT_MAX;
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) * params.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);
}
}
}
return minDistance;
}
ccl_device float voronoi_n_sphere_radius(ccl_private const VoronoiParams &params,
const float2 coord)
{
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 = FLT_MAX;
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) * params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistance = FLT_MAX;
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) * params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0f;
}
/* **** 3D Voronoi **** */
ccl_device float4 voronoi_position(const float3 coord)
{
return float3_to_float4(coord);
}
ccl_device VoronoiOutput voronoi_f1(ccl_private const VoronoiParams &params, const float3 coord)
{
float3 cellPosition = floor(coord);
float3 localPosition = coord - cellPosition;
float minDistance = FLT_MAX;
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) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.color = hash_float3_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_smooth_f1(ccl_private const VoronoiParams &params,
const float3 coord)
{
float3 cellPosition = floor(coord);
float3 localPosition = coord - cellPosition;
float smoothDistance = 0.0f;
float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
float3 smoothPosition = make_float3(0.0f, 0.0f, 0.0f);
float h = -1.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) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
h = h == -1.0f ?
1.0f :
smoothstep(0.0f,
1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
float3 cellColor = hash_float3_to_float3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
}
VoronoiOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_f2(ccl_private const VoronoiParams &params, const float3 coord)
{
float3 cellPosition = floor(coord);
float3 localPosition = coord - cellPosition;
float distanceF1 = FLT_MAX;
float distanceF2 = FLT_MAX;
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) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
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;
}
}
}
}
VoronoiOutput octave;
octave.distance = distanceF2;
octave.color = hash_float3_to_float3(cellPosition + offsetF2);
octave.position = voronoi_position(positionF2 + cellPosition);
return octave;
}
ccl_device float voronoi_distance_to_edge(ccl_private const VoronoiParams &params,
const float3 coord)
{
float3 cellPosition = floor(coord);
float3 localPosition = coord - cellPosition;
float3 vectorToClosest = make_float3(0.0f, 0.0f, 0.0f);
float minDistance = FLT_MAX;
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) *
params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
minDistance = FLT_MAX;
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) *
params.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);
}
}
}
}
return minDistance;
}
ccl_device float voronoi_n_sphere_radius(ccl_private const VoronoiParams &params,
const float3 coord)
{
float3 cellPosition = floor(coord);
float3 localPosition = coord - cellPosition;
float3 closestPoint = make_float3(0.0f, 0.0f, 0.0f);
float3 closestPointOffset = make_float3(0.0f, 0.0f, 0.0f);
float minDistance = FLT_MAX;
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) *
params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
minDistance = FLT_MAX;
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) *
params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0f;
}
/* **** 4D Voronoi **** */
ccl_device float4 voronoi_position(const float4 coord)
{
return coord;
}
ccl_device VoronoiOutput voronoi_f1(ccl_private const VoronoiParams &params, const float4 coord)
{
float4 cellPosition = floor(coord);
float4 localPosition = coord - cellPosition;
float minDistance = FLT_MAX;
float4 targetOffset = zero_float4();
float4 targetPosition = zero_float4();
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
ccl_loop_no_unroll 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) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.color = hash_float4_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_smooth_f1(ccl_private const VoronoiParams &params,
const float4 coord)
{
float4 cellPosition = floor(coord);
float4 localPosition = coord - cellPosition;
float smoothDistance = 0.0f;
float3 smoothColor = make_float3(0.0f, 0.0f, 0.0f);
float4 smoothPosition = zero_float4();
float h = -1.0f;
for (int u = -2; u <= 2; u++) {
for (int k = -2; k <= 2; k++) {
ccl_loop_no_unroll 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) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
h = h == -1.0f ?
1.0f :
smoothstep(0.0f,
1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) / params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
float3 cellColor = hash_float4_to_float3(cellPosition + cellOffset);
smoothColor = mix(smoothColor, cellColor, h) - correctionFactor;
smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor;
}
}
}
}
VoronoiOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = voronoi_position(cellPosition + smoothPosition);
return octave;
}
ccl_device VoronoiOutput voronoi_f2(ccl_private const VoronoiParams &params, const float4 coord)
{
float4 cellPosition = floor(coord);
float4 localPosition = coord - cellPosition;
float distanceF1 = FLT_MAX;
float distanceF2 = FLT_MAX;
float4 offsetF1 = zero_float4();
float4 positionF1 = zero_float4();
float4 offsetF2 = zero_float4();
float4 positionF2 = zero_float4();
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
ccl_loop_no_unroll 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) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
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;
}
}
}
}
}
VoronoiOutput octave;
octave.distance = distanceF2;
octave.color = hash_float4_to_float3(cellPosition + offsetF2);
octave.position = voronoi_position(positionF2 + cellPosition);
return octave;
}
ccl_device float voronoi_distance_to_edge(ccl_private const VoronoiParams &params,
const float4 coord)
{
float4 cellPosition = floor(coord);
float4 localPosition = coord - cellPosition;
float4 vectorToClosest = zero_float4();
float minDistance = FLT_MAX;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
ccl_loop_no_unroll 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) *
params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
}
minDistance = FLT_MAX;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
ccl_loop_no_unroll 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) *
params.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);
}
}
}
}
}
return minDistance;
}
ccl_device float voronoi_n_sphere_radius(ccl_private const VoronoiParams &params,
const float4 coord)
{
float4 cellPosition = floor(coord);
float4 localPosition = coord - cellPosition;
float4 closestPoint = zero_float4();
float4 closestPointOffset = zero_float4();
float minDistance = FLT_MAX;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
ccl_loop_no_unroll 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) *
params.randomness;
float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
}
minDistance = FLT_MAX;
float4 closestPointToClosestPoint = zero_float4();
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
ccl_loop_no_unroll 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) *
params.randomness;
float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0f;
}
/* **** Fractal Voronoi **** */
/* The fractalization logic is the same as for fBM Noise, except that some additions are replaced
* by lerps. */
template<typename T>
ccl_device VoronoiOutput fractal_voronoi_x_fx(ccl_private const VoronoiParams &params,
const T coord)
{
float amplitude = 1.0f;
float max_amplitude = 0.0f;
float scale = 1.0f;
VoronoiOutput output;
const bool zero_input = params.detail == 0.0f || params.roughness == 0.0f;
for (int i = 0; i <= ceilf(params.detail); ++i) {
VoronoiOutput octave = (params.feature == NODE_VORONOI_F2) ?
voronoi_f2(params, coord * scale) :
(params.feature == NODE_VORONOI_SMOOTH_F1 &&
params.smoothness != 0.0f) ?
voronoi_smooth_f1(params, coord * scale) :
voronoi_f1(params, coord * scale);
if (zero_input) {
max_amplitude = 1.0f;
output = octave;
break;
}
else if (i <= params.detail) {
max_amplitude += amplitude;
output.distance += octave.distance * amplitude;
output.color += octave.color * amplitude;
output.position = mix(output.position, octave.position / scale, amplitude);
scale *= params.lacunarity;
amplitude *= params.roughness;
}
else {
float remainder = params.detail - floorf(params.detail);
if (remainder != 0.0f) {
max_amplitude = mix(max_amplitude, max_amplitude + amplitude, remainder);
output.distance = mix(
output.distance, output.distance + octave.distance * amplitude, remainder);
output.color = mix(output.color, output.color + octave.color * amplitude, remainder);
output.position = mix(
output.position, mix(output.position, octave.position / scale, amplitude), remainder);
}
}
}
if (params.normalize) {
output.distance /= max_amplitude * params.max_distance;
output.color /= max_amplitude;
}
output.position = safe_divide(output.position, params.scale);
return output;
}
/* The fractalization logic is the same as for fBM Noise, except that some additions are replaced
* by lerps. */
template<typename T>
ccl_device float fractal_voronoi_distance_to_edge(ccl_private const VoronoiParams &params,
const T coord)
{
float amplitude = 1.0f;
float max_amplitude = params.max_distance;
float scale = 1.0f;
float distance = 8.0f;
const bool zero_input = params.detail == 0.0f || params.roughness == 0.0f;
for (int i = 0; i <= ceilf(params.detail); ++i) {
const float octave_distance = voronoi_distance_to_edge(params, coord * scale);
if (zero_input) {
distance = octave_distance;
break;
}
else if (i <= params.detail) {
max_amplitude = mix(max_amplitude, params.max_distance / scale, amplitude);
distance = mix(distance, min(distance, octave_distance / scale), amplitude);
scale *= params.lacunarity;
amplitude *= params.roughness;
}
else {
float remainder = params.detail - floorf(params.detail);
if (remainder != 0.0f) {
float lerp_amplitude = mix(max_amplitude, params.max_distance / scale, amplitude);
max_amplitude = mix(max_amplitude, lerp_amplitude, remainder);
float lerp_distance = mix(distance, min(distance, octave_distance / scale), amplitude);
distance = mix(distance, min(distance, lerp_distance), remainder);
}
}
}
if (params.normalize) {
distance /= max_amplitude;
}
return distance;
}
ccl_device void svm_voronoi_output(const uint4 stack_offsets,
ccl_private float *stack,
const float distance,
const float3 color,
const float3 position,
const float w,
const float radius)
{
uint distance_stack_offset, color_stack_offset, position_stack_offset;
uint w_out_stack_offset, radius_stack_offset, unused;
svm_unpack_node_uchar4(
stack_offsets.z, &unused, &unused, &distance_stack_offset, &color_stack_offset);
svm_unpack_node_uchar3(
stack_offsets.w, &position_stack_offset, &w_out_stack_offset, &radius_stack_offset);
if (stack_valid(distance_stack_offset))
stack_store_float(stack, distance_stack_offset, distance);
if (stack_valid(color_stack_offset))
stack_store_float3(stack, color_stack_offset, color);
if (stack_valid(position_stack_offset))
stack_store_float3(stack, position_stack_offset, position);
if (stack_valid(w_out_stack_offset))
stack_store_float(stack, w_out_stack_offset, w);
if (stack_valid(radius_stack_offset))
stack_store_float(stack, radius_stack_offset, radius);
}
template<uint node_feature_mask>
ccl_device_noinline int svm_node_tex_voronoi(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
uint dimensions,
uint feature,
uint metric,
int offset)
{
/* Read node defaults and stack offsets. */
uint4 stack_offsets = read_node(kg, &offset);
uint4 defaults1 = read_node(kg, &offset);
uint4 defaults2 = read_node(kg, &offset);
uint coord_stack_offset, w_stack_offset, scale_stack_offset, detail_stack_offset;
uint roughness_stack_offset, lacunarity_stack_offset, smoothness_stack_offset,
exponent_stack_offset;
uint randomness_stack_offset, normalize;
svm_unpack_node_uchar4(stack_offsets.x,
&coord_stack_offset,
&w_stack_offset,
&scale_stack_offset,
&detail_stack_offset);
svm_unpack_node_uchar4(stack_offsets.y,
&roughness_stack_offset,
&lacunarity_stack_offset,
&smoothness_stack_offset,
&exponent_stack_offset);
svm_unpack_node_uchar2(stack_offsets.z, &randomness_stack_offset, &normalize);
/* Read from stack. */
float3 coord = stack_load_float3(stack, coord_stack_offset);
float w = stack_load_float_default(stack, w_stack_offset, defaults1.x);
VoronoiParams params;
params.feature = (NodeVoronoiFeature)feature;
params.metric = (NodeVoronoiDistanceMetric)metric;
params.scale = stack_load_float_default(stack, scale_stack_offset, defaults1.y);
params.detail = stack_load_float_default(stack, detail_stack_offset, defaults1.z);
params.roughness = stack_load_float_default(stack, roughness_stack_offset, defaults1.w);
params.lacunarity = stack_load_float_default(stack, lacunarity_stack_offset, defaults2.x);
params.smoothness = stack_load_float_default(stack, smoothness_stack_offset, defaults2.y);
params.exponent = stack_load_float_default(stack, exponent_stack_offset, defaults2.z);
params.randomness = stack_load_float_default(stack, randomness_stack_offset, defaults2.w);
params.max_distance = 0.0f;
params.normalize = normalize;
params.detail = clamp(params.detail, 0.0f, 15.0f);
params.roughness = clamp(params.roughness, 0.0f, 1.0f);
params.randomness = clamp(params.randomness, 0.0f, 1.0f);
params.smoothness = clamp(params.smoothness / 2.0f, 0.0f, 0.5f);
coord *= params.scale;
w *= params.scale;
/* Compute output, specialized for each dimension. */
switch (params.feature) {
case NODE_VORONOI_DISTANCE_TO_EDGE: {
float distance = 0.0f;
params.max_distance = 0.5f + 0.5f * params.randomness;
switch (dimensions) {
case 1:
distance = fractal_voronoi_distance_to_edge(params, w);
break;
case 2:
distance = fractal_voronoi_distance_to_edge(params, float3_to_float2(coord));
break;
case 3:
distance = fractal_voronoi_distance_to_edge(params, coord);
break;
case 4:
distance = fractal_voronoi_distance_to_edge(params, float3_to_float4(coord, w));
break;
}
svm_voronoi_output(stack_offsets, stack, distance, zero_float3(), zero_float3(), 0.0f, 0.0f);
break;
}
case NODE_VORONOI_N_SPHERE_RADIUS: {
float radius = 0.0f;
switch (dimensions) {
case 1:
radius = voronoi_n_sphere_radius(params, w);
break;
case 2:
radius = voronoi_n_sphere_radius(params, float3_to_float2(coord));
break;
case 3:
radius = voronoi_n_sphere_radius(params, coord);
break;
case 4:
radius = voronoi_n_sphere_radius(params, float3_to_float4(coord, w));
break;
}
svm_voronoi_output(stack_offsets, stack, 0.0f, zero_float3(), zero_float3(), 0.0f, radius);
break;
}
default: {
VoronoiOutput output;
switch (dimensions) {
case 1:
params.max_distance = (0.5f + 0.5f * params.randomness) *
((params.feature == NODE_VORONOI_F2) ? 2.0f : 1.0f);
output = fractal_voronoi_x_fx(params, w);
break;
case 2:
IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA)
{
params.max_distance = voronoi_distance(zero_float2(),
make_float2(0.5f + 0.5f * params.randomness,
0.5f + 0.5f * params.randomness),
params) *
((params.feature == NODE_VORONOI_F2) ? 2.0f : 1.0f);
output = fractal_voronoi_x_fx(params, float3_to_float2(coord));
}
break;
case 3:
IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA)
{
params.max_distance = voronoi_distance(zero_float3(),
make_float3(0.5f + 0.5f * params.randomness,
0.5f + 0.5f * params.randomness,
0.5f + 0.5f * params.randomness),
params) *
((params.feature == NODE_VORONOI_F2) ? 2.0f : 1.0f);
output = fractal_voronoi_x_fx(params, coord);
}
break;
case 4:
IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA)
{
params.max_distance = voronoi_distance(zero_float4(),
make_float4(0.5f + 0.5f * params.randomness,
0.5f + 0.5f * params.randomness,
0.5f + 0.5f * params.randomness,
0.5f + 0.5f * params.randomness),
params) *
((params.feature == NODE_VORONOI_F2) ? 2.0f : 1.0f);
output = fractal_voronoi_x_fx(params, float3_to_float4(coord, w));
}
break;
}
svm_voronoi_output(stack_offsets,
stack,
output.distance,
output.color,
float4_to_float3(output.position),
output.position.w,
0.0f);
break;
}
}
return offset;
}
CCL_NAMESPACE_END