Node: Gabor Noise Texture #110802

Open
Charlie Jolly wants to merge 68 commits from CharlieJolly/blender:gabor into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
10 changed files with 170 additions and 457 deletions
Showing only changes of commit 019eb3d42f - Show all commits

View File

@ -944,7 +944,6 @@ static ShaderNode *add_node(Scene *scene,
BL::ShaderNodeTexGabor b_gabor_node(b_node);
GaborTextureNode *gabor = graph->create_node<GaborTextureNode>();
gabor->set_mode((NodeGaborMode)b_gabor_node.mode());
gabor->set_anisotropic((NodeGaborAnisotropic)b_gabor_node.anisotropic());
gabor->set_dimensions(b_gabor_node.gabor_dimensions());
gabor->set_periodic(b_gabor_node.periodic());
gabor->set_use_normalize(b_gabor_node.normalize());

View File

@ -23,7 +23,7 @@
#define vector3 point
#define GABOR_SEED 1259
#define GABOR_SEED 11939
struct GaborParams {
float base_frequency;
@ -34,10 +34,10 @@ struct GaborParams {
float phase_variance;
float rotation;
float rot_variance;
float dir_randomness;
float tilt_randomness;
float cell_randomness;
float anistropy;
string mode;
string anisotropic;
vector direction;
};
@ -123,25 +123,15 @@ vector gabor_sample(GaborParams gp, vector3 cell, int seed, output float phi)
float pvar = mix(0.0, rand_values.z, gp.phase_variance);
phi = M_2PI * pvar + gp.phase;
if (gp.anisotropic == "aniso") { /* ANISO */
return normalize(gp.direction + rand_values * gp.dir_randomness);
}
else if (gp.anisotropic == "hybrid") {
float ovar = M_PI * (rand_values.x) * gp.rot_variance;
float omega_t = ovar - gp.rotation;
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return normalize(gp.direction * point(cos_omega_t, sin_omega_t, gp.dir_randomness));
}
else { /* ISO */
float ovar = M_PI * (rand_values.x) * gp.rot_variance;
float omega_t = ovar - gp.rotation;
float cos_omega_p = clamp(rand_values.y * gp.dir_randomness, -1.0, 1.0);
float sin_omega_p = sqrt(1.0 - cos_omega_p * cos_omega_p);
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return normalize(vector(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
}
float ovar = M_PI * (rand_values.x) * gp.rot_variance;
float omega_t = ovar - gp.rotation;
float cos_omega_p = clamp(rand_values.y * gp.tilt_randomness, -1.0, 1.0);
float sin_omega_p = sqrt(1.0 - cos_omega_p * cos_omega_p);
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return mix(normalize(vector(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p)),
normalize(gp.direction),
gp.anistropy);
}
vector3 gabor_cell_3d(output GaborParams gp, point cell, point cell_position)
@ -286,21 +276,21 @@ GaborParams gabor_parameters(vector direction,
float phase_variance,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float cell_randomness,
string mode,
string anisotropic)
float anistropy,
string mode)
{
GaborParams gp;
gp.impulses = clamp(impulses, 0.0001, 32.0);
gp.rot_variance = rot_variance;
gp.anisotropic = anisotropic;
gp.anistropy = anistropy;
gp.mode = mode;
gp.direction = direction;
gp.phase = phase;
gp.rotation = rotation;
gp.phase_variance = phase_variance;
gp.dir_randomness = dir_randomness;
gp.tilt_randomness = tilt_randomness;
gp.cell_randomness = cell_randomness;
gp.gain = gain;
gp.radius = radius;
@ -367,11 +357,11 @@ float gabor_noise(point p,
float phase_variance,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float cell_randomness,
float anistropy,
string dimensions,
string mode,
string anisotropic,
int use_normalize,
int periodic,
int use_origin_offset)
@ -399,10 +389,10 @@ float gabor_noise(point p,
phase_variance,
rotation,
rot_variance,
dir_randomness,
tilt_randomness,
cell_randomness,
mode,
anisotropic);
anistropy,
mode);
float g = gabor_fractal_noise(fp, gp, p, scale, dimensions, periodic, use_origin_offset);
@ -428,7 +418,6 @@ shader node_gabor_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 = "2D",
string mode = "gabor",
string anisotropic = "iso",
int use_normalize = 1,
int periodic = 0,
int use_origin_offset = 1,
@ -445,11 +434,12 @@ shader node_gabor_texture(int use_mapping = 0,
float Impulses = 2.0,
float Phase = 0.0,
float PhaseVariance = 1.0,
float CellRandomness = 1.0,
float Rotation = 0.0,
float RotationVariance = 0.0,
float DirectionRandomness = 1.0,
vector3 Direction = vector(0, 0, 1),
float CellRandomness = 1.0,
float AnistropicFactor = 0.0,
output float Value = 0.0)
{
vector3 p = Vector;
@ -475,9 +465,9 @@ shader node_gabor_texture(int use_mapping = 0,
RotationVariance,
DirectionRandomness,
CellRandomness,
AnistropicFactor,
dimensions,
mode,
anisotropic,
use_normalize,
periodic,
use_origin_offset);

View File

@ -17,7 +17,7 @@ CCL_NAMESPACE_BEGIN
/* See GLSL implementation for code comments. */
#define GABOR_SEED 1259
#define GABOR_SEED 11939
typedef struct GaborParams {
float base_frequency;
@ -28,10 +28,10 @@ typedef struct GaborParams {
float phase_variance;
float rotation;
float rot_variance;
float dir_randomness;
float tilt_randomness;
float cell_randomness;
float anistropy;
int mode;
int anisotropic;
float3 direction;
} GaborParams;
@ -114,26 +114,16 @@ ccl_device float3 gabor_sample(GaborParams gp, float3 cell, int seed, ccl_privat
float pvar = mix(0.0f, rand_values.z, gp.phase_variance);
*phi = M_2PI_F * pvar + gp.phase;
if (gp.anisotropic == 1) { /* ANISO */
return normalize(gp.direction + rand_values * gp.dir_randomness);
}
else if (gp.anisotropic == 2) { /* HYBRID */
float ovar = M_PI_F * (rand_values.x);
float omega_t = ovar * gp.rot_variance - gp.rotation;
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return normalize(gp.direction * make_float3(cos_omega_t, sin_omega_t, gp.dir_randomness));
}
else { /* ISO */
float ovar = M_PI_F * (rand_values.x);
float omega_t = ovar * gp.rot_variance - gp.rotation;
float cos_omega_p = clamp(rand_values.y * gp.dir_randomness, -1.0f, 1.0f);
float sin_omega_p = sqrtf(1.0f - cos_omega_p * cos_omega_p);
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return normalize(
make_float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
}
float ovar = M_PI_F * (rand_values.x);
float omega_t = ovar * gp.rot_variance - gp.rotation;
float cos_omega_p = clamp(rand_values.y * gp.tilt_randomness, -1.0f, 1.0f);
float sin_omega_p = sqrtf(1.0f - cos_omega_p * cos_omega_p);
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return mix(
normalize(make_float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p)),
normalize(gp.direction),
gp.anistropy);
}
ccl_device float3 gabor_cell_3d(GaborParams gp, float3 cell, float3 cell_position)
@ -319,21 +309,21 @@ ccl_device GaborParams gabor_parameters(float3 direction,
float phase_variance,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float cell_randomness,
int mode,
int anisotropic)
float anistropy,
int mode)
{
GaborParams gp;
gp.impulses = clamp(impulses, 0.0001f, 32.0f);
gp.rot_variance = rot_variance;
gp.anisotropic = anisotropic;
gp.anistropy = anistropy;
gp.mode = mode;
gp.direction = direction;
gp.phase = phase;
gp.rotation = rotation;
gp.phase_variance = phase_variance;
gp.dir_randomness = dir_randomness;
gp.tilt_randomness = tilt_randomness;
gp.cell_randomness = cell_randomness;
gp.gain = gain;
gp.radius = radius;
@ -359,11 +349,11 @@ ccl_device float gabor_noise(float3 p,
float phase_variance,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float cell_randomness,
float anistropy,
int dimensions,
int mode,
int anisotropic,
int normalize,
int periodic,
int use_origin_offset)
@ -388,10 +378,10 @@ ccl_device float gabor_noise(float3 p,
phase_variance,
rotation,
rot_variance,
dir_randomness,
tilt_randomness,
cell_randomness,
mode,
anisotropic);
anistropy,
mode);
float g = gabor_fractal_noise(fp, gp, p, scale, dimensions, periodic, use_origin_offset);
@ -416,13 +406,14 @@ ccl_device_noinline int svm_node_tex_gabor(
uint4 defaults_node4 = read_node(kg, &offset);
uint4 defaults_node5 = read_node(kg, &offset);
uint4 defaults_node6 = read_node(kg, &offset);
uint4 defaults_node7 = read_node(kg, &offset);
/* Input and Output Sockets */
uint vector_in_offset, scale_offset, detail_offset, phase_offset, impulse_offset;
uint direction_offset, value_out_offset, fre_lacunarity_offset;
uint mode_offset, aniso_offset, periodic_offset, roughness_offset;
uint rot_variance_offset, phase_variance_offset, rotation_offset, gain_offset,
dir_randomness_offset, cell_randomness_offset;
tilt_randomness_offset, cell_randomness_offset;
uint scl_lacunarity_offset, use_normalize_offset, dimension_offset, rot_lacunarity_offset;
uint base_frequency_offset, radius_offset;
@ -439,7 +430,7 @@ ccl_device_noinline int svm_node_tex_gabor(
&phase_variance_offset,
&cell_randomness_offset,
&rotation_offset,
&dir_randomness_offset);
&tilt_randomness_offset);
svm_unpack_node_uchar4(
node2.y, &rot_variance_offset, &direction_offset, &value_out_offset, &dimension_offset);
svm_unpack_node_uchar4(
@ -466,7 +457,9 @@ ccl_device_noinline int svm_node_tex_gabor(
stack, cell_randomness_offset, defaults_node6.x);
float rotation = stack_load_float_default(stack, rotation_offset, defaults_node6.y);
float rot_variance = stack_load_float_default(stack, rot_variance_offset, defaults_node6.z);
float dir_randomness = stack_load_float_default(stack, dir_randomness_offset, defaults_node6.w);
float tilt_randomness = stack_load_float_default(
stack, tilt_randomness_offset, defaults_node6.w);
float anistropy = stack_load_float_default(stack, aniso_offset, defaults_node7.x);
float3 direction = stack_load_float3(stack, direction_offset);
@ -487,11 +480,11 @@ ccl_device_noinline int svm_node_tex_gabor(
phase_variance,
rotation,
rot_variance,
dir_randomness,
tilt_randomness,
cell_randomness,
anistropy,
dimension_offset,
mode_offset,
aniso_offset,
use_normalize_offset,
periodic_offset,
int(node2.w));

View File

@ -420,12 +420,6 @@ typedef enum NodeGaborMode {
SHD_GABOR_MODE_PHASOR_SQUARE,
} NodeGaborMode;
typedef enum NodeGaborAnisotropic {
SHD_GABOR_ISOTROPIC,
SHD_GABOR_ANISOTROPIC,
SHD_GABOR_HYBRID,
} NodeGaborAnisotropic;
/* Closure */
typedef enum ClosureType {

View File

@ -1219,12 +1219,6 @@ NODE_DEFINE(GaborTextureNode)
dimensions_enum.insert("3D", 3);
SOCKET_ENUM(dimensions, "Dimensions", dimensions_enum, 2);
static NodeEnum aniso_enum;
aniso_enum.insert("iso", SHD_GABOR_ISOTROPIC);
aniso_enum.insert("aniso", SHD_GABOR_ANISOTROPIC);
aniso_enum.insert("hybrid", SHD_GABOR_HYBRID);
SOCKET_ENUM(anisotropic, "Anisotropic", aniso_enum, SHD_GABOR_ISOTROPIC);
static NodeEnum mode_enum;
mode_enum.insert("gabor", SHD_GABOR_MODE_GABOR);
mode_enum.insert("gabor_ring", SHD_GABOR_MODE_RING);
@ -1255,9 +1249,10 @@ NODE_DEFINE(GaborTextureNode)
SOCKET_IN_FLOAT(phase_variance, "Phase Variance", 1.0f);
SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f);
SOCKET_IN_FLOAT(rot_variance, "Rotation Variance", 0.0f);
SOCKET_IN_FLOAT(dir_randomness, "Direction Randomness", 1.0f);
SOCKET_IN_FLOAT(tilt_randomness, "Tilt Randomness", 1.0f);
SOCKET_IN_POINT(direction, "Direction", make_float3(0.0f, 0.0f, 1.0f));
SOCKET_IN_FLOAT(cell_randomness, "Cell Randomness", 1.0f);
SOCKET_IN_FLOAT(anistropy, "Anistropic Factor", 1.0f);
SOCKET_OUT_FLOAT(value, "Value");
@ -1297,9 +1292,10 @@ void GaborTextureNode::compile(SVMCompiler &compiler)
ShaderInput *phase_variance_in = input("Phase Variance");
ShaderInput *rotation_in = input("Rotation");
ShaderInput *rot_variance_in = input("Rotation Variance");
ShaderInput *dir_randomness_in = input("Direction Randomness");
ShaderInput *tilt_randomness_in = input("Tilt Randomness");
ShaderInput *direction_in = input("Direction");
ShaderInput *cell_randomness_in = input("Cell Randomness");
ShaderInput *anistropy_in = input("Anistropic Factor");
ShaderOutput *value_out = output("Value");
@ -1319,8 +1315,9 @@ void GaborTextureNode::compile(SVMCompiler &compiler)
int phase_variance_in_stack_offset = compiler.stack_assign(phase_variance_in);
int rotation_in_stack_offset = compiler.stack_assign(rotation_in);
int rot_variance_in_stack_offset = compiler.stack_assign(rot_variance_in);
int dir_randomness_in_stack_offset = compiler.stack_assign(dir_randomness_in);
int tilt_randomness_in_stack_offset = compiler.stack_assign(tilt_randomness_in);
int cell_randomness_in_stack_offset = compiler.stack_assign(cell_randomness_in);
int anistropy_in_stack_offset = compiler.stack_assign(anistropy_in);
int direction_in_stack_offset = compiler.stack_assign(direction_in);
int value_out_stack_offset = compiler.stack_assign_if_linked(value_out);
@ -1338,16 +1335,17 @@ void GaborTextureNode::compile(SVMCompiler &compiler)
radius_in_stack_offset,
impulses_in_stack_offset,
phase_in_stack_offset));
compiler.add_node(compiler.encode_uchar4(phase_variance_in_stack_offset,
cell_randomness_in_stack_offset,
rotation_in_stack_offset,
dir_randomness_in_stack_offset),
compiler.encode_uchar4(rot_variance_in_stack_offset,
direction_in_stack_offset,
value_out_stack_offset,
dimensions),
compiler.encode_uchar4(mode, anisotropic, periodic, use_normalize),
use_origin_offset);
compiler.add_node(
compiler.encode_uchar4(phase_variance_in_stack_offset,
cell_randomness_in_stack_offset,
rotation_in_stack_offset,
tilt_randomness_in_stack_offset),
compiler.encode_uchar4(rot_variance_in_stack_offset,
direction_in_stack_offset,
value_out_stack_offset,
dimensions),
compiler.encode_uchar4(mode, anistropy_in_stack_offset, periodic, use_normalize),
use_origin_offset);
compiler.add_node(__float_as_int(scale),
__float_as_int(base_frequency),
@ -1364,7 +1362,8 @@ void GaborTextureNode::compile(SVMCompiler &compiler)
compiler.add_node(__float_as_int(cell_randomness),
__float_as_int(rotation),
__float_as_int(rot_variance),
__float_as_int(dir_randomness));
__float_as_int(tilt_randomness));
compiler.add_node(__float_as_int(anistropy));
tex_mapping.compile_end(compiler, vector_in, vector_stack_offset);
}
@ -1374,7 +1373,6 @@ void GaborTextureNode::compile(OSLCompiler &compiler)
tex_mapping.compile(compiler);
compiler.parameter(this, "dimensions");
compiler.parameter(this, "mode");
compiler.parameter(this, "anisotropic");
compiler.parameter(this, "periodic");
compiler.parameter(this, "use_normalize");
compiler.parameter(this, "use_origin_offset");

View File

@ -245,7 +245,6 @@ class GaborTextureNode : public TextureNode {
NODE_SOCKET_API(int, dimensions)
NODE_SOCKET_API(NodeGaborMode, mode);
NODE_SOCKET_API(NodeGaborAnisotropic, anisotropic);
NODE_SOCKET_API(bool, periodic)
NODE_SOCKET_API(bool, use_normalize)
NODE_SOCKET_API(bool, use_origin_offset)
@ -265,8 +264,9 @@ class GaborTextureNode : public TextureNode {
NODE_SOCKET_API(float, phase_variance)
NODE_SOCKET_API(float, rotation)
NODE_SOCKET_API(float, rot_variance)
NODE_SOCKET_API(float, dir_randomness)
NODE_SOCKET_API(float, tilt_randomness)
NODE_SOCKET_API(float, cell_randomness)
NODE_SOCKET_API(float, anistropy)
NODE_SOCKET_API(float3, direction)
};

View File

@ -25,7 +25,6 @@
* - Added Roughness, Scale Lacunarity, Frequency Lacunarity and Rotation Lacunarity to control
* additive fractal noise.
* - Uses built-in Blender hashes instead of adding a separate gabor rng.
* - Anisotropic input direction is not normalised by default for artistic control.
*
* Adapted from Open Shading Language implementation.
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
@ -49,7 +48,8 @@
#define SHD_GABOR_MODE_PHASOR_CROSS 6
#define SHD_GABOR_MODE_PHASOR_SQUARE 7
#define GABOR_SEED 1259
/* Large prime number for grid offset and impulse seed. */
#define GABOR_SEED 11939
struct GaborParams {
float base_frequency;
@ -60,10 +60,10 @@ struct GaborParams {
float phase_variance;
float rotation;
float rot_variance;
float dir_randomness;
float tilt_randomness;
float cell_randomness;
float anistropy;
int mode;
int anisotropic;
vec3 direction;
};
@ -143,7 +143,7 @@ vec3 gabor_kernel(GaborParams gp, vec3 omega, float phi, vec3 position, float dv
return r * g;
}
/* Set omega (angular frequency) and phi (phase) for the impulse based on the anisotropic
/* Set omega (angular frequency) and phi (phase) for the impulse based on the anistropy
* parameter. */
vec3 gabor_sample(GaborParams gp, vec3 cell, int seed, out float phi)
{
@ -151,28 +151,15 @@ vec3 gabor_sample(GaborParams gp, vec3 cell, int seed, out float phi)
float pvar = mix(0.0, rand_values.z, gp.phase_variance);
phi = M_2PI * pvar + gp.phase;
/* Anisotropic direction. */
if (gp.anisotropic == 1) {
/* Reference code returns normalize(gp.direction). */
return normalize(gp.direction + rand_values * gp.dir_randomness);
}
else if (gp.anisotropic == 2) { /* Hybrid. */
float ovar = M_PI * (rand_values.x);
float omega_t = ovar * gp.rot_variance - gp.rotation;
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
/* Reference code uses length(gp.direction). */
return normalize(gp.direction * vec3(cos_omega_t, sin_omega_t, gp.dir_randomness));
}
else { /* Isotropic. */
float ovar = M_PI * (rand_values.x);
float omega_t = ovar * gp.rot_variance - gp.rotation;
float cos_omega_p = clamp(rand_values.y * gp.dir_randomness, -1.0, 1.0);
float sin_omega_p = sqrt(1.0 - cos_omega_p * cos_omega_p);
float sin_omega_t = sin(omega_t);
float cos_omega_t = cos(omega_t);
return normalize(vec3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
}
float ovar = M_PI * (rand_values.x);
float omega_t = ovar * gp.rot_variance - gp.rotation;
float cos_omega_p = clamp(rand_values.y * gp.tilt_randomness, -1.0, 1.0);
float sin_omega_p = sqrt(1.0 - cos_omega_p * cos_omega_p);
float sin_omega_t = sin(omega_t);
CharlieJolly marked this conversation as resolved Outdated

This if seems redundant and can be removed.

This `if` seems redundant and can be removed.

Is this a redundant optimisation?

Is this a redundant optimisation?

I wouldn't call it an optimization, branches like this can needlessly slow down vectorized code, so it is best avoided.

I wouldn't call it an optimization, branches like this can needlessly slow down vectorized code, so it is best avoided.

I assume this is the same for all codebases or just glsl?

I assume this is the same for all codebases or just glsl?

This is probably for all backends, not just GLSL.

This is probably for all backends, not just GLSL.
float cos_omega_t = cos(omega_t);
return mix(normalize(vec3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p)),
CharlieJolly marked this conversation as resolved Outdated

I am still not sure why the anisotropic and isotropic types can't be joined by defining an annular sector as described in section "3.2 Noise with Controllable Band-Limits" of the paper.

I am still not sure why the anisotropic and isotropic types can't be joined by defining an annular sector as described in section "3.2 Noise with Controllable Band-Limits" of the paper.

I can add back the Hybrid mode.

I can add back the Hybrid mode.

@CharlieJolly So the hybrid mode was based on that section of the paper?

@CharlieJolly So the hybrid mode was based on that section of the paper?

Well TBH I'm not sure, reading it again this looks like a feature of their application which is no longer available it seems. In this case I believe this can be achieved using the Rotation Variance control or by using the fractal noise options.

Well TBH I'm not sure, reading it again this looks like a feature of their application which is no longer available it seems. In this case I believe this can be achieved using the Rotation Variance control or by using the fractal noise options.

Well, the hybrid mode is actually described in section 3.3 of:

Lagae, Ares, and George Drettakis. "Filtering solid Gabor noise." ACM Transactions on Graphics (TOG) 30.4 (2011): 1-6.

But what I am after is removing the mode altogether and replacing it with intutive parameters that describe and annular sector as mentioned before.

Well, the hybrid mode is actually described in section 3.3 of: Lagae, Ares, and George Drettakis. "Filtering solid Gabor noise." ACM Transactions on Graphics (TOG) 30.4 (2011): 1-6. But what I am after is removing the mode altogether and replacing it with intutive parameters that describe and annular sector as mentioned before.

@OmarEmaraDev this is achieved using the Rotation and Rotation Variance controls.

image

@OmarEmaraDev this is achieved using the Rotation and Rotation Variance controls. ![image](/attachments/e4d3e0a6-737b-466c-8c23-2eba581bdebd)

Is isotropic the same as Rotation Variance = 360?

Is isotropic the same as `Rotation Variance = 360`?

Yes, in OSL implementation we have the angular frequency set to a random number

float omega_t = float(M_TWO_PI) * rng();

in this patch this is contolled by Rotation and Rotation Variance.

float ovar = M_PI * (rand_values.x * 2.0 - 1.0);
float omega_t = ovar * gp.rot_variance - gp.rotation;
Yes, in OSL implementation we have the angular frequency set to a random number ``` float omega_t = float(M_TWO_PI) * rng(); ``` in this patch this is contolled by Rotation and Rotation Variance. ``` float ovar = M_PI * (rand_values.x * 2.0 - 1.0); float omega_t = ovar * gp.rot_variance - gp.rotation; ```

Okay, so why not remove the enum, rename Rotation Variance to Anisotropy, and unify the Direction and Rotation inputs?

Okay, so why not remove the enum, rename Rotation Variance to Anisotropy, and unify the Direction and Rotation inputs?

Okay, so why not remove the enum, rename Rotation Variance to Anisotropy, and unify the Direction and Rotation inputs?

I did think about this but found it tricky to do this in a nice way. The Rotation controls are not shown on Anisotropic mode as it is only controlled by the direction vector. Hybrid mixed both these modes so that maybe something to consider adding back.

> Okay, so why not remove the enum, rename Rotation Variance to Anisotropy, and unify the Direction and Rotation inputs? I did think about this but found it tricky to do this in a nice way. The Rotation controls are not shown on Anisotropic mode as it is only controlled by the direction vector. Hybrid mixed both these modes so that maybe something to consider adding back.

I really think we should try to reduce the number of options and inputs in the node as much as we can, because those inputs are not all orthogonal, so the node is hard to use for the average user.

I really think we should try to reduce the number of options and inputs in the node as much as we can, because those inputs are not all orthogonal, so the node is hard to use for the average user.

The number of inputs is definitely increased by adding the fractal controls. Removing those alone would reduce inputs by five but at the loss of that feature. (@Hoshinova)

image

Node panels patch may help here.

Otherwise it is difficult to reduce the params without compromising the features.

In comparison to the Principled BSDF.

image

The number of inputs is definitely increased by adding the fractal controls. Removing those alone would reduce inputs by five but at the loss of that feature. (@Hoshinova) ![image](/attachments/213c08e3-2dfa-4c3b-8d9c-c6b451629ebc) Node panels patch may help here. Otherwise it is difficult to reduce the params without compromising the features. In comparison to the Principled BSDF. ![image](/attachments/d12b218d-3582-496e-9f24-7e0c5bf624a7)

Please stop this project and look into adding loops to shaders and reworking all fractal noise texture types into a node group asset)

Please stop this project and look into adding loops to shaders and reworking all fractal noise texture types into a node group asset)

Please stop this project and look into adding loops to shaders and reworking all fractal noise texture types into a node group asset)

Voronoi and Noise have both recently had fractal noise modes added. That is why this was added to this node.

I can't comment on the feasibility of adding loops to shading nodes. Don't forget that for any shading node, there has to be three implementations for Eevee, Cycles and OSL.

> Please stop this project and look into adding loops to shaders and reworking all fractal noise texture types into a node group asset) Voronoi and Noise have both recently had fractal noise modes added. That is why this was added to this node. I can't comment on the feasibility of adding loops to shading nodes. Don't forget that for any shading node, there has to be three implementations for Eevee, Cycles and OSL.

The UI has now been grouped using the new Panel feature.

The UI has now been grouped using the new Panel feature.
normalize(gp.direction),
gp.anistropy);
}
/* Generate noise based on the cell position and number of impulses. */
@ -324,21 +311,21 @@ GaborParams gabor_parameters(vec3 direction,
float phase_variance,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float cell_randomness,
int mode,
int anisotropic)
float anistropy,
int mode)
{
GaborParams gp;
gp.impulses = clamp(impulses, 0.0001, 32.0);
gp.rot_variance = rot_variance;
gp.anisotropic = anisotropic;
gp.anistropy = anistropy;
gp.mode = mode;
gp.direction = direction;
gp.phase = phase;
gp.rotation = rotation;
gp.phase_variance = phase_variance;
gp.dir_randomness = dir_randomness;
gp.tilt_randomness = tilt_randomness;
gp.cell_randomness = cell_randomness;
gp.gain = gain;
gp.radius = radius;
@ -407,14 +394,14 @@ void node_tex_gabor(vec3 co,
float impulses,
float phase,
float phase_variance,
float cell_randomness,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float anistropy,
vec3 direction,
float cell_randomness,
float dimensions,
float mode,
float anisotropic,
float use_normalize,
float periodic,
float use_origin_offset,
@ -444,10 +431,10 @@ void node_tex_gabor(vec3 co,
phase_variance,
rotation,
rot_variance,
CharlieJolly marked this conversation as resolved Outdated

What does sqrt(0.75 / pi) correspond to here?

What does `sqrt(0.75 / pi)` correspond to here?

Locally I have updated the comment.

  /* Scale height of noise by the number of impulses. This is empircal as there is no easy way
   * to analytically determine the scaling factor for the sum of impulses.
   * Phasor does not require this because it is always returns [-1,1].
   * Following calculation from Lee Bruemmer OSL script implementation
   * Scale the noise so the range [-1,1] covers 6 standard deviations, or 3*sqrt(variance)
   * Since the radius is set to 1/a the scale simplifies to 3/4*sqrt(3*n/(pi*sqrt(2)))
   * float scale = 0.6162961511 * sqrt( Impulses ); but with a fixed number of impulses also divide
   * by sqrt(0.75/pi) (0.4886025119) which give 1.2613446229 * sqrt(gp.impulses).
   * In tests I've found that sqrt(2) 1.41 clips less when clamped.
   */
Locally I have updated the comment. ``` /* Scale height of noise by the number of impulses. This is empircal as there is no easy way * to analytically determine the scaling factor for the sum of impulses. * Phasor does not require this because it is always returns [-1,1]. * Following calculation from Lee Bruemmer OSL script implementation * Scale the noise so the range [-1,1] covers 6 standard deviations, or 3*sqrt(variance) * Since the radius is set to 1/a the scale simplifies to 3/4*sqrt(3*n/(pi*sqrt(2))) * float scale = 0.6162961511 * sqrt( Impulses ); but with a fixed number of impulses also divide * by sqrt(0.75/pi) (0.4886025119) which give 1.2613446229 * sqrt(gp.impulses). * In tests I've found that sqrt(2) 1.41 clips less when clamped. */ ```
dir_randomness,
tilt_randomness,
cell_randomness,
int(mode),
int(anisotropic));
anistropy,
int(mode));
float g = gabor_fractal_noise(
CharlieJolly marked this conversation as resolved Outdated

Does clamping here mean we will get flat areas? Can't we adjust the scale factor to ensure a correct range instead?

Does clamping here mean we will get flat areas? Can't we adjust the scale factor to ensure a correct range instead?

See previous comment. Due to the way the impulse sum, it is a compromise between a scale factor that is too low and clips and is too high and creates a mid level grey 0.5 texture when scale factor is too high.

See previous comment. Due to the way the impulse sum, it is a compromise between a scale factor that is too low and clips and is too high and creates a mid level grey 0.5 texture when scale factor is too high.

Honestly, I'm also not entirely convinced by this solution mainly because it loses all the detail in the highs and lows.
I thought of a solution which involves using a piecewise function which would need to converge towards 0.0 when x -> -infinity and 1.0 when x -> +infinity. The function should simply be linear in the middle part and be continuously differentiable everywhere.
That way we could both remap it into a [0.0, 1.0] range and preserve all details, however I'd first need to think of a fitting function, so if necessary I'd do it in a separate PR after this one has been merged.

But ideally @CharlieJolly can find a better way.

Honestly, I'm also not entirely convinced by this solution mainly because it loses all the detail in the highs and lows. I thought of a solution which involves using a piecewise function which would need to converge towards 0.0 when x -> -infinity and 1.0 when x -> +infinity. The function should simply be linear in the middle part and be continuously differentiable everywhere. That way we could both remap it into a [0.0, 1.0] range **and** preserve all details, however I'd first need to think of a fitting function, so if necessary I'd do it in a separate PR after this one has been merged. But ideally @CharlieJolly can find a better way.

Some kind of gain and bias function would be useful to do this I think. Users can run this through a float curve but I'm not sure there is a perfect solution here.

Some kind of gain and bias function would be useful to do this I think. Users can run this through a float curve but I'm not sure there is a perfect solution here.

If you can't think of anything immediately I think it's also fine to leave it for now.
I'll think about an appropriate function in the meantime.

If you can't think of anything immediately I think it's also fine to leave it for now. I'll think about an appropriate function in the meantime.
fp, gp, co, scale, int(dimensions), int(periodic), int(use_origin_offset));

View File

@ -1270,12 +1270,11 @@ typedef struct NodeTexNoise {
typedef struct NodeTexGabor {
NodeTexBase base;
char mode;
char anisotropic;
char periodic;
char normalize;
char dimensions;
char use_origin_offset;
char _pad[2];
char _pad[3];
} NodeTexGabor;
typedef struct NodeTexVoronoi {
@ -2049,12 +2048,6 @@ typedef enum NodeGaborMode {
SHD_GABOR_MODE_PHASOR_SQUARE,
} NodeGaborMode;
typedef enum NodeGaborAnisotropic {
SHD_GABOR_ISOTROPIC,
SHD_GABOR_ANISOTROPIC,
SHD_GABOR_HYBRID,
} NodeGaborAnisotropic;
/* musgrave texture */
enum {
SHD_MUSGRAVE_MULTIFRACTAL = 0,

View File

@ -5026,13 +5026,6 @@ static void def_sh_tex_gabor(StructRNA *srna)
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem prop_gabor_aniso[] = {
{SHD_GABOR_ISOTROPIC, "ISOTROPIC", 0, "Isotropic", "Isotropic noise"},
{SHD_GABOR_ANISOTROPIC, "ANISOTROPIC", 0, "Anisotropic", "Anisotropic noise"},
{SHD_GABOR_HYBRID, "HYBRID", 0, "Hybrid", "Hybrid noise"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem prop_gabor_dimensions[] = {
{2, "2D", 0, "2D", "Use the 2D vector (X, Y) as input. The Z component is ignored"},
{3, "3D", 0, "3D", "Use the 3D vector (X, Y, Z) as input"},
@ -5055,12 +5048,6 @@ static void def_sh_tex_gabor(StructRNA *srna)
RNA_def_property_ui_text(prop, "Mode", "Mode");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "anisotropic", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "anisotropic");
RNA_def_property_enum_items(prop, prop_gabor_aniso);
RNA_def_property_ui_text(prop, "Anisotropic", "Anisotropic");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "periodic", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "periodic", 0);
RNA_def_property_ui_text(prop, "Periodic", "Periodic noise");

View File

@ -24,7 +24,6 @@
#include "node_shader_util.hh"
#include "node_util.hh"
#include "BKE_node_runtime.hh"
#include "BKE_texture.h"
#include "BLI_hash.hh"
@ -78,11 +77,10 @@ static void node_declare(NodeDeclarationBuilder &b)
.description("The difference between the kernel frequency of each consecutive octave");
fractal.add_input<decl::Float>("Rotation Lacunarity")
.subtype(PROP_ANGLE)
.description("The difference between the kernel rotation of each consecutive octave");
PanelDeclarationBuilder &kernel = b.add_panel("Kernel").default_closed(true).draw_buttons(
[](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) {
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
});
.description(
"The difference between the kernel rotation of each consecutive octave, does not work "
"when Anisotropic is set to 1");
PanelDeclarationBuilder &kernel = b.add_panel("Kernel").default_closed(true);
kernel.add_input<decl::Float>("Radius")
.min(0.0f)
.max(1.0f)
@ -94,38 +92,43 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(16.0f)
.default_value(2.0f)
.description("Controls the amount of kernel impulses, high values are slower to compute");
PanelDeclarationBuilder &params = b.add_panel("Parameters").default_closed(false);
params.add_input<decl::Float>("Phase")
kernel.add_input<decl::Float>("Phase")
.subtype(PROP_ANGLE)
.description("Kernel shape phase offset");
params.add_input<decl::Float>("Phase Variance")
kernel.add_input<decl::Float>("Phase Variance")
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR);
params.add_input<decl::Float>("Rotation")
kernel.add_input<decl::Float>("Cell Randomness")
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR);
PanelDeclarationBuilder &aniso = b.add_panel("Anisotropy").default_closed(false);
aniso.add_input<decl::Float>("Rotation")
.subtype(PROP_ANGLE)
.description("Kernel shape rotation");
params.add_input<decl::Float>("Rotation Variance")
aniso.add_input<decl::Float>("Rotation Variance")
.subtype(PROP_ANGLE)
.description("Rotation randomness");
params.add_input<decl::Float>("Direction Randomness")
aniso.add_input<decl::Float>("Tilt Randomness")
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR);
params.add_input<decl::Vector>("Direction")
aniso.add_input<decl::Float>("Anistropic Factor")
.min(0.0f)
.max(1.0f)
.default_value(0.0f)
.subtype(PROP_FACTOR)
.description("Mix between Isotropic and fixed Anisotropic control");
aniso.add_input<decl::Vector>("Direction")
.description("Direction and magnitude of the anisotropy")
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
.max(1.0f)
.subtype(PROP_DIRECTION);
params.add_input<decl::Float>("Cell Randomness")
.min(0.0f)
.max(1.0f)
.default_value(1.0f)
.subtype(PROP_FACTOR)
.description("Cell randomness");
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
@ -135,7 +138,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
col = uiLayoutColumn(layout, true);
split = uiLayoutSplit(col, 0.33f, true);
uiItemR(split, ptr, "gabor_dimensions", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(split, ptr, "anisotropic", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(split, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "periodic", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
@ -149,7 +152,6 @@ static void node_init(bNodeTree * /*ntree*/, bNode *node)
BKE_texture_colormapping_default(&tex->base.color_mapping);
tex->mode = SHD_GABOR_MODE_GABOR;
tex->anisotropic = SHD_GABOR_ISOTROPIC;
tex->periodic = 0;
tex->normalize = 1;
tex->dimensions = 2;
@ -169,7 +171,6 @@ static int node_shader_gpu_tex_gabor(GPUMaterial *mat,
const NodeTexGabor &storage = node_storage(*node);
const float dimensions = storage.dimensions;
const float mode = storage.mode;
const float anisotropic = storage.anisotropic;
const float periodic = storage.periodic;
const float use_normalize = storage.normalize;
const float use_origin_offset = storage.use_origin_offset;
@ -181,32 +182,12 @@ static int node_shader_gpu_tex_gabor(GPUMaterial *mat,
out,
GPU_constant(&dimensions),
GPU_constant(&mode),
GPU_constant(&anisotropic),
GPU_constant(&use_normalize),
GPU_constant(&periodic),
GPU_constant(&use_origin_offset));
}
static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *inDirectionSock = nodeFindSocket(node, SOCK_IN, "Direction");
bNodeSocket *inRotationSock = nodeFindSocket(node, SOCK_IN, "Rotation");
bNodeSocket *inVarianceSock = nodeFindSocket(node, SOCK_IN, "Rotation Variance");
bNodeSocket *inRotationLacunaritySock = nodeFindSocket(node, SOCK_IN, "Rotation Lacunarity");
const NodeTexGabor &storage = node_storage(*node);
bke::nodeSetSocketAvailability(
ntree, inDirectionSock, (storage.anisotropic != SHD_GABOR_ISOTROPIC));
bke::nodeSetSocketAvailability(
ntree, inRotationSock, (storage.anisotropic != SHD_GABOR_ANISOTROPIC));
bke::nodeSetSocketAvailability(
ntree, inVarianceSock, (storage.anisotropic != SHD_GABOR_ANISOTROPIC));
bke::nodeSetSocketAvailability(
ntree, inRotationLacunaritySock, (storage.anisotropic != SHD_GABOR_ANISOTROPIC));
}
#define GABOR_SEED 1259
#define GABOR_SEED 11939
typedef struct GaborParams {
float base_frequency;
@ -217,10 +198,10 @@ typedef struct GaborParams {
float phase_variance;
float rotation;
float rot_variance;
float dir_randomness;
float tilt_randomness;
float cell_randomness;
float anistropy;
int mode;
int anisotropic;
float3 direction;
} GaborParams;
@ -305,26 +286,16 @@ static float3 gabor_sample(const GaborParams gp, const float3 cell, const int se
const float pvar = math::interpolate(0.0f, rand_values.z, gp.phase_variance);
phi = 2.0f * float(M_PI) * pvar + gp.phase;
if (gp.anisotropic == SHD_GABOR_ANISOTROPIC) { /* ANISO */
return math::normalize(gp.direction + rand_values * gp.dir_randomness);
}
else if (gp.anisotropic == SHD_GABOR_HYBRID) { /* HYBRID */
const float ovar = float(M_PI) * (rand_values.x);
const float omega_t = ovar * gp.rot_variance - gp.rotation;
float sin_omega_t = math::sin(omega_t);
float cos_omega_t = math::cos(omega_t);
return math::normalize(gp.direction * float3(cos_omega_t, sin_omega_t, gp.dir_randomness));
}
else { /* ISO */
const float ovar = float(M_PI) * (rand_values.x);
const float omega_t = ovar * gp.rot_variance - gp.rotation;
const float cos_omega_p = math::clamp(rand_values.y * gp.dir_randomness, -1.0f, 1.0f);
const float sin_omega_p = math::sqrt(1.0f - cos_omega_p * cos_omega_p);
const float sin_omega_t = math::sin(omega_t);
const float cos_omega_t = math::cos(omega_t);
return math::normalize(
float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
}
const float ovar = float(M_PI) * (rand_values.x);
const float omega_t = ovar * gp.rot_variance - gp.rotation;
const float cos_omega_p = math::clamp(rand_values.y * gp.tilt_randomness, -1.0f, 1.0f);
const float sin_omega_p = math::sqrt(1.0f - cos_omega_p * cos_omega_p);
const float sin_omega_t = math::sin(omega_t);
const float cos_omega_t = math::cos(omega_t);
return math::interpolate(
math::normalize(float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p)),
math::normalize(gp.direction),
gp.anistropy);
}
/* Generate noise based on the cell position. */
@ -518,21 +489,21 @@ static GaborParams gabor_parameters(float3 direction,
float phase_variance,
float rotation,
float rot_variance,
float dir_randomness,
float tilt_randomness,
float cell_randomness,
int mode,
int anisotropic)
float anistropy,
int mode)
{
GaborParams gp;
gp.impulses = math::clamp(impulses, 0.0001f, 32.0f);
gp.rot_variance = rot_variance;
gp.anisotropic = anisotropic;
gp.anistropy = anistropy;
gp.mode = mode;
gp.direction = direction;
gp.phase = phase;
gp.rotation = rotation;
gp.phase_variance = phase_variance;
gp.dir_randomness = dir_randomness;
gp.tilt_randomness = tilt_randomness;
gp.cell_randomness = cell_randomness;
gp.gain = gain;
gp.radius = radius;
@ -556,11 +527,11 @@ static float gabor_noise(const float3 p,
const float phase_variance,
const float rotation,
const float rot_variance,
const float dir_randomness,
const float tilt_randomness,
const float cell_randomness,
const float anistropy,
const int dimensions,
const int mode,
const int anisotropic,
const int use_normalize,
const int periodic,
const int use_origin_offset)
@ -585,10 +556,10 @@ static float gabor_noise(const float3 p,
phase_variance,
rotation,
rot_variance,
dir_randomness,
tilt_randomness,
cell_randomness,
mode,
anisotropic);
anistropy,
mode);
float g = gabor_fractal_noise(fp, gp, p, scale, dimensions, periodic, use_origin_offset);
@ -606,7 +577,7 @@ static float gabor_noise(const float3 p,
return g;
}
static mf::Signature gabor_signature(const char *name, const int anistropic)
static mf::Signature gabor_signature(const char *name)
{
mf::Signature signature;
mf::SignatureBuilder builder{name, signature};
@ -618,23 +589,17 @@ static mf::Signature gabor_signature(const char *name, const int anistropic)
builder.single_input<float>("Roughness");
builder.single_input<float>("Scale Lacunarity");
builder.single_input<float>("Frequency Lacunarity");
if (anistropic != SHD_GABOR_ANISOTROPIC) {
builder.single_input<float>("Rotation Lacunarity");
}
builder.single_input<float>("Rotation Lacunarity");
builder.single_input<float>("Radius");
builder.single_input<float>("Impulses");
builder.single_input<float>("Phase");
builder.single_input<float>("Phase Variance");
if (anistropic != SHD_GABOR_ANISOTROPIC) {
builder.single_input<float>("Rotation");
builder.single_input<float>("Rotation Variance");
}
if (anistropic != SHD_GABOR_ISOTROPIC) {
builder.single_input<float3>("Direction");
}
builder.single_input<float>("Direction Randomness");
builder.single_input<float>("Cell Randomness");
builder.single_input<float>("Rotation");
builder.single_input<float>("Rotation Variance");
builder.single_input<float3>("Direction");
builder.single_input<float>("Tilt Randomness");
builder.single_input<float>("Anistropic Factor");
builder.single_output<float>("Value");
return signature;
}
@ -657,7 +622,7 @@ class GaborNoiseFunction : public mf::MultiFunction {
use_origin_offset_(use_origin_offset)
{
static mf::Signature signature = gabor_signature("GaborNoise", SHD_GABOR_ISOTROPIC);
static mf::Signature signature = gabor_signature("GaborNoise");
this->set_signature(&signature);
}
@ -683,190 +648,17 @@ class GaborNoiseFunction : public mf::MultiFunction {
const VArray<float> &phase = params.readonly_single_input<float>(param++, "Phase");
const VArray<float> &phase_variance = params.readonly_single_input<float>(param++,
"Phase Variance");
const VArray<float> &cell_randomness = params.readonly_single_input<float>(param++,
"Cell Randomness");
const VArray<float> &rotation = params.readonly_single_input<float>(param++, "Rotation");
const VArray<float> &rot_variance = params.readonly_single_input<float>(param++,
"Rotation Variance");
const VArray<float> &dir_randomness = params.readonly_single_input<float>(
param++, "Direction Randomness");
const VArray<float> &cell_randomness = params.readonly_single_input<float>(param++,
"Cell Randomness");
MutableSpan<float> r_value = params.uninitialized_single_output_if_required<float>(param++,
"Value");
mask.foreach_index([&](const int64_t i) {
r_value[i] = gabor_noise(vector[i],
float3(0.0f),
scale[i],
base_frequency[i],
detail[i],
roughness[i],
scale_lacunarity[i],
freq_lacunarity[i],
rot_lacunarity[i],
gain[i],
radius[i],
impulses[i],
phase[i],
phase_variance[i],
rotation[i],
rot_variance[i],
dir_randomness[i],
cell_randomness[i],
dimensions_,
mode_,
SHD_GABOR_ISOTROPIC,
normalize_,
periodic_,
use_origin_offset_);
});
}
ExecutionHints get_execution_hints() const override
{
ExecutionHints hints;
hints.allocates_array = false;
hints.min_grain_size = 100;
return hints;
}
};
class AnisotropicGaborNoiseFunction : public mf::MultiFunction {
private:
int dimensions_;
int mode_;
int periodic_;
int normalize_;
int use_origin_offset_;
public:
AnisotropicGaborNoiseFunction(
int dimensions, int mode, int periodic, int normalize, int use_origin_offset)
: dimensions_(dimensions),
mode_(mode),
periodic_(periodic),
normalize_(normalize),
use_origin_offset_(use_origin_offset)
{
static mf::Signature signature = gabor_signature("AnistropicGaborNoise",
SHD_GABOR_ANISOTROPIC);
this->set_signature(&signature);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
int param = 0;
const VArray<float3> &vector = params.readonly_single_input<float3>(param++, "Vector");
const VArray<float> &scale = params.readonly_single_input<float>(param++, "Scale");
const VArray<float> &base_frequency = params.readonly_single_input<float>(param++,
"Base Frequency");
const VArray<float> &gain = params.readonly_single_input<float>(param++, "Gain");
const VArray<float> &detail = params.readonly_single_input<float>(param++, "Detail");
const VArray<float> &roughness = params.readonly_single_input<float>(param++, "Roughness");
const VArray<float> &scale_lacunarity = params.readonly_single_input<float>(
param++, "Scale Lacunarity");
const VArray<float> &freq_lacunarity = params.readonly_single_input<float>(
param++, "Frequency Lacunarity");
const VArray<float> &radius = params.readonly_single_input<float>(param++, "Radius");
const VArray<float> &impulses = params.readonly_single_input<float>(param++, "Impulses");
const VArray<float> &phase = params.readonly_single_input<float>(param++, "Phase");
const VArray<float> &phase_variance = params.readonly_single_input<float>(param++,
"Phase Variance");
const VArray<float> &dir_randomness = params.readonly_single_input<float>(
param++, "Direction Randomness");
const VArray<float> &tilt_randomness = params.readonly_single_input<float>(param++,
"Tilt Randomness");
const VArray<float> &anistropy = params.readonly_single_input<float>(param++,
"Anistropic Factor");
const VArray<float3> &direction = params.readonly_single_input<float3>(param++, "Direction");
const VArray<float> &cell_randomness = params.readonly_single_input<float>(param++,
"Cell Randomness");
MutableSpan<float> r_value = params.uninitialized_single_output_if_required<float>(param++,
"Value");
mask.foreach_index([&](const int64_t i) {
r_value[i] = gabor_noise(vector[i],
direction[i],
scale[i],
base_frequency[i],
detail[i],
roughness[i],
scale_lacunarity[i],
freq_lacunarity[i],
0.0f,
gain[i],
radius[i],
impulses[i],
phase[i],
phase_variance[i],
0.0f,
0.0f,
dir_randomness[i],
cell_randomness[i],
dimensions_,
mode_,
SHD_GABOR_ANISOTROPIC,
normalize_,
periodic_,
use_origin_offset_);
});
}
ExecutionHints get_execution_hints() const override
{
ExecutionHints hints;
hints.allocates_array = false;
hints.min_grain_size = 100;
return hints;
}
};
class HybridGaborNoiseFunction : public mf::MultiFunction {
private:
int dimensions_;
int mode_;
int periodic_;
int normalize_;
int use_origin_offset_;
public:
HybridGaborNoiseFunction(
int dimensions, int kernel, int periodic, int normalize, int use_origin_offset)
: dimensions_(dimensions),
mode_(kernel),
periodic_(periodic),
normalize_(normalize),
use_origin_offset_(use_origin_offset)
{
static mf::Signature signature = gabor_signature("HybridGaborNoise", SHD_GABOR_HYBRID);
this->set_signature(&signature);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
int param = 0;
const VArray<float3> &vector = params.readonly_single_input<float3>(param++, "Vector");
const VArray<float> &scale = params.readonly_single_input<float>(param++, "Scale");
const VArray<float> &base_frequency = params.readonly_single_input<float>(param++,
"Base Frequency");
const VArray<float> &gain = params.readonly_single_input<float>(param++, "Gain");
const VArray<float> &detail = params.readonly_single_input<float>(param++, "Detail");
const VArray<float> &roughness = params.readonly_single_input<float>(param++, "Roughness");
const VArray<float> &scale_lacunarity = params.readonly_single_input<float>(
param++, "Scale Lacunarity");
const VArray<float> &freq_lacunarity = params.readonly_single_input<float>(
param++, "Frequency Lacunarity");
const VArray<float> &rot_lacunarity = params.readonly_single_input<float>(
param++, "Rotation Lacunarity");
const VArray<float> &radius = params.readonly_single_input<float>(param++, "Radius");
const VArray<float> &impulses = params.readonly_single_input<float>(param++, "Impulses");
const VArray<float> &phase = params.readonly_single_input<float>(param++, "Phase");
const VArray<float> &phase_variance = params.readonly_single_input<float>(param++,
"Phase Variance");
const VArray<float> &rotation = params.readonly_single_input<float>(param++, "Rotation");
const VArray<float> &rot_variance = params.readonly_single_input<float>(param++,
"Rotation Variance");
const VArray<float> &dir_randomness = params.readonly_single_input<float>(
param++, "Direction Randomness");
const VArray<float3> &direction = params.readonly_single_input<float3>(param++, "Direction");
const VArray<float> &cell_randomness = params.readonly_single_input<float>(param++,
"Cell Randomness");
MutableSpan<float> r_value = params.uninitialized_single_output_if_required<float>(param++,
"Value");
@ -887,11 +679,11 @@ class HybridGaborNoiseFunction : public mf::MultiFunction {
phase_variance[i],
rotation[i],
rot_variance[i],
dir_randomness[i],
tilt_randomness[i],
cell_randomness[i],
anistropy[i],
dimensions_,
mode_,
SHD_GABOR_HYBRID,
normalize_,
periodic_,
use_origin_offset_);
@ -910,30 +702,11 @@ class HybridGaborNoiseFunction : public mf::MultiFunction {
static void build_multi_function(NodeMultiFunctionBuilder &builder)
{
const NodeTexGabor &storage = node_storage(builder.node());
switch (storage.anisotropic) {
case SHD_GABOR_ISOTROPIC:
builder.construct_and_set_matching_fn<GaborNoiseFunction>(storage.dimensions,
storage.mode,
storage.periodic,
storage.normalize,
storage.use_origin_offset);
break;
case SHD_GABOR_ANISOTROPIC:
builder.construct_and_set_matching_fn<AnisotropicGaborNoiseFunction>(
storage.dimensions,
storage.mode,
storage.periodic,
storage.normalize,
storage.use_origin_offset);
break;
case SHD_GABOR_HYBRID:
builder.construct_and_set_matching_fn<HybridGaborNoiseFunction>(storage.dimensions,
storage.mode,
storage.periodic,
storage.normalize,
storage.use_origin_offset);
break;
}
builder.construct_and_set_matching_fn<GaborNoiseFunction>(storage.dimensions,
storage.mode,
storage.periodic,
storage.normalize,
storage.use_origin_offset);
}
} // namespace blender::nodes::node_shader_tex_gabor_cc
@ -949,7 +722,6 @@ void register_node_type_sh_tex_gabor()
ntype.draw_buttons = file_ns::node_layout;
ntype.initfunc = file_ns::node_init;
ntype.gpu_fn = file_ns::node_shader_gpu_tex_gabor;
ntype.updatefunc = file_ns::node_update;
node_type_storage(
&ntype, "NodeTexGabor", node_free_standard_storage, node_copy_standard_storage);
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::MIDDLE);