Node: Gabor Noise Texture #110802
|
@ -119,23 +119,31 @@ vector3 gabor_kernel(GaborParams gp, point omega, float phi, point position, flo
|
|||
return r * g;
|
||||
}
|
||||
|
||||
void gabor_sample(GaborParams gp, vector3 cell, int seed, output vector omega, output float phi)
|
||||
vector gabor_sample(GaborParams gp, vector3 cell, int seed, output float phi)
|
||||
{
|
||||
vector3 rand_values = hash_vector4_to_color(vector4(cell[0], cell[1], cell[2], float(seed)));
|
||||
float pvar = mix(0.0, rand_values.z * 2.0 - 1.0, gp.phase_variance);
|
||||
vector3 rand_values = hash_vector4_to_color(vector4(cell.x, cell.y, cell.z, float(seed))) * 2.0 -
|
||||
1.0;
|
||||
float pvar = mix(0.0, rand_values.z, gp.phase_variance);
|
||||
phi = M_2PI * pvar + gp.phase;
|
||||
|
||||
if (gp.anisotropic == "aniso") { /* ANISO */
|
||||
omega = gp.direction;
|
||||
return gp.direction;
|
||||
}
|
||||
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 gp.direction * point(cos_omega_t, sin_omega_t, 0.0);
|
||||
}
|
||||
else { /* ISO */
|
||||
float ovar = M_PI * (rand_values.x * 2.0 - 1.0) * gp.rot_variance;
|
||||
float ovar = M_PI * (rand_values.x) * gp.rot_variance;
|
||||
float omega_t = ovar - gp.rotation;
|
||||
float cos_omega_p = 1.0 - 2.0 * rand_values.y;
|
||||
float cos_omega_p = -rand_values.y;
|
||||
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);
|
||||
omega = normalize(vector(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
|
||||
return normalize(vector(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,10 +162,8 @@ vector3 gabor_cell_3d(output GaborParams gp, point cell, point cell_position, in
|
|||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0) {
|
||||
vector3 omega;
|
||||
float phi;
|
||||
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, omega, phi);
|
||||
vector3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
@ -180,10 +186,8 @@ vector3 gabor_cell_2d(output GaborParams gp, point cell, point cell_position, in
|
|||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0) {
|
||||
vector3 omega;
|
||||
float phi;
|
||||
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, omega, phi);
|
||||
vector3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,24 +109,32 @@ ccl_device float3 gabor_kernel(GaborParams gp, float3 omega, float phi, float3 p
|
|||
return r * g;
|
||||
}
|
||||
|
||||
ccl_device void gabor_sample(
|
||||
GaborParams gp, float3 cell, int seed, ccl_private float3 *omega, ccl_private float *phi)
|
||||
ccl_device float3 gabor_sample(GaborParams gp, float3 cell, int seed, ccl_private float *phi)
|
||||
{
|
||||
float3 rand_values = hash_float4_to_float3(make_float4(cell.x, cell.y, cell.z, float(seed)));
|
||||
float pvar = mix(0.0f, rand_values.z * 2.0f - 1.0f, gp.phase_variance);
|
||||
float3 rand_values = hash_float4_to_float3(make_float4(cell.x, cell.y, cell.z, float(seed))) *
|
||||
2.0f -
|
||||
1.0f;
|
||||
float pvar = mix(0.0f, rand_values.z, gp.phase_variance);
|
||||
*phi = M_2PI_F * pvar + gp.phase;
|
||||
|
||||
if (gp.anisotropic == 1) { /* ANISO */
|
||||
*omega = gp.direction;
|
||||
return gp.direction;
|
||||
}
|
||||
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 gp.direction * make_float3(cos_omega_t, sin_omega_t, 0.0f);
|
||||
}
|
||||
else { /* ISO */
|
||||
float ovar = M_PI_F * (rand_values.x * 2.0f - 1.0f);
|
||||
float ovar = M_PI_F * (rand_values.x);
|
||||
float omega_t = ovar * gp.rot_variance - gp.rotation;
|
||||
float cos_omega_p = 1.0f - 2.0f * rand_values.y;
|
||||
float cos_omega_p = -rand_values.y;
|
||||
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);
|
||||
*omega = normalize(
|
||||
return normalize(
|
||||
make_float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
|
||||
}
|
||||
}
|
||||
|
@ -146,10 +154,8 @@ ccl_device float3 gabor_cell_3d(GaborParams gp, float3 cell, float3 cell_positio
|
|||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0f) {
|
||||
float3 omega;
|
||||
float phi;
|
||||
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, &omega, &phi);
|
||||
float3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, &phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
@ -172,10 +178,8 @@ ccl_device float3 gabor_cell_2d(GaborParams gp, float3 cell, float3 cell_positio
|
|||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0f) {
|
||||
float3 omega;
|
||||
float phi;
|
||||
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, &omega, &phi);
|
||||
float3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, &phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -423,6 +423,7 @@ typedef enum NodeGaborMode {
|
|||
typedef enum NodeGaborAnisotropic {
|
||||
SHD_GABOR_ISOTROPIC,
|
||||
SHD_GABOR_ANISOTROPIC,
|
||||
SHD_GABOR_HYBRID,
|
||||
} NodeGaborAnisotropic;
|
||||
|
||||
/* Closure */
|
||||
|
|
|
@ -1205,6 +1205,7 @@ NODE_DEFINE(GaborTextureNode)
|
|||
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;
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
* 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.
|
||||
* - Removed Hybrid Anistropic option (can be added back but I find it is non-intuitive).
|
||||
*
|
||||
* Adapted from Open Shading Language implementation.
|
||||
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
|
||||
|
@ -149,30 +148,35 @@ vec3 gabor_kernel(GaborParams gp, vec3 omega, float phi, vec3 position, float dv
|
|||
|
||||
/* Set omega (angular frequency) and phi (phase) for the impulse based on the anisotropic
|
||||
* parameter. Unlike reference papers, Anisotropic is not normalised to provide additional artistic
|
||||
* control. Hybrid mode is not implemented as it seemed unintuituve in testing, it can easily be
|
||||
* added back in. */
|
||||
GaborParams gabor_sample(GaborParams gp, vec3 cell, int seed, out vec3 omega, out float phi)
|
||||
* control and Hybrid mode is non-uniform and does not calculate the length. */
|
||||
vec3 gabor_sample(GaborParams gp, vec3 cell, int seed, out float phi)
|
||||
{
|
||||
vec3 rand_values = hash_vec4_to_vec3(vec4(cell, float(seed)));
|
||||
float pvar = mix(0.0, rand_values.z * 2.0 - 1.0, gp.phase_variance);
|
||||
vec3 rand_values = hash_vec4_to_vec3(vec4(cell, float(seed))) * 2.0 - 1.0;
|
||||
float pvar = mix(0.0, rand_values.z, gp.phase_variance);
|
||||
phi = M_2PI * pvar + gp.phase;
|
||||
|
||||
/* Anisotropic direction. */
|
||||
CharlieJolly marked this conversation as resolved
Outdated
|
||||
if (gp.anisotropic == 1) {
|
||||
omega = gp.direction;
|
||||
/* Reference code returns normalize(gp.direction). */
|
||||
CharlieJolly marked this conversation as resolved
Outdated
Omar Emara
commented
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.
Charlie Jolly
commented
I can add back the Hybrid mode. I can add back the Hybrid mode.
Omar Emara
commented
@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?
Charlie Jolly
commented
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.
Omar Emara
commented
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.
Charlie Jolly
commented
@OmarEmaraDev this is achieved using the Rotation and Rotation Variance controls. @OmarEmaraDev this is achieved using the Rotation and Rotation Variance controls.
![image](/attachments/e4d3e0a6-737b-466c-8c23-2eba581bdebd)
Omar Emara
commented
Is isotropic the same as Is isotropic the same as `Rotation Variance = 360`?
Charlie Jolly
commented
Yes, in OSL implementation we have the angular frequency set to a random number
in this patch this is contolled by Rotation and Rotation Variance.
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;
```
Omar Emara
commented
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?
Charlie Jolly
commented
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.
Omar Emara
commented
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.
Charlie Jolly
commented
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) Node panels patch may help here. Otherwise it is difficult to reduce the params without compromising the features. In comparison to the Principled BSDF. 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)
Iliya Katushenock
commented
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)
Charlie Jolly
commented
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.
Charlie Jolly
commented
The UI has now been grouped using the new Panel feature. The UI has now been grouped using the new Panel feature.
|
||||
return gp.direction;
|
||||
}
|
||||
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 normalize(gp.direction). */
|
||||
return gp.direction * vec3(cos_omega_t, sin_omega_t, 0.0);
|
||||
}
|
||||
else { /* Isotropic. */
|
||||
CharlieJolly marked this conversation as resolved
Outdated
Omar Emara
commented
If this is simply only passed to If this is simply only passed to `gabor_sample`, why not call it in `gabor_sample` directly?
|
||||
float ovar = M_PI * (rand_values.x * 2.0 - 1.0);
|
||||
float ovar = M_PI * (rand_values.x);
|
||||
float omega_t = ovar * gp.rot_variance - gp.rotation;
|
||||
float cos_omega_p = 1.0 - 2.0 * rand_values.y;
|
||||
float cos_omega_p = -rand_values.y;
|
||||
float sin_omega_p = sqrt(1.0 - cos_omega_p * cos_omega_p);
|
||||
CharlieJolly marked this conversation as resolved
Outdated
Omar Emara
commented
The same for the Gaussian componenet, just compute it in The same for the Gaussian componenet, just compute it in `gabor_sample` directly.
Charlie Jolly
commented
Added to Added to `gabor_kernel` function
|
||||
float sin_omega_t = sin(omega_t);
|
||||
float cos_omega_t = cos(omega_t);
|
||||
omega = normalize(vec3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
|
||||
return normalize(vec3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
|
||||
}
|
||||
/* Return GaborParams to workaround gpu_codegen parser warning when return type is set to void :
|
||||
* Unknown parameter type "GaborParams". Known issue: see 111353 */
|
||||
return gp;
|
||||
}
|
||||
|
||||
/* Generate noise based on the cell position and number of impulses. */
|
||||
|
@ -190,10 +194,8 @@ vec3 gabor_cell_3d(GaborParams gp, vec3 cell, vec3 cell_position, int seed)
|
|||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0) {
|
||||
vec3 omega;
|
||||
float phi;
|
||||
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, omega, phi);
|
||||
vec3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
@ -217,10 +219,8 @@ vec3 gabor_cell_2d(GaborParams gp, vec3 cell, vec3 cell_position, int seed)
|
|||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0) {
|
||||
vec3 omega;
|
||||
float phi;
|
||||
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, omega, phi);
|
||||
vec3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2005,6 +2005,7 @@ typedef enum NodeGaborMode {
|
|||
typedef enum NodeGaborAnisotropic {
|
||||
SHD_GABOR_ISOTROPIC,
|
||||
SHD_GABOR_ANISOTROPIC,
|
||||
SHD_GABOR_HYBRID,
|
||||
} NodeGaborAnisotropic;
|
||||
|
||||
/* musgrave texture */
|
||||
|
|
|
@ -5229,6 +5229,7 @@ static void def_sh_tex_gabor(StructRNA *srna)
|
|||
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},
|
||||
};
|
||||
|
||||
|
|
|
@ -284,24 +284,30 @@ static float3 gabor_kernel(const GaborParams gp,
|
|||
return r * g;
|
||||
}
|
||||
|
||||
static void gabor_sample(
|
||||
const GaborParams gp, const float3 cell, const int seed, float3 &omega, float &phi)
|
||||
static float3 gabor_sample(const GaborParams gp, const float3 cell, const int seed, float &phi)
|
||||
{
|
||||
const float3 rand_values = noise::hash_float_to_float3(float4(cell, float(seed)));
|
||||
const float pvar = math::interpolate(0.0f, rand_values.z * 2.0f - 1.0f, gp.phase_variance);
|
||||
const float3 rand_values = noise::hash_float_to_float3(float4(cell, float(seed))) * 2.0f - 1.0f;
|
||||
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 */
|
||||
omega = gp.direction;
|
||||
return gp.direction;
|
||||
}
|
||||
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 gp.direction * float3(cos_omega_t, sin_omega_t, 0.0f);
|
||||
}
|
||||
else { /* ISO */
|
||||
const float ovar = float(M_PI) * (rand_values.x * 2.0f - 1.0f);
|
||||
const float ovar = float(M_PI) * (rand_values.x);
|
||||
const float omega_t = ovar * gp.rot_variance - gp.rotation;
|
||||
const float cos_omega_p = 1.0f - 2.0f * rand_values.y;
|
||||
const float cos_omega_p = -rand_values.y;
|
||||
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);
|
||||
omega = math::normalize(
|
||||
return math::normalize(
|
||||
float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p));
|
||||
}
|
||||
}
|
||||
|
@ -322,9 +328,8 @@ static float3 gabor_cell_3d(GaborParams &gp, float3 cell, float3 cell_position,
|
|||
const float dv = math::dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0f) {
|
||||
float3 omega;
|
||||
float phi;
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, omega, phi);
|
||||
float3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
@ -347,9 +352,8 @@ static float3 gabor_cell_2d(GaborParams &gp, float3 cell, float3 cell_position,
|
|||
const float dv = math::dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0f) {
|
||||
float3 omega;
|
||||
float phi;
|
||||
gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, omega, phi);
|
||||
float3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
|
@ -592,7 +596,7 @@ 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_ISOTROPIC) {
|
||||
if (anistropic != SHD_GABOR_ANISOTROPIC) {
|
||||
builder.single_input<float>("Rotation Lacunarity");
|
||||
}
|
||||
builder.single_input<float>("Bandwidth");
|
||||
|
@ -601,11 +605,11 @@ static mf::Signature gabor_signature(const char *name, const int anistropic)
|
|||
builder.single_input<float>("Phase");
|
||||
builder.single_input<float>("Phase Variance");
|
||||
builder.single_input<float>("Cell Randomness");
|
||||
if (anistropic == SHD_GABOR_ISOTROPIC) {
|
||||
if (anistropic != SHD_GABOR_ANISOTROPIC) {
|
||||
builder.single_input<float>("Rotation");
|
||||
builder.single_input<float>("Rotation Variance");
|
||||
}
|
||||
if (anistropic == SHD_GABOR_ANISOTROPIC) {
|
||||
if (anistropic != SHD_GABOR_ISOTROPIC) {
|
||||
builder.single_input<float3>("Direction");
|
||||
}
|
||||
builder.single_output<float>("Value");
|
||||
|
@ -768,6 +772,87 @@ class AnisotropicGaborNoiseFunction : public mf::MultiFunction {
|
|||
}
|
||||
};
|
||||
|
||||
class HybridGaborNoiseFunction : public mf::MultiFunction {
|
||||
private:
|
||||
int dimensions_;
|
||||
int mode_;
|
||||
int periodic_;
|
||||
int normalize_;
|
||||
|
||||
public:
|
||||
HybridGaborNoiseFunction(int dimensions, int kernel, int periodic, int normalize)
|
||||
: dimensions_(dimensions), mode_(kernel), periodic_(periodic), normalize_(normalize)
|
||||
{
|
||||
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> &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> &bandwidth = params.readonly_single_input<float>(param++, "Bandwidth");
|
||||
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> &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<float3> &direction = params.readonly_single_input<float3>(param++, "Direction");
|
||||
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],
|
||||
rot_lacunarity[i],
|
||||
bandwidth[i],
|
||||
radius[i],
|
||||
impulses[i],
|
||||
phase[i],
|
||||
phase_variance[i],
|
||||
cell_randomness[i],
|
||||
rotation[i],
|
||||
rot_variance[i],
|
||||
dimensions_,
|
||||
mode_,
|
||||
SHD_GABOR_HYBRID,
|
||||
normalize_,
|
||||
periodic_);
|
||||
});
|
||||
}
|
||||
|
||||
ExecutionHints get_execution_hints() const override
|
||||
{
|
||||
ExecutionHints hints;
|
||||
hints.allocates_array = false;
|
||||
hints.min_grain_size = 100;
|
||||
return hints;
|
||||
}
|
||||
};
|
||||
|
||||
static void build_multi_function(NodeMultiFunctionBuilder &builder)
|
||||
{
|
||||
const NodeTexGabor &storage = node_storage(builder.node());
|
||||
|
@ -780,6 +865,10 @@ static void build_multi_function(NodeMultiFunctionBuilder &builder)
|
|||
builder.construct_and_set_matching_fn<AnisotropicGaborNoiseFunction>(
|
||||
storage.dimensions, storage.mode, storage.periodic, storage.normalize);
|
||||
break;
|
||||
case SHD_GABOR_HYBRID:
|
||||
builder.construct_and_set_matching_fn<HybridGaborNoiseFunction>(
|
||||
storage.dimensions, storage.mode, storage.periodic, storage.normalize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
This
if
seems redundant and can be removed.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 assume this is the same for all codebases or just glsl?
This is probably for all backends, not just GLSL.