Nodes/Jovi_GLSL/NOISE WORLEY (JOV_GL)
ComfyUI Node

NOISE WORLEY (JOV_GL)

Worley noise — the cellular 'crackle' texture, generated as a node

By Amorano·Created 2 years ago·Updated about a year ago· 20
NOISE WORLEY (JOV_GL)
  • iRes
  • RGBA
  • RGB
  • MASK
frequency1.00
amplitude1.00
octaves4
lacunarity2.00
persistence0.50
offset0.00
seed0
FRAGMENT// name: NOISE WORLEY // desc: Worley noise with the best // category: NOISE // control: res, seed #ifndef LIB_NOISE_PARAMS #define LIB_NOISE_PARAMS #ifndef MAX_OCTAVES #define MAX_OCTAVES 16 #endif struct NoiseParams { float frequency; // Base frequency multiplier float amplitude; // Base amplitude multiplier int octaves; // Number of octaves float lacunarity; // Frequency multiplier per octave float persistence; // Amplitude multiplier per octave (same as 'gain' in some functions) float offset; // For ridge noise int seed; // Seed }; NoiseParams defaultNoiseParams() { NoiseParams params; params.frequency = 1.0; params.amplitude = 1.0; params.octaves = 4; params.lacunarity = 2.0; params.persistence = 0.5; params.offset = 1.0; params.seed = 0; return params; } #endif #ifndef LIB_NOISE_WORLEY #define LIB_NOISE_WORLEY #ifndef LIB_NOISE_RAND #define LIB_NOISE_RAND //------------------------------------------------------------------------------ // RANDOM VALUE GENERATORS // These functions generate pseudo-random values using different input dimensions //------------------------------------------------------------------------------ float noise_rand(float x) { return fract(sin(x * 12.9898) * 43758.5453); } float noise_rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453123); } float noise_rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.233, 45.678))) * 43758.5453123); } float noise_rand(vec4 co) { return fract(sin(dot(co, vec4(12.9898, 78.233, 45.678, 94.673))) * 43758.5453123); } #endif //------------------------------------------------------------------------------ // NOISE WORLEY //------------------------------------------------------------------------------ // 2D Worley noise function (Cellular noise) float noise_worley(vec2 p, int num_cells) { vec2 i = floor(p); vec2 f = fract(p); float d = 1.0; // Initial distance (for min distance to feature points) for (int x = -num_cells; x <= num_cells; ++x) { for (int y = -num_cells; y <= num_cells; ++y) { vec2 cell = vec2(float(x), float(y)); vec2 point = cell + vec2(noise_rand(i + cell), noise_rand(i + cell + vec2(42.0, 17.0))); vec2 offset = point - f; float len = length(offset); d = min(d, len); } } return d; } // 3D Worley noise function (Cellular noise) float noise_worley(vec3 p, int num_cells) { vec3 i = floor(p); vec3 f = fract(p); float d = 1.0; // Initial distance (for min distance to feature points) for (int x = -num_cells; x <= num_cells; ++x) { for (int y = -num_cells; y <= num_cells; ++y) { for (int z = -num_cells; z <= num_cells; ++z) { vec3 cell = vec3(float(x), float(y), float(z)); vec3 point = cell + vec3(noise_rand(i + cell), noise_rand(i + cell + vec3(42.0, 17.0, 23.0)), noise_rand(i + cell + vec3(23.0, 31.0, 51.0))); vec3 offset = point - f; float len = length(offset); d = min(d, len); } } } return d; } // 4D Worley Noise function float noise_worley(vec4 p, int num_cells) { // Grid cell dimensions float cell_size = 1.0 / float(num_cells); // Compute cell coordinates vec4 cell_coords = floor(p / cell_size); // Compute the local position within the cell vec4 local_pos = fract(p / cell_size); float min_dist = 1.0; // Loop over the neighboring cells for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { for (int z = -1; z <= 1; ++z) { for (int w = -1; w <= 1; ++w) { vec4 neighbor_cell = vec4(x, y, z, w); vec4 neighbor_coords = cell_coords + neighbor_cell; // Randomize the position within the neighboring cell vec4 random_offset = vec4(fract(sin(dot(neighbor_coords, vec4(12.9898, 78.233, 37.719, 4.581))) * 43758.5453)); // Compute the distance to the random point in the neighboring cell vec4 offset_pos = neighbor_cell * cell_size + random_offset; vec4 diff = p - offset_pos; float dist = length(diff); // Update the minimum distance min_dist = min(min_dist, dist); } } } } return min_dist; } #endif uniform float frequency; // 1.; 1.; 100.; 0.01 | Base frequency multiplier uniform float amplitude; // 1.; 1.; 100.; 0.01 | Base amplitude multiplier uniform int octaves; // 4; 1; 12; 1 | Number of octaves uniform float lacunarity; // 2.; 0.; 100.; 0.01 | Frequency multiplier per octave uniform float persistence; // 0.5; 0.; 100.; 0.01 | Amplitude multiplier per octave (same as 'gain' in some functions) uniform float offset; // 0.; 0.; 100.; 0.01 | For ridge noise void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; NoiseParams nparam = defaultNoiseParams(); nparam.frequency = frequency; nparam.amplitude = amplitude; nparam.octaves = octaves; nparam.lacunarity = lacunarity; nparam.persistence = persistence; nparam.offset = offset; nparam.seed = iSeed; float worley = noise_worley(uv, nparam); fragColor = vec4(worley, worley, worley, 1.); }

