WIP: Node: Gabor Noise Texture #110802
@ -953,6 +953,18 @@ static ShaderNode *add_node(Scene *scene,
|
||||
get_tex_mapping(gabor, b_texture_mapping);
|
||||
node = gabor;
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeTexGaborF)) {
|
||||
BL::ShaderNodeTexGaborF b_gaborf_node(b_node);
|
||||
GaborFTextureNode *gaborf = graph->create_node<GaborFTextureNode>();
|
||||
gaborf->set_mode((NodeGaborFMode)b_gaborf_node.mode());
|
||||
gaborf->set_dimensions(b_gaborf_node.gabor_dimensions());
|
||||
gaborf->set_periodic(b_gaborf_node.periodic());
|
||||
gaborf->set_use_normalize(b_gaborf_node.normalize());
|
||||
gaborf->set_use_origin_offset(b_gaborf_node.use_origin_offset());
|
||||
BL::TexMapping b_texture_mapping(b_gaborf_node.texture_mapping());
|
||||
get_tex_mapping(gaborf, b_texture_mapping);
|
||||
node = gaborf;
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeTexCoord)) {
|
||||
BL::ShaderNodeTexCoord b_tex_coord_node(b_node);
|
||||
TextureCoordinateNode *tex_coord = graph->create_node<TextureCoordinateNode>();
|
||||
|
@ -163,6 +163,7 @@ set(SRC_KERNEL_SVM_HEADERS
|
||||
svm/wireframe.h
|
||||
svm/wavelength.h
|
||||
svm/gabor.h
|
||||
svm/gaborf.h
|
||||
svm/gamma.h
|
||||
svm/brightness.h
|
||||
svm/geometry.h
|
||||
|
@ -34,6 +34,7 @@ set(SRC_OSL
|
||||
node_float_curve.osl
|
||||
node_fresnel.osl
|
||||
node_gabor_texture.osl
|
||||
node_gaborf_texture.osl
|
||||
node_gamma.osl
|
||||
node_geometry.osl
|
||||
node_glass_bsdf.osl
|
||||
|
466
intern/cycles/kernel/osl/shaders/node_gaborf_texture.osl
Normal file
466
intern/cycles/kernel/osl/shaders/node_gaborf_texture.osl
Normal file
@ -0,0 +1,466 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Gabor Noise
|
||||
*
|
||||
* Based on: Blender patch D287
|
||||
*
|
||||
* Adapted from Open Shading Language
|
||||
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Gabor noise originally based on:
|
||||
* Lagae, A. and Drettakis, G. 2011. Filtering Solid Gabor Noise.
|
||||
*/
|
||||
|
||||
/* See GLSL implementation for code comments. */
|
||||
|
||||
#include "node_hash.h"
|
||||
#include "node_math.h"
|
||||
#include "node_noise.h"
|
||||
#include "stdcycles.h"
|
||||
#include "vector2.h"
|
||||
#include "vector4.h"
|
||||
|
||||
#define vector3 point
|
||||
|
||||
#define GABOR_SEED 1259
|
||||
|
||||
struct GaborParams {
|
||||
float frequency;
|
||||
float radius;
|
||||
float impulses;
|
||||
float phase;
|
||||
float phase_variance;
|
||||
float rotation;
|
||||
float init_rotation;
|
||||
float rot_variance;
|
||||
float tilt_randomness;
|
||||
float cell_randomness;
|
||||
float anisotropy;
|
||||
string mode;
|
||||
vector direction;
|
||||
};
|
||||
|
||||
struct FractalParams {
|
||||
float octaves;
|
||||
float roughness;
|
||||
float scl_lacunarity;
|
||||
float fre_lacunarity;
|
||||
float rot_lacunarity;
|
||||
};
|
||||
|
||||
int impulses_per_cell(vector3 cell, float impulses, int seed)
|
||||
{
|
||||
int n = int(impulses);
|
||||
float rmd = impulses - floor(impulses);
|
||||
if (rmd > 0.0) {
|
||||
float t = hash_vector4_to_float(vector4(cell[0], cell[1], cell[2], float(seed - GABOR_SEED)));
|
||||
if (t <= rmd) {
|
||||
return n + 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
vector3 gabor_kernel(GaborParams gp, point omega, float phi, point position, float dv)
|
||||
{
|
||||
float g = cos(M_PI * sqrt(dv)) * 0.5 + 0.5;
|
||||
vector3 r = vector3(0.0);
|
||||
float h;
|
||||
|
||||
if (gp.mode == "gabor") { /* SHD_GABOR_MODE_GABOR */
|
||||
h = gp.frequency * dot(omega, position) + phi;
|
||||
r = vector3(cos(h), 0.0, 0.0);
|
||||
}
|
||||
else if (gp.mode == "phasor") { /* SHD_GABOR_MODE_PHASOR */
|
||||
h = gp.frequency * dot(omega, position) + phi;
|
||||
r = vector3(cos(h), sin(h), 0.0);
|
||||
}
|
||||
else if (gp.mode == "gabor_cross") { /* SHD_GABOR_MODE_CROSS */
|
||||
h = gp.frequency * length(omega * position) + phi;
|
||||
r = vector3(cos(h), 0.0, 0.0);
|
||||
}
|
||||
else if (gp.mode == "phasor_cross") { /* SHD_GABOR_MODE_PHASOR_CROSS */
|
||||
h = gp.frequency * length(omega * position) + phi;
|
||||
r = vector3(cos(h), sin(h), 0.0);
|
||||
}
|
||||
else if (gp.mode == "gabor_ring") { /* SHD_GABOR_MODE_RING */
|
||||
h = cos(gp.frequency * dot(omega, position) + phi) +
|
||||
cos(gp.frequency * length(position) + phi);
|
||||
r = vector3(h, 0.0, 0.0) * 0.5;
|
||||
;
|
||||
}
|
||||
else if (gp.mode == "phasor_ring") { /* SHD_GABOR_MODE_PHASOR_RING */
|
||||
h = cos(gp.frequency * dot(omega, position) + phi) +
|
||||
cos(gp.frequency * length(position) + phi);
|
||||
float h2 = sin(gp.frequency * dot(omega, position) + phi) +
|
||||
sin(gp.frequency * length(position) + phi);
|
||||
r = vector3(h, h2, 0.0) * 0.5;
|
||||
}
|
||||
else if (gp.mode == "gabor_square") { /* SHD_GABOR_MODE_SQUARE */
|
||||
vector3 positionyxz = vector3(position.y, position.x, position.z);
|
||||
h = cos(gp.frequency * dot(omega, position) + phi) +
|
||||
cos(gp.frequency * dot(omega, positionyxz) + phi);
|
||||
r = vector3(h, 0.0, 0.0) * 0.5;
|
||||
}
|
||||
else if (gp.mode == "phasor_square") {
|
||||
vector3 positionyxz = vector3(position.y, position.x, position.z);
|
||||
h = cos(gp.frequency * dot(omega, position) + phi) +
|
||||
cos(gp.frequency * dot(omega, positionyxz) + phi);
|
||||
float h2 = sin(gp.frequency * dot(omega, position) + phi) +
|
||||
sin(gp.frequency * dot(omega, positionyxz) + phi);
|
||||
r = vector3(h, h2, 0.0) * 0.5;
|
||||
}
|
||||
|
||||
return r * g;
|
||||
}
|
||||
|
||||
vector gabor_sample(GaborParams gp, vector3 cell, int seed, output float phi)
|
||||
{
|
||||
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;
|
||||
|
||||
float omega_t = M_PI * (rand_values.x) * gp.rot_variance - gp.rotation - gp.init_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(transform(euler_to_mat(vector3(0.0, 0.0, -gp.rotation)), gp.direction)),
|
||||
gp.anisotropy);
|
||||
}
|
||||
|
||||
vector3 gabor_cell_3d(output GaborParams gp, point cell, point cell_position, int seed)
|
||||
{
|
||||
int num_impulses = impulses_per_cell(cell, gp.impulses, seed);
|
||||
vector3 sum = vector3(0.0);
|
||||
for (int i = 0; i < num_impulses; ++i) {
|
||||
vector3 rand_position = mix(
|
||||
vector3(0.0),
|
||||
hash_vector4_to_color(vector4(cell[0], cell[1], cell[2], float(seed + i * GABOR_SEED))),
|
||||
gp.cell_randomness);
|
||||
|
||||
point kernel_position = (cell_position - rand_position);
|
||||
|
||||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0) {
|
||||
float phi;
|
||||
vector3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
vector3 gabor_cell_2d(output GaborParams gp, point cell, point cell_position, int seed)
|
||||
{
|
||||
int num_impulses = impulses_per_cell(cell, gp.impulses, seed);
|
||||
vector3 sum = vector3(0.0);
|
||||
for (int i = 0; i < num_impulses; ++i) {
|
||||
vector3 rand_position = mix(
|
||||
vector3(0.0),
|
||||
hash_vector4_to_color(vector4(cell[0], cell[1], cell[2], float(seed + i * GABOR_SEED))),
|
||||
gp.cell_randomness);
|
||||
rand_position.z = 0.0;
|
||||
|
||||
point kernel_position = (cell_position - rand_position);
|
||||
|
||||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0) {
|
||||
float phi;
|
||||
vector3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float gabor_coord_wrap(float a, float b)
|
||||
{
|
||||
return (b != 0.0) ? a - b * floor(a / b) : 0.0;
|
||||
}
|
||||
|
||||
vector3 gabor_grid_3d(output GaborParams gp, point p, float scale, int periodic, int seed)
|
||||
{
|
||||
point coords = p * scale;
|
||||
point position = floor(coords);
|
||||
point local_position = coords - position;
|
||||
|
||||
vector3 sum = 0.0;
|
||||
for (int k = -1; k <= 1; k++) {
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
point cell_offset = point(i, j, k);
|
||||
point cell = position + cell_offset;
|
||||
point cell_position = local_position - cell_offset;
|
||||
|
||||
/* Skip this cell if it's too far away to contribute - Bruemmer.osl */
|
||||
point Pr = (point(i > 0, j > 0, k > 0) - local_position) * cell_offset;
|
||||
if (dot(Pr, Pr) >= 1.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (periodic == 1) {
|
||||
cell[0] = gabor_coord_wrap(cell[0], scale);
|
||||
cell[1] = gabor_coord_wrap(cell[1], scale);
|
||||
cell[2] = gabor_coord_wrap(cell[2], scale);
|
||||
}
|
||||
|
||||
sum += gabor_cell_3d(gp, cell, cell_position, seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
vector3 gabor_grid_2d(output GaborParams gp, point p, float scale, int periodic, int seed)
|
||||
{
|
||||
point coords = point(p.x, p.y, 0.0) * scale;
|
||||
point position = floor(coords);
|
||||
point local_position = coords - position;
|
||||
|
||||
vector3 sum = 0.0;
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
point cell_offset = point(i, j, 0.0);
|
||||
point cell = position + cell_offset;
|
||||
point cell_position = local_position - cell_offset;
|
||||
|
||||
/* Skip this cell if it's too far away to contribute - Bruemmer.osl */
|
||||
point Pr = (point(i > 0, j > 0, 0) - local_position) * cell_offset;
|
||||
if (dot(Pr, Pr) >= 1.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (periodic == 1) {
|
||||
cell[0] = gabor_coord_wrap(cell[0], scale);
|
||||
cell[1] = gabor_coord_wrap(cell[1], scale);
|
||||
}
|
||||
|
||||
sum += gabor_cell_2d(gp, cell, cell_position, seed);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float gabor_fractal_noise(FractalParams fp,
|
||||
output GaborParams gp,
|
||||
vector3 p,
|
||||
float scale,
|
||||
string dimensions,
|
||||
int periodic,
|
||||
int use_origin_offset)
|
||||
{
|
||||
float fscale = 1.0;
|
||||
float amp = 1.0;
|
||||
float maxamp = 0.0;
|
||||
vector3 sum = 0.0;
|
||||
float octaves = clamp(fp.octaves, 0.0, 15.0);
|
||||
if (fp.roughness == 0.0) {
|
||||
octaves = 0.0;
|
||||
}
|
||||
int n = int(octaves);
|
||||
for (int i = 0; i <= n; i++) {
|
||||
int seed = use_origin_offset * i * GABOR_SEED;
|
||||
vector3 t = (dimensions == "3D") ? gabor_grid_3d(gp, fscale * p, scale, periodic, seed) :
|
||||
gabor_grid_2d(gp, fscale * p, scale, periodic, seed);
|
||||
gp.frequency *= fp.fre_lacunarity;
|
||||
gp.rotation -= fp.rot_lacunarity;
|
||||
sum += t * amp;
|
||||
maxamp += amp;
|
||||
amp *= fp.roughness;
|
||||
fscale *= fp.scl_lacunarity;
|
||||
}
|
||||
float rmd = octaves - floor(octaves);
|
||||
if (rmd != 0.0) {
|
||||
int seed = use_origin_offset * (n + 1) * GABOR_SEED;
|
||||
vector3 t = (dimensions == "3D") ? gabor_grid_3d(gp, fscale * p, scale, periodic, seed) :
|
||||
gabor_grid_2d(gp, fscale * p, scale, periodic, seed);
|
||||
vector3 sum2 = sum + t * amp;
|
||||
sum = mix(sum, sum2, rmd);
|
||||
}
|
||||
sum /= maxamp;
|
||||
|
||||
if (gp.mode == "phasor" || gp.mode == "phasor_ring" || gp.mode == "phasor_cross" ||
|
||||
gp.mode == "phasor_square")
|
||||
{
|
||||
float pn = atan2(sum.y, sum.x) / M_PI;
|
||||
return pn;
|
||||
}
|
||||
else {
|
||||
return sum.x;
|
||||
}
|
||||
}
|
||||
|
||||
GaborParams gabor_parameters(vector direction,
|
||||
float frequency,
|
||||
float radius,
|
||||
float impulses,
|
||||
float phase,
|
||||
float phase_variance,
|
||||
float rotation,
|
||||
float rot_variance,
|
||||
float tilt_randomness,
|
||||
float cell_randomness,
|
||||
float anisotropy,
|
||||
string mode)
|
||||
{
|
||||
GaborParams gp;
|
||||
gp.impulses = clamp(impulses, 0.0001, 32.0);
|
||||
gp.rot_variance = rot_variance;
|
||||
gp.anisotropy = anisotropy;
|
||||
gp.mode = mode;
|
||||
gp.direction = direction;
|
||||
gp.phase = phase;
|
||||
gp.rotation = 0.0;
|
||||
gp.init_rotation = rotation;
|
||||
gp.phase_variance = phase_variance;
|
||||
gp.tilt_randomness = tilt_randomness;
|
||||
gp.cell_randomness = cell_randomness;
|
||||
gp.radius = radius;
|
||||
gp.frequency = frequency * M_PI;
|
||||
return gp;
|
||||
}
|
||||
|
||||
/* Shader */
|
||||
|
||||
float gabor_noise(point p,
|
||||
vector direction,
|
||||
float scale,
|
||||
float frequency,
|
||||
float detail,
|
||||
float roughness,
|
||||
float scl_lacunarity,
|
||||
float fre_lacunarity,
|
||||
float rot_lacunarity,
|
||||
float gain,
|
||||
float radius,
|
||||
float impulses,
|
||||
float phase,
|
||||
float phase_variance,
|
||||
float rotation,
|
||||
float rot_variance,
|
||||
float tilt_randomness,
|
||||
float cell_randomness,
|
||||
float anisotropy,
|
||||
string dimensions,
|
||||
string mode,
|
||||
int use_normalize,
|
||||
int periodic,
|
||||
int use_origin_offset)
|
||||
{
|
||||
if (impulses == 0.0 || gain == 0.0 || radius <= 0.0 || scale == 0.0) {
|
||||
if (use_normalize == 1) {
|
||||
return 0.5;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
FractalParams fp;
|
||||
fp.roughness = roughness;
|
||||
fp.octaves = detail;
|
||||
fp.scl_lacunarity = scl_lacunarity;
|
||||
fp.fre_lacunarity = fre_lacunarity;
|
||||
fp.rot_lacunarity = rot_lacunarity;
|
||||
|
||||
GaborParams gp = gabor_parameters(direction,
|
||||
frequency,
|
||||
radius,
|
||||
impulses,
|
||||
phase,
|
||||
phase_variance,
|
||||
rotation,
|
||||
rot_variance,
|
||||
tilt_randomness,
|
||||
cell_randomness,
|
||||
anisotropy,
|
||||
mode);
|
||||
|
||||
float g = gabor_fractal_noise(fp, gp, p, scale, dimensions, periodic, use_origin_offset) * gain;
|
||||
|
||||
if (gp.mode == "gabor" || gp.mode == "gabor_ring" || gp.mode == "gabor_cross" ||
|
||||
gp.mode == "gabor_square")
|
||||
{
|
||||
float impulse_scale = 1.2613446229;
|
||||
if (impulses > 1.0) {
|
||||
impulse_scale = 1.2613446229 * sqrt(gp.impulses);
|
||||
}
|
||||
g = g / impulse_scale;
|
||||
}
|
||||
|
||||
if (use_normalize == 1) {
|
||||
return clamp(0.5 * g + 0.5, 0.0, 1.0);
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
/* Gabor */
|
||||
|
||||
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",
|
||||
int use_normalize = 1,
|
||||
int periodic = 0,
|
||||
int use_origin_offset = 1,
|
||||
vector3 Vector = vector(0, 0, 0),
|
||||
float Scale = 5.0,
|
||||
float Detail = 0.0,
|
||||
float Roughness = 0.5,
|
||||
float ScaleLacunarity = 2.0,
|
||||
float FrequencyLacunarity = 2.0,
|
||||
float RotationLacunarity = 0.0,
|
||||
float Gain = 1.0,
|
||||
float Frequency = 4.0,
|
||||
float Radius = 1.0,
|
||||
float Impulses = 2.0,
|
||||
float PhaseOffset = 0.0,
|
||||
float PhaseVariance = 1.0,
|
||||
float CellRandomness = 1.0,
|
||||
float Rotation = 0.0,
|
||||
float RotationVariance = 0.0,
|
||||
float TiltRandomness = 1.0,
|
||||
vector3 Direction = vector(0, 0, 1),
|
||||
float AnisotropicFactor = 0.0,
|
||||
output float Value = 0.0)
|
||||
{
|
||||
vector3 p = Vector;
|
||||
|
||||
if (use_mapping)
|
||||
p = transform(mapping, p);
|
||||
|
||||
Value = gabor_noise(p,
|
||||
Direction,
|
||||
Scale,
|
||||
Frequency,
|
||||
Detail,
|
||||
Roughness,
|
||||
ScaleLacunarity,
|
||||
FrequencyLacunarity,
|
||||
RotationLacunarity,
|
||||
Gain,
|
||||
Radius,
|
||||
Impulses,
|
||||
PhaseOffset,
|
||||
PhaseVariance,
|
||||
Rotation,
|
||||
RotationVariance,
|
||||
TiltRandomness,
|
||||
CellRandomness,
|
||||
AnisotropicFactor,
|
||||
dimensions,
|
||||
mode,
|
||||
use_normalize,
|
||||
periodic,
|
||||
use_origin_offset);
|
||||
}
|
||||
|
||||
#undef vector3
|
487
intern/cycles/kernel/svm/gaborf.h
Normal file
487
intern/cycles/kernel/svm/gaborf.h
Normal file
@ -0,0 +1,487 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Gabor Noise
|
||||
*
|
||||
* Based on: Blender patch D287
|
||||
*
|
||||
* Adapted from Open Shading Language
|
||||
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Gabor noise originally based on:
|
||||
* Lagae, A. and Drettakis, G. 2011. Filtering Solid Gabor Noise.
|
||||
*/
|
||||
|
||||
/* See GLSL implementation for code comments. */
|
||||
|
||||
#define GABOR_SEED 1259
|
||||
|
||||
typedef struct GaborParams {
|
||||
float frequency;
|
||||
float radius;
|
||||
float impulses;
|
||||
float phase;
|
||||
float phase_variance;
|
||||
float rotation;
|
||||
float init_rotation;
|
||||
float rot_variance;
|
||||
float tilt_randomness;
|
||||
float cell_randomness;
|
||||
float anisotropy;
|
||||
int mode;
|
||||
float3 direction;
|
||||
} GaborParams;
|
||||
|
||||
typedef struct FractalParams {
|
||||
float octaves;
|
||||
float roughness;
|
||||
float scl_lacunarity;
|
||||
float fre_lacunarity;
|
||||
float rot_lacunarity;
|
||||
} FractalParams;
|
||||
|
||||
ccl_device int impulses_per_cell(float3 cell, float impulses, int seed)
|
||||
{
|
||||
int n = int(impulses);
|
||||
float rmd = impulses - floorf(impulses);
|
||||
if (rmd > 0.0f) {
|
||||
float t = hash_float4_to_float(make_float4(cell.x, cell.y, cell.z, float(seed - GABOR_SEED)));
|
||||
return (t <= rmd) ? n + 1 : n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
ccl_device float3 gabor_kernel(GaborParams gp, float3 omega, float phi, float3 position, float dv)
|
||||
{
|
||||
float g = cosf(M_PI_F * sqrtf(dv)) * 0.5f + 0.5f;
|
||||
float3 r = zero_float3();
|
||||
float h;
|
||||
|
||||
if (gp.mode == SHD_GABOR_MODE_GABOR) {
|
||||
h = gp.frequency * dot(omega, position) + phi;
|
||||
r = make_float3(cosf(h), 0.0f, 0.0f);
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_PHASOR) {
|
||||
h = gp.frequency * dot(omega, position) + phi;
|
||||
r = make_float3(cosf(h), sin(h), 0.0f);
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_CROSS) {
|
||||
h = gp.frequency * len(omega * position) + phi;
|
||||
r = make_float3(cosf(h), 0.0f, 0.0f);
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_PHASOR_CROSS) {
|
||||
h = gp.frequency * len(omega * position) + phi;
|
||||
r = make_float3(cosf(h), sinf(h), 0.0f);
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_RING) {
|
||||
h = cosf(gp.frequency * dot(omega, position) + phi) + cosf(gp.frequency * len(position) + phi);
|
||||
r = make_float3(h, 0.0f, 0.0f) * 0.5f;
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_PHASOR_RING) {
|
||||
h = cosf(gp.frequency * dot(omega, position) + phi) + cosf(gp.frequency * len(position) + phi);
|
||||
const float h2 = sinf(gp.frequency * dot(omega, position) + phi) +
|
||||
sinf(gp.frequency * len(position) + phi);
|
||||
r = make_float3(h, h2, 0.0f) * 0.5f;
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_SQUARE) {
|
||||
const float3 positionyxz = make_float3(position.y, position.x, position.z);
|
||||
h = cosf(gp.frequency * dot(omega, position) + phi) +
|
||||
cosf(gp.frequency * dot(omega, positionyxz) + phi);
|
||||
r = make_float3(h, 0.0f, 0.0f) * 0.5f;
|
||||
}
|
||||
else if (gp.mode == SHD_GABOR_MODE_PHASOR_SQUARE) {
|
||||
const float3 positionyxz = make_float3(position.y, position.x, position.z);
|
||||
h = cosf(gp.frequency * dot(omega, position) + phi) +
|
||||
cosf(gp.frequency * dot(omega, positionyxz) + phi);
|
||||
const float h2 = sinf(gp.frequency * dot(omega, position) + phi) +
|
||||
sinf(gp.frequency * dot(omega, positionyxz) + phi);
|
||||
r = make_float3(h, h2, 0.0f) * 0.5f;
|
||||
}
|
||||
|
||||
return r * g;
|
||||
}
|
||||
|
||||
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))) *
|
||||
2.0f -
|
||||
1.0f;
|
||||
float pvar = mix(0.0f, rand_values.z, gp.phase_variance);
|
||||
*phi = M_2PI_F * pvar + gp.phase;
|
||||
|
||||
float omega_t = M_PI_F * (rand_values.x) * gp.rot_variance - gp.rotation - gp.init_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 = sinf(omega_t);
|
||||
float cos_omega_t = cosf(omega_t);
|
||||
|
||||
Transform rotationTransform = euler_to_transform(make_float3(0.0f, 0.0f, -gp.rotation));
|
||||
return mix(
|
||||
normalize(make_float3(cos_omega_t * sin_omega_p, sin_omega_t * sin_omega_p, cos_omega_p)),
|
||||
normalize(transform_direction(&rotationTransform, gp.direction)),
|
||||
gp.anisotropy);
|
||||
}
|
||||
|
||||
ccl_device float3 gabor_cell_3d(GaborParams gp, float3 cell, float3 cell_position, int seed)
|
||||
{
|
||||
int num_impulses = impulses_per_cell(cell, gp.impulses, seed);
|
||||
float3 sum = zero_float3();
|
||||
for (int i = 0; i < num_impulses; ++i) {
|
||||
float3 rand_position = mix(
|
||||
zero_float3(),
|
||||
hash_float4_to_float3(make_float4(cell.x, cell.y, cell.z, float(seed + i * GABOR_SEED))),
|
||||
gp.cell_randomness);
|
||||
|
||||
float3 kernel_position = (cell_position - rand_position);
|
||||
|
||||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0f) {
|
||||
float phi;
|
||||
float3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, &phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
ccl_device float3 gabor_cell_2d(GaborParams gp, float3 cell, float3 cell_position, int seed)
|
||||
{
|
||||
int num_impulses = impulses_per_cell(cell, gp.impulses, seed);
|
||||
float3 sum = zero_float3();
|
||||
for (int i = 0; i < num_impulses; ++i) {
|
||||
float3 rand_position = mix(
|
||||
zero_float3(),
|
||||
hash_float4_to_float3(make_float4(cell.x, cell.y, cell.z, float(seed + i * GABOR_SEED))),
|
||||
gp.cell_randomness);
|
||||
rand_position.z = 0.0f;
|
||||
|
||||
float3 kernel_position = (cell_position - rand_position);
|
||||
|
||||
float dv = dot(kernel_position, kernel_position) / gp.radius;
|
||||
|
||||
if (dv <= 1.0f) {
|
||||
float phi;
|
||||
float3 omega = gabor_sample(gp, cell, seed + (num_impulses + i) * GABOR_SEED, &phi);
|
||||
sum += gabor_kernel(gp, omega, phi, kernel_position, dv);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
ccl_device float gabor_coord_wrap(float a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? a - b * floorf(a / b) : 0.0f;
|
||||
}
|
||||
|
||||
ccl_device float3 gabor_grid_3d(GaborParams gp, float3 p, float scale, int periodic, int seed)
|
||||
{
|
||||
float3 coords = p * scale;
|
||||
float3 position = floor(coords);
|
||||
float3 local_position = coords - position;
|
||||
|
||||
float3 sum = zero_float3();
|
||||
for (int k = -1; k <= 1; k++) {
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
float3 cell_offset = make_float3(i, j, k);
|
||||
float3 cell = position + cell_offset;
|
||||
float3 cell_position = local_position - cell_offset;
|
||||
|
||||
/* Skip this cell if it's too far away to contribute - Bruemmer.osl */
|
||||
float3 Pr = (make_float3(i > 0, j > 0, k > 0) - local_position) * cell_offset;
|
||||
if (dot(Pr, Pr) >= 1.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (periodic) {
|
||||
cell.x = gabor_coord_wrap(cell.x, scale);
|
||||
cell.y = gabor_coord_wrap(cell.y, scale);
|
||||
cell.z = gabor_coord_wrap(cell.z, scale);
|
||||
}
|
||||
|
||||
sum += gabor_cell_3d(gp, cell, cell_position, seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
ccl_device float3 gabor_grid_2d(GaborParams gp, float3 p, float scale, int periodic, int seed)
|
||||
{
|
||||
float3 coords = make_float3(p.x, p.y, 0.0f) * scale;
|
||||
float3 position = floor(coords);
|
||||
float3 local_position = coords - position;
|
||||
|
||||
float3 sum = zero_float3();
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
float3 cell_offset = make_float3(i, j, 0.0f);
|
||||
float3 cell = position + cell_offset;
|
||||
float3 cell_position = local_position - cell_offset;
|
||||
|
||||
/* Skip this cell if it's too far away to contribute - Bruemmer.osl */
|
||||
float3 Pr = (make_float3(i > 0, j > 0, 0) - local_position) * cell_offset;
|
||||
if (dot(Pr, Pr) >= 1.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (periodic) {
|
||||
cell.x = gabor_coord_wrap(cell.x, scale);
|
||||
cell.y = gabor_coord_wrap(cell.y, scale);
|
||||
}
|
||||
|
||||
sum += gabor_cell_2d(gp, cell, cell_position, seed);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
ccl_device float gabor_fractal_noise(FractalParams fp,
|
||||
GaborParams gp,
|
||||
float3 p,
|
||||
float scale,
|
||||
int dimensions,
|
||||
int periodic,
|
||||
int use_origin_offset)
|
||||
{
|
||||
float fscale = 1.0f;
|
||||
float amp = 1.0f;
|
||||
float maxamp = 0.0f;
|
||||
float3 sum = zero_float3();
|
||||
float octaves = clamp(fp.octaves, 0.0f, 15.0f);
|
||||
if (fp.roughness == 0.0f) {
|
||||
octaves = 0.0f;
|
||||
}
|
||||
int n = int(octaves);
|
||||
for (int i = 0; i <= n; i++) {
|
||||
int seed = use_origin_offset * i * GABOR_SEED;
|
||||
float3 t = (dimensions == 3) ? gabor_grid_3d(gp, fscale * p, scale, periodic, seed) :
|
||||
gabor_grid_2d(gp, fscale * p, scale, periodic, seed);
|
||||
gp.frequency *= fp.fre_lacunarity;
|
||||
gp.rotation -= fp.rot_lacunarity;
|
||||
sum += t * amp;
|
||||
maxamp += amp;
|
||||
amp *= fp.roughness;
|
||||
fscale *= fp.scl_lacunarity;
|
||||
}
|
||||
float rmd = octaves - floorf(octaves);
|
||||
if (rmd != 0.0f) {
|
||||
int seed = use_origin_offset * (n + 1) * GABOR_SEED;
|
||||
float3 t = (dimensions == 3) ? gabor_grid_3d(gp, fscale * p, scale, periodic, seed) :
|
||||
gabor_grid_2d(gp, fscale * p, scale, periodic, seed);
|
||||
float3 sum2 = sum + t * amp;
|
||||
sum = mix(sum, sum2, rmd);
|
||||
}
|
||||
sum /= maxamp;
|
||||
|
||||
if (gp.mode == SHD_GABOR_MODE_PHASOR || gp.mode == SHD_GABOR_MODE_PHASOR_RING ||
|
||||
gp.mode == SHD_GABOR_MODE_PHASOR_CROSS || gp.mode == SHD_GABOR_MODE_PHASOR_SQUARE)
|
||||
{
|
||||
float pn = atan2f(sum.y, sum.x) / M_PI_F;
|
||||
return pn;
|
||||
}
|
||||
else {
|
||||
return sum.x;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device GaborParams gabor_parameters(float3 direction,
|
||||
float frequency,
|
||||
float radius,
|
||||
float impulses,
|
||||
float phase,
|
||||
float phase_variance,
|
||||
float rotation,
|
||||
float rot_variance,
|
||||
float tilt_randomness,
|
||||
float cell_randomness,
|
||||
float anisotropy,
|
||||
int mode)
|
||||
{
|
||||
GaborParams gp;
|
||||
gp.impulses = clamp(impulses, 0.0001f, 32.0f);
|
||||
gp.rot_variance = rot_variance;
|
||||
gp.anisotropy = anisotropy;
|
||||
gp.mode = mode;
|
||||
gp.direction = direction;
|
||||
gp.phase = phase;
|
||||
gp.rotation = 0.0f;
|
||||
gp.init_rotation = rotation;
|
||||
gp.phase_variance = phase_variance;
|
||||
gp.tilt_randomness = tilt_randomness;
|
||||
gp.cell_randomness = cell_randomness;
|
||||
gp.radius = radius;
|
||||
gp.frequency = frequency * M_PI_F;
|
||||
return gp;
|
||||
}
|
||||
|
||||
/* Gabor shader */
|
||||
|
||||
ccl_device float gabor_noise(float3 p,
|
||||
float3 direction,
|
||||
float scale,
|
||||
float frequency,
|
||||
float detail,
|
||||
float roughness,
|
||||
float scl_lacunarity,
|
||||
float fre_lacunarity,
|
||||
float rot_lacunarity,
|
||||
float gain,
|
||||
float radius,
|
||||
float impulses,
|
||||
float phase,
|
||||
float phase_variance,
|
||||
float rotation,
|
||||
float rot_variance,
|
||||
float tilt_randomness,
|
||||
float cell_randomness,
|
||||
float anisotropy,
|
||||
int dimensions,
|
||||
int mode,
|
||||
int normalize,
|
||||
int periodic,
|
||||
int use_origin_offset)
|
||||
{
|
||||
if (impulses == 0.0f || gain == 0.0f || radius <= 0.0f || scale == 0.0f) {
|
||||
return (normalize == 1) ? 0.5f : 0.0f;
|
||||
}
|
||||
|
||||
FractalParams fp;
|
||||
fp.roughness = roughness;
|
||||
fp.octaves = detail;
|
||||
fp.scl_lacunarity = scl_lacunarity;
|
||||
fp.fre_lacunarity = fre_lacunarity;
|
||||
fp.rot_lacunarity = rot_lacunarity;
|
||||
|
||||
GaborParams gp = gabor_parameters(direction,
|
||||
frequency,
|
||||
radius,
|
||||
impulses,
|
||||
phase,
|
||||
phase_variance,
|
||||
rotation,
|
||||
rot_variance,
|
||||
tilt_randomness,
|
||||
cell_randomness,
|
||||
anisotropy,
|
||||
mode);
|
||||
|
||||
float g = gabor_fractal_noise(fp, gp, p, scale, dimensions, periodic, use_origin_offset) * gain;
|
||||
|
||||
if (gp.mode == SHD_GABOR_MODE_GABOR || gp.mode == SHD_GABOR_MODE_RING ||
|
||||
gp.mode == SHD_GABOR_MODE_CROSS || gp.mode == SHD_GABOR_MODE_SQUARE)
|
||||
{
|
||||
float impulse_scale = impulses > 1.0f ? 1.2613446229f * sqrt(gp.impulses) : 1.2613446229f;
|
||||
g = g / impulse_scale;
|
||||
}
|
||||
|
||||
if (normalize == 1) {
|
||||
return clamp(0.5f * g + 0.5f, 0.0f, 1.0f);
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
ccl_device_noinline int svm_node_tex_gabor(
|
||||
KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset)
|
||||
{
|
||||
uint4 node2 = read_node(kg, &offset);
|
||||
uint4 defaults_node3 = read_node(kg, &offset);
|
||||
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,
|
||||
tilt_randomness_offset, cell_randomness_offset;
|
||||
uint scl_lacunarity_offset, use_normalize_offset, dimension_offset, rot_lacunarity_offset;
|
||||
uint frequency_offset, radius_offset;
|
||||
|
||||
svm_unpack_node_uchar4(
|
||||
node.y, &vector_in_offset, &scale_offset, &frequency_offset, &detail_offset);
|
||||
svm_unpack_node_uchar4(node.z,
|
||||
&roughness_offset,
|
||||
&scl_lacunarity_offset,
|
||||
&fre_lacunarity_offset,
|
||||
&rot_lacunarity_offset);
|
||||
svm_unpack_node_uchar4(node.w, &gain_offset, &radius_offset, &impulse_offset, &phase_offset);
|
||||
|
||||
svm_unpack_node_uchar4(node2.x,
|
||||
&phase_variance_offset,
|
||||
&cell_randomness_offset,
|
||||
&rotation_offset,
|
||||
&tilt_randomness_offset);
|
||||
svm_unpack_node_uchar4(
|
||||
node2.y, &rot_variance_offset, &direction_offset, &value_out_offset, &dimension_offset);
|
||||
svm_unpack_node_uchar4(
|
||||
node2.z, &mode_offset, &aniso_offset, &periodic_offset, &use_normalize_offset);
|
||||
|
||||
float3 vector_in = stack_load_float3(stack, vector_in_offset);
|
||||
|
||||
float scale = stack_load_float_default(stack, scale_offset, defaults_node3.x);
|
||||
float frequency = stack_load_float_default(stack, frequency_offset, defaults_node3.y);
|
||||
float detail = stack_load_float_default(stack, detail_offset, defaults_node3.z);
|
||||
float roughness = stack_load_float_default(stack, roughness_offset, defaults_node3.w);
|
||||
|
||||
float scl_lacunarity = stack_load_float_default(stack, scl_lacunarity_offset, defaults_node4.x);
|
||||
float fre_lacunarity = stack_load_float_default(stack, fre_lacunarity_offset, defaults_node4.y);
|
||||
float rot_lacunarity = stack_load_float_default(stack, rot_lacunarity_offset, defaults_node4.z);
|
||||
float gain = stack_load_float_default(stack, gain_offset, defaults_node4.w);
|
||||
|
||||
float radius = stack_load_float_default(stack, radius_offset, defaults_node5.x);
|
||||
float impulses = stack_load_float_default(stack, impulse_offset, defaults_node5.y);
|
||||
float phase = stack_load_float_default(stack, phase_offset, defaults_node5.z);
|
||||
float phase_variance = stack_load_float_default(stack, phase_variance_offset, defaults_node5.w);
|
||||
|
||||
float cell_randomness = stack_load_float_default(
|
||||
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 tilt_randomness = stack_load_float_default(
|
||||
stack, tilt_randomness_offset, defaults_node6.w);
|
||||
float anisotropy = stack_load_float_default(stack, aniso_offset, defaults_node7.x);
|
||||
|
||||
float3 direction = stack_load_float3(stack, direction_offset);
|
||||
|
||||
if (stack_valid(value_out_offset)) {
|
||||
float value = gabor_noise(vector_in,
|
||||
direction,
|
||||
scale,
|
||||
frequency,
|
||||
detail,
|
||||
roughness,
|
||||
scl_lacunarity,
|
||||
fre_lacunarity,
|
||||
rot_lacunarity,
|
||||
gain,
|
||||
radius,
|
||||
impulses,
|
||||
phase,
|
||||
phase_variance,
|
||||
rotation,
|
||||
rot_variance,
|
||||
tilt_randomness,
|
||||
cell_randomness,
|
||||
anisotropy,
|
||||
dimension_offset,
|
||||
mode_offset,
|
||||
use_normalize_offset,
|
||||
periodic_offset,
|
||||
int(node2.w));
|
||||
stack_store_float(stack, value_out_offset, value);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
@ -70,6 +70,7 @@ SHADER_NODE_TYPE(NODE_TEX_SKY)
|
||||
SHADER_NODE_TYPE(NODE_TEX_GRADIENT)
|
||||
SHADER_NODE_TYPE(NODE_TEX_VORONOI)
|
||||
SHADER_NODE_TYPE(NODE_TEX_GABOR)
|
||||
SHADER_NODE_TYPE(NODE_TEX_GABORF)
|
||||
SHADER_NODE_TYPE(NODE_TEX_WAVE)
|
||||
SHADER_NODE_TYPE(NODE_TEX_MAGIC)
|
||||
SHADER_NODE_TYPE(NODE_TEX_CHECKER)
|
||||
|
@ -166,6 +166,7 @@ CCL_NAMESPACE_END
|
||||
#include "kernel/svm/displace.h"
|
||||
#include "kernel/svm/fresnel.h"
|
||||
#include "kernel/svm/gabor.h"
|
||||
#include "kernel/svm/gaborf.h"
|
||||
#include "kernel/svm/gamma.h"
|
||||
#include "kernel/svm/geometry.h"
|
||||
#include "kernel/svm/gradient.h"
|
||||
@ -484,6 +485,9 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
|
||||
SVM_CASE(NODE_TEX_GABOR)
|
||||
offset = svm_node_tex_gabor(kg, sd, stack, node.y, node.z, node.w, offset);
|
||||
break;
|
||||
SVM_CASE(NODE_TEX_GABORF)
|
||||
offset = svm_node_tex_gabor(kg, sd, stack, node, offset);
|
||||
break;
|
||||
SVM_CASE(NODE_TEX_WAVE)
|
||||
offset = svm_node_tex_wave(kg, sd, stack, node, offset);
|
||||
break;
|
||||
|
@ -281,6 +281,17 @@ typedef enum NodeGaborType {
|
||||
NODE_GABOR_TYPE_3D,
|
||||
} NodeGaborType;
|
||||
|
||||
typedef enum NodeGaborFMode {
|
||||
SHD_GABOR_MODE_GABOR,
|
||||
SHD_GABOR_MODE_RING,
|
||||
SHD_GABOR_MODE_CROSS,
|
||||
SHD_GABOR_MODE_SQUARE,
|
||||
SHD_GABOR_MODE_PHASOR,
|
||||
SHD_GABOR_MODE_PHASOR_RING,
|
||||
SHD_GABOR_MODE_PHASOR_CROSS,
|
||||
SHD_GABOR_MODE_PHASOR_SQUARE,
|
||||
} NodeGaborFMode;
|
||||
|
||||
typedef enum NodeWaveType { NODE_WAVE_BANDS, NODE_WAVE_RINGS } NodeWaveType;
|
||||
|
||||
typedef enum NodeWaveBandsDirection {
|
||||
|
@ -1299,6 +1299,179 @@ void GaborTextureNode::compile(OSLCompiler &compiler)
|
||||
compiler.add(this, "node_gabor_texture");
|
||||
}
|
||||
|
||||
/* GaborF Texture */
|
||||
|
||||
NODE_DEFINE(GaborFTextureNode)
|
||||
{
|
||||
NodeType *type = NodeType::add("gaborf_texture", create, NodeType::SHADER);
|
||||
|
||||
TEXTURE_MAPPING_DEFINE(GaborFTextureNode);
|
||||
|
||||
static NodeEnum dimensions_enum;
|
||||
dimensions_enum.insert("2D", 2);
|
||||
dimensions_enum.insert("3D", 3);
|
||||
SOCKET_ENUM(dimensions, "Dimensions", dimensions_enum, 2);
|
||||
|
||||
static NodeEnum mode_enum;
|
||||
mode_enum.insert("gabor", SHD_GABOR_MODE_GABOR);
|
||||
mode_enum.insert("gabor_ring", SHD_GABOR_MODE_RING);
|
||||
mode_enum.insert("gabor_cross", SHD_GABOR_MODE_CROSS);
|
||||
mode_enum.insert("gabor_square", SHD_GABOR_MODE_SQUARE);
|
||||
mode_enum.insert("phasor", SHD_GABOR_MODE_PHASOR);
|
||||
mode_enum.insert("phasor_ring", SHD_GABOR_MODE_PHASOR_RING);
|
||||
mode_enum.insert("phasor_cross", SHD_GABOR_MODE_PHASOR_CROSS);
|
||||
mode_enum.insert("phasor_square", SHD_GABOR_MODE_PHASOR_SQUARE);
|
||||
SOCKET_ENUM(mode, "Mode", mode_enum, SHD_GABOR_MODE_GABOR);
|
||||
|
||||
SOCKET_BOOLEAN(periodic, "Periodic", false);
|
||||
SOCKET_BOOLEAN(use_normalize, "Normalize", true);
|
||||
SOCKET_BOOLEAN(use_origin_offset, "Origin Offset", true);
|
||||
|
||||
SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED);
|
||||
SOCKET_IN_FLOAT(scale, "Scale", 5.0f);
|
||||
SOCKET_IN_FLOAT(frequency, "Frequency", 4.0f);
|
||||
SOCKET_IN_FLOAT(gain, "Gain", 1.0f);
|
||||
SOCKET_IN_FLOAT(detail, "Detail", 0.0f);
|
||||
SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f);
|
||||
SOCKET_IN_FLOAT(scl_lacunarity, "Scale Lacunarity", 2.0f);
|
||||
SOCKET_IN_FLOAT(fre_lacunarity, "Frequency Lacunarity", 2.0f);
|
||||
SOCKET_IN_FLOAT(rot_lacunarity, "Rotation Lacunarity", 0.0f);
|
||||
SOCKET_IN_FLOAT(radius, "Radius", 1.0f);
|
||||
SOCKET_IN_FLOAT(impulses, "Impulses", 2.0f);
|
||||
SOCKET_IN_FLOAT(phase, "Phase Offset", 0.0f);
|
||||
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(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(anisotropy, "Anisotropic Factor", 1.0f);
|
||||
|
||||
SOCKET_OUT_FLOAT(value, "Value");
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
GaborFTextureNode::GaborFTextureNode() : TextureNode(node_type) {}
|
||||
|
||||
void GaborFTextureNode::constant_fold(const ConstantFolder &folder)
|
||||
{
|
||||
ShaderInput *scale_in = input("Scale");
|
||||
ShaderInput *gain_in = input("Gain");
|
||||
ShaderInput *radius_in = input("Radius");
|
||||
ShaderInput *impulses_in = input("Impulses");
|
||||
|
||||
if ((!scale_in->link && scale == 0.0f) || (!gain_in->link && gain == 0.0f) ||
|
||||
(!radius_in->link && radius == 0.0f) || (!impulses_in->link && impulses == 0.0f))
|
||||
{
|
||||
folder.make_constant(use_normalize ? 0.5f : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void GaborFTextureNode::compile(SVMCompiler &compiler)
|
||||
{
|
||||
ShaderInput *vector_in = input("Vector");
|
||||
ShaderInput *scale_in = input("Scale");
|
||||
ShaderInput *frequency_in = input("Frequency");
|
||||
ShaderInput *detail_in = input("Detail");
|
||||
ShaderInput *roughness_in = input("Roughness");
|
||||
ShaderInput *scl_lacunarity_in = input("Scale Lacunarity");
|
||||
ShaderInput *fre_lacunarity_in = input("Frequency Lacunarity");
|
||||
ShaderInput *rot_lacunarity_in = input("Rotation Lacunarity");
|
||||
ShaderInput *gain_in = input("Gain");
|
||||
ShaderInput *radius_in = input("Radius");
|
||||
ShaderInput *impulses_in = input("Impulses");
|
||||
ShaderInput *phase_in = input("Phase Offset");
|
||||
ShaderInput *phase_variance_in = input("Phase Variance");
|
||||
ShaderInput *rotation_in = input("Rotation");
|
||||
ShaderInput *rot_variance_in = input("Rotation Variance");
|
||||
ShaderInput *tilt_randomness_in = input("Tilt Randomness");
|
||||
ShaderInput *direction_in = input("Direction");
|
||||
ShaderInput *cell_randomness_in = input("Cell Randomness");
|
||||
ShaderInput *anisotropy_in = input("Anisotropic Factor");
|
||||
|
||||
ShaderOutput *value_out = output("Value");
|
||||
|
||||
int vector_stack_offset = tex_mapping.compile_begin(compiler, vector_in);
|
||||
|
||||
int scale_in_stack_offset = compiler.stack_assign(scale_in);
|
||||
int frequency_in_stack_offset = compiler.stack_assign(frequency_in);
|
||||
int detail_in_stack_offset = compiler.stack_assign(detail_in);
|
||||
int roughness_in_stack_offset = compiler.stack_assign(roughness_in);
|
||||
int scl_lacunarity_in_stack_offset = compiler.stack_assign(scl_lacunarity_in);
|
||||
int fre_lacunarity_in_stack_offset = compiler.stack_assign(fre_lacunarity_in);
|
||||
int rot_lacunarity_in_stack_offset = compiler.stack_assign(rot_lacunarity_in);
|
||||
int gain_in_stack_offset = compiler.stack_assign(gain_in);
|
||||
int radius_in_stack_offset = compiler.stack_assign(radius_in);
|
||||
int impulses_in_stack_offset = compiler.stack_assign(impulses_in);
|
||||
int phase_in_stack_offset = compiler.stack_assign(phase_in);
|
||||
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 tilt_randomness_in_stack_offset = compiler.stack_assign(tilt_randomness_in);
|
||||
int cell_randomness_in_stack_offset = compiler.stack_assign(cell_randomness_in);
|
||||
int anisotropy_in_stack_offset = compiler.stack_assign(anisotropy_in);
|
||||
int direction_in_stack_offset = compiler.stack_assign(direction_in);
|
||||
|
||||
int value_out_stack_offset = compiler.stack_assign_if_linked(value_out);
|
||||
|
||||
compiler.add_node(NODE_TEX_GABORF,
|
||||
compiler.encode_uchar4(vector_stack_offset,
|
||||
scale_in_stack_offset,
|
||||
frequency_in_stack_offset,
|
||||
detail_in_stack_offset),
|
||||
compiler.encode_uchar4(roughness_in_stack_offset,
|
||||
scl_lacunarity_in_stack_offset,
|
||||
fre_lacunarity_in_stack_offset,
|
||||
rot_lacunarity_in_stack_offset),
|
||||
compiler.encode_uchar4(gain_in_stack_offset,
|
||||
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,
|
||||
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, anisotropy_in_stack_offset, periodic, use_normalize),
|
||||
use_origin_offset);
|
||||
|
||||
compiler.add_node(__float_as_int(scale),
|
||||
__float_as_int(frequency),
|
||||
__float_as_int(detail),
|
||||
__float_as_int(roughness));
|
||||
compiler.add_node(__float_as_int(scl_lacunarity),
|
||||
__float_as_int(fre_lacunarity),
|
||||
__float_as_int(rot_lacunarity),
|
||||
__float_as_int(gain));
|
||||
compiler.add_node(__float_as_int(radius),
|
||||
__float_as_int(impulses),
|
||||
__float_as_int(phase),
|
||||
__float_as_int(phase_variance));
|
||||
compiler.add_node(__float_as_int(cell_randomness),
|
||||
__float_as_int(rotation),
|
||||
__float_as_int(rot_variance),
|
||||
__float_as_int(tilt_randomness));
|
||||
compiler.add_node(__float_as_int(anisotropy));
|
||||
|
||||
tex_mapping.compile_end(compiler, vector_in, vector_stack_offset);
|
||||
}
|
||||
|
||||
void GaborFTextureNode::compile(OSLCompiler &compiler)
|
||||
{
|
||||
tex_mapping.compile(compiler);
|
||||
compiler.parameter(this, "dimensions");
|
||||
compiler.parameter(this, "mode");
|
||||
compiler.parameter(this, "periodic");
|
||||
compiler.parameter(this, "use_normalize");
|
||||
compiler.parameter(this, "use_origin_offset");
|
||||
compiler.add(this, "node_gaborf_texture");
|
||||
}
|
||||
|
||||
/* Voronoi Texture */
|
||||
|
||||
NODE_DEFINE(VoronoiTextureNode)
|
||||
|
@ -254,6 +254,38 @@ class GaborTextureNode : public TextureNode {
|
||||
NODE_SOCKET_API(float3, orientation_3d)
|
||||
};
|
||||
|
||||
class GaborFTextureNode : public TextureNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(GaborFTextureNode)
|
||||
void constant_fold(const ConstantFolder &folder);
|
||||
|
||||
NODE_SOCKET_API(int, dimensions)
|
||||
NODE_SOCKET_API(NodeGaborFMode, mode);
|
||||
NODE_SOCKET_API(bool, periodic)
|
||||
NODE_SOCKET_API(bool, use_normalize)
|
||||
NODE_SOCKET_API(bool, use_origin_offset)
|
||||
|
||||
NODE_SOCKET_API(float3, vector)
|
||||
NODE_SOCKET_API(float, scale)
|
||||
NODE_SOCKET_API(float, frequency)
|
||||
NODE_SOCKET_API(float, detail)
|
||||
NODE_SOCKET_API(float, roughness)
|
||||
NODE_SOCKET_API(float, scl_lacunarity)
|
||||
NODE_SOCKET_API(float, fre_lacunarity)
|
||||
NODE_SOCKET_API(float, rot_lacunarity)
|
||||
NODE_SOCKET_API(float, gain)
|
||||
NODE_SOCKET_API(float, radius)
|
||||
NODE_SOCKET_API(float, impulses)
|
||||
NODE_SOCKET_API(float, phase)
|
||||
NODE_SOCKET_API(float, phase_variance)
|
||||
NODE_SOCKET_API(float, rotation)
|
||||
NODE_SOCKET_API(float, rot_variance)
|
||||
NODE_SOCKET_API(float, tilt_randomness)
|
||||
NODE_SOCKET_API(float, cell_randomness)
|
||||
NODE_SOCKET_API(float, anisotropy)
|
||||
NODE_SOCKET_API(float3, direction)
|
||||
};
|
||||
|
||||
class VoronoiTextureNode : public TextureNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(VoronoiTextureNode)
|
||||
|
@ -565,6 +565,7 @@ class NODE_MT_category_GEO_TEXTURE(Menu):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexBrick")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexChecker")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexGaborF")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexGradient")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeImageTexture")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexMagic")
|
||||
|
@ -295,6 +295,7 @@ class NODE_MT_category_shader_texture(Menu):
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexChecker")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexEnvironment")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexGabor")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexGaborF")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexGradient")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexIES")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexImage")
|
||||
|
@ -997,6 +997,7 @@ void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index
|
||||
#define SH_NODE_MIX 713
|
||||
#define SH_NODE_BSDF_RAY_PORTAL 714
|
||||
#define SH_NODE_TEX_GABOR 715
|
||||
#define SH_NODE_TEX_GABORF 716
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@ -596,6 +596,7 @@ set(GLSL_SRC
|
||||
shaders/material/gpu_shader_material_tex_checker.glsl
|
||||
shaders/material/gpu_shader_material_tex_environment.glsl
|
||||
shaders/material/gpu_shader_material_tex_gabor.glsl
|
||||
shaders/material/gpu_shader_material_tex_gaborf.glsl
|
||||
shaders/material/gpu_shader_material_tex_gradient.glsl
|
||||
shaders/material/gpu_shader_material_tex_image.glsl
|
||||
shaders/material/gpu_shader_material_tex_magic.glsl
|
||||
|
@ -0,0 +1,472 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
|
||||
/* Gabor Noise
|
||||
*
|
||||
* Based on: Blender patch D287 & D3495
|
||||
* With additional controls for kernel variance.
|
||||
*
|
||||
* This implementation is adapted from the original built-in OSL implementation based on the 2009
|
||||
* paper "Procedural noise using sparse Gabor convolution". Some parts are also adapted from the
|
||||
* 2011 paper "Filtering Solid Gabor Noise" but this does not include the filtering and slicing.
|
||||
* References to the papers are for copyright and reference.
|
||||
*
|
||||
* Notes and changes from the original OSL implementation and reference papers:
|
||||
* - For 2D noise, as with Voronoi the calculation uses a 3x3x1 grid rather than slicing 3D noise.
|
||||
* This is more performant when only 2D texture is required.
|
||||
* - For artistic control, calculations for Bandwidth have been simplified and replaced with
|
||||
* separate controls for Frequency, Gain and Radius. This provides finer control where
|
||||
* |