Perlin and simplex give you smooth gradients. Worley - also called cellular or Voronoi noise - gives you something else entirely: a field of cells, each centered on a random point, with the noise value rising as you get farther from the nearest cell center. It's the "crackle," "leather," and "cell wall" texture in a hundred procedural tutorials, and it's a genuinely different kind of material than smooth noise. This node generates it directly on the GPU with the same fBm controls as the pack's other noise nodes, and a seed so you can reproduce any particular cell pattern.

How it works

The shader scatters points across space and computes, per pixel, the distance to the nearest one - that distance becomes the noise value. Crank the frequency and you get more, smaller cells; drop it and the cells grow into chunky organic plates. The fBm stack then layers these at different scales:

  • frequency (default 1) - base cell density.
  • amplitude (default 1) - strength of the first layer.
  • octaves (default 4, max 12) - stacked cell layers.
  • lacunarity (default 2) - frequency multiplier per octave.
  • persistence (default 0.5) - amplitude multiplier per octave.
  • offset - shifts the field.
  • seed - drives iSeed for reproducible cell layouts.

Pure generator, no image input, output at iRes (default 512×512). Outputs are the pack's RGBA, RGB, and MASK.

When you'd actually reach for it

Worley is a material node, not a soft-mask node. Use it for: organic camouflage and mottling, crackle/leather detail as a bump or mask, and edge-detection-friendly "cell boundary" patterns when you invert it and grab the walls between cells. It also pairs beautifully with the pack's NORMAL node - feed a Worley field in and you get a cellular normal map for cheap, bumpy materials. If you want smooth clouds, use Perlin or Simplex; if you want structure, use this one.

Install

One-time pack install. Via ComfyUI Manager, search Jovi_GLSL; or:

cd ComfyUI/custom_nodes
git clone https://github.com/Amorano/Jovi_GLSL.git
cd Jovi_GLSL
pip install -r requirements.txt

Restart ComfyUI. Standard pack deps (PyOpenGL, glfw, opencv-contrib-python, cozy_comfyui from git), needs an OpenGL context. No models, no keys. Under JOVI_GLSL 🌈 → NOISE.

Gotchas

Same stale tooltip on seed as the other noise nodes - it's the seed, not a frame counter. And one thing that catches people: at default settings Worley reads very dark because most of the image sits at low distance values; raising amplitude and adjusting the range is usually the first move. The output is deterministic per seed, so once you find a cell layout you like, it stays put.

CategoryJOV_GL 🌈/NOISE

Inputs (9)

NameTypeDefaultDescription
frequencyoptFLOAT1.001–100Base frequency multiplier
amplitudeoptFLOAT1.001–100Base amplitude multiplier
octavesoptINT41–12Number of octaves
lacunarityoptFLOAT2.000–100Frequency multiplier per octave
persistenceoptFLOAT0.500–100Amplitude multiplier per octave (same as 'gain' in some functions)
offsetoptFLOAT0.000–100For ridge noise
iResoptVEC2INT512,512Width and Height as a Vector2 Integer (x, y)
seedoptINT00–9223372036854776000Number of frames to generate. 0 (continuous mode) means continue from the last queue generating the next single frame based on iFrameRate.
FRAGMENToptSTRING// name: NOISE WORLEY // desc: Worley noise with the best // category: NOISE // control: res, seed #ifndef LIB_NOISE_PARAMS #define LIB_NOISE_PARAMS #ifndef MAX_OCTAVES #define MAX_OCTAVES 16 #endif struct NoiseParams { float frequency; // Base frequency multiplier float amplitude; // Base amplitude multiplier int octaves; // Number of octaves float lacunarity; // Frequency multiplier per octave float persistence; // Amplitude multiplier per octave (same as 'gain' in some functions) float offset; // For ridge noise int seed; // Seed }; NoiseParams defaultNoiseParams() { NoiseParams params; params.frequency = 1.0; params.amplitude = 1.0; params.octaves = 4; params.lacunarity = 2.0; params.persistence = 0.5; params.offset = 1.0; params.seed = 0; return params; } #endif #ifndef LIB_NOISE_WORLEY #define LIB_NOISE_WORLEY #ifndef LIB_NOISE_RAND #define LIB_NOISE_RAND //------------------------------------------------------------------------------ // RANDOM VALUE GENERATORS // These functions generate pseudo-random values using different input dimensions //------------------------------------------------------------------------------ float noise_rand(float x) { return fract(sin(x * 12.9898) * 43758.5453); } float noise_rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453123); } float noise_rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.233, 45.678))) * 43758.5453123); } float noise_rand(vec4 co) { return fract(sin(dot(co, vec4(12.9898, 78.233, 45.678, 94.673))) * 43758.5453123); } #endif //------------------------------------------------------------------------------ // NOISE WORLEY //------------------------------------------------------------------------------ // 2D Worley noise function (Cellular noise) float noise_worley(vec2 p, int num_cells) { vec2 i = floor(p); vec2 f = fract(p); float d = 1.0; // Initial distance (for min distance to feature points) for (int x = -num_cells; x <= num_cells; ++x) { for (int y = -num_cells; y <= num_cells; ++y) { vec2 cell = vec2(float(x), float(y)); vec2 point = cell + vec2(noise_rand(i + cell), noise_rand(i + cell + vec2(42.0, 17.0))); vec2 offset = point - f; float len = length(offset); d = min(d, len); } } return d; } // 3D Worley noise function (Cellular noise) float noise_worley(vec3 p, int num_cells) { vec3 i = floor(p); vec3 f = fract(p); float d = 1.0; // Initial distance (for min distance to feature points) for (int x = -num_cells; x <= num_cells; ++x) { for (int y = -num_cells; y <= num_cells; ++y) { for (int z = -num_cells; z <= num_cells; ++z) { vec3 cell = vec3(float(x), float(y), float(z)); vec3 point = cell + vec3(noise_rand(i + cell), noise_rand(i + cell + vec3(42.0, 17.0, 23.0)), noise_rand(i + cell + vec3(23.0, 31.0, 51.0))); vec3 offset = point - f; float len = length(offset); d = min(d, len); } } } return d; } // 4D Worley Noise function float noise_worley(vec4 p, int num_cells) { // Grid cell dimensions float cell_size = 1.0 / float(num_cells); // Compute cell coordinates vec4 cell_coords = floor(p / cell_size); // Compute the local position within the cell vec4 local_pos = fract(p / cell_size); float min_dist = 1.0; // Loop over the neighboring cells for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { for (int z = -1; z <= 1; ++z) { for (int w = -1; w <= 1; ++w) { vec4 neighbor_cell = vec4(x, y, z, w); vec4 neighbor_coords = cell_coords + neighbor_cell; // Randomize the position within the neighboring cell vec4 random_offset = vec4(fract(sin(dot(neighbor_coords, vec4(12.9898, 78.233, 37.719, 4.581))) * 43758.5453)); // Compute the distance to the random point in the neighboring cell vec4 offset_pos = neighbor_cell * cell_size + random_offset; vec4 diff = p - offset_pos; float dist = length(diff); // Update the minimum distance min_dist = min(min_dist, dist); } } } } return min_dist; } #endif uniform float frequency; // 1.; 1.; 100.; 0.01 | Base frequency multiplier uniform float amplitude; // 1.; 1.; 100.; 0.01 | Base amplitude multiplier uniform int octaves; // 4; 1; 12; 1 | Number of octaves uniform float lacunarity; // 2.; 0.; 100.; 0.01 | Frequency multiplier per octave uniform float persistence; // 0.5; 0.; 100.; 0.01 | Amplitude multiplier per octave (same as 'gain' in some functions) uniform float offset; // 0.; 0.; 100.; 0.01 | For ridge noise void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; NoiseParams nparam = defaultNoiseParams(); nparam.frequency = frequency; nparam.amplitude = amplitude; nparam.octaves = octaves; nparam.lacunarity = lacunarity; nparam.persistence = persistence; nparam.offset = offset; nparam.seed = iSeed; float worley = noise_worley(uv, nparam); fragColor = vec4(worley, worley, worley, 1.); }

Outputs (3)

NameTypeDescription
RGBAIMAGEFull channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output.
RGBIMAGEThree channel [RGB] image. There will be no alpha.
MASKMASKSingle channel mask output.