import { code, fn } from '../../Nodes.js'; // Original shader code from: // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_noise.glsl export const mx_noise = code( `float mx_select(bool b, float t, float f) { return b ? t : f; } float mx_negate_if(float val, bool b) { return b ? -val : val; } int mx_floor(float x) { return int(floor(x)); } // return mx_floor as well as the fractional remainder float mx_floorfrac(float x, out int i) { i = mx_floor(x); return x - float(i); } float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t) { float s1 = 1.0 - s; return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s); } vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t) { float s1 = 1.0 - s; return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s); } float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r) { float s1 = 1.0 - s; float t1 = 1.0 - t; float r1 = 1.0 - r; return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) + r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s))); } vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r) { float s1 = 1.0 - s; float t1 = 1.0 - t; float r1 = 1.0 - r; return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) + r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s))); } // 2 and 3 dimensional gradient functions - perform a dot product against a // randomly chosen vector. Note that the gradient vector is not normalized, but // this only affects the overal "scale" of the result, so we simply account for // the scale by multiplying in the corresponding "perlin" function. float mx_gradient_float(uint hash, float x, float y) { // 8 possible directions (+-1,+-2) and (+-2,+-1) uint h = hash & 7u; float u = mx_select(h<4u, x, y); float v = 2.0 * mx_select(h<4u, y, x); // compute the dot product with (x,y). return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u)); } float mx_gradient_float(uint hash, float x, float y, float z) { // use vectors pointing to the edges of the cube uint h = hash & 15u; float u = mx_select(h<8u, x, y); float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z)); return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u)); } vec3 mx_gradient_vec3(uvec3 hash, float x, float y) { return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y)); } vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z) { return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z)); } // Scaling factors to normalize the result of gradients above. // These factors were experimentally calculated to be: // 2D: 0.6616 // 3D: 0.9820 float mx_gradient_scale2d(float v) { return 0.6616 * v; } float mx_gradient_scale3d(float v) { return 0.9820 * v; } vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; } vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; } /// Bitwise circular rotation left by k bits (for 32 bit unsigned integers) uint mx_rotl32(uint x, int k) { return (x<>(32-k)); } void mx_bjmix(inout uint a, inout uint b, inout uint c) { a -= c; a ^= mx_rotl32(c, 4); c += b; b -= a; b ^= mx_rotl32(a, 6); a += c; c -= b; c ^= mx_rotl32(b, 8); b += a; a -= c; a ^= mx_rotl32(c,16); c += b; b -= a; b ^= mx_rotl32(a,19); a += c; c -= b; c ^= mx_rotl32(b, 4); b += a; } // Mix up and combine the bits of a, b, and c (doesn't change them, but // returns a hash of those three original values). uint mx_bjfinal(uint a, uint b, uint c) { c ^= b; c -= mx_rotl32(b,14); a ^= c; a -= mx_rotl32(c,11); b ^= a; b -= mx_rotl32(a,25); c ^= b; c -= mx_rotl32(b,16); a ^= c; a -= mx_rotl32(c,4); b ^= a; b -= mx_rotl32(a,14); c ^= b; c -= mx_rotl32(b,24); return c; } // Convert a 32 bit integer into a floating point number in [0,1] float mx_bits_to_01(uint bits) { return float(bits) / float(uint(0xffffffff)); } float mx_fade(float t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } uint mx_hash_int(int x) { uint len = 1u; uint seed = uint(0xdeadbeef) + (len << 2u) + 13u; return mx_bjfinal(seed+uint(x), seed, seed); } uint mx_hash_int(int x, int y) { uint len = 2u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); return mx_bjfinal(a, b, c); } uint mx_hash_int(int x, int y, int z) { uint len = 3u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); c += uint(z); return mx_bjfinal(a, b, c); } uint mx_hash_int(int x, int y, int z, int xx) { uint len = 4u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); c += uint(z); mx_bjmix(a, b, c); a += uint(xx); return mx_bjfinal(a, b, c); } uint mx_hash_int(int x, int y, int z, int xx, int yy) { uint len = 5u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); c += uint(z); mx_bjmix(a, b, c); a += uint(xx); b += uint(yy); return mx_bjfinal(a, b, c); } uvec3 mx_hash_vec3(int x, int y) { uint h = mx_hash_int(x, y); // we only need the low-order bits to be random, so split out // the 32 bit result into 3 parts for each channel uvec3 result; result.x = (h ) & 0xFFu; result.y = (h >> 8 ) & 0xFFu; result.z = (h >> 16) & 0xFFu; return result; } uvec3 mx_hash_vec3(int x, int y, int z) { uint h = mx_hash_int(x, y, z); // we only need the low-order bits to be random, so split out // the 32 bit result into 3 parts for each channel uvec3 result; result.x = (h ) & 0xFFu; result.y = (h >> 8 ) & 0xFFu; result.z = (h >> 16) & 0xFFu; return result; } float mx_perlin_noise_float(vec2 p) { int X, Y; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float u = mx_fade(fx); float v = mx_fade(fy); float result = mx_bilerp( mx_gradient_float(mx_hash_int(X , Y ), fx , fy ), mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ), mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0), mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0), u, v); return mx_gradient_scale2d(result); } float mx_perlin_noise_float(vec3 p) { int X, Y, Z; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float fz = mx_floorfrac(p.z, Z); float u = mx_fade(fx); float v = mx_fade(fy); float w = mx_fade(fz); float result = mx_trilerp( mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ), mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ), mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ), mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ), mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0), mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0), mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0), mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0), u, v, w); return mx_gradient_scale3d(result); } vec3 mx_perlin_noise_vec3(vec2 p) { int X, Y; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float u = mx_fade(fx); float v = mx_fade(fy); vec3 result = mx_bilerp( mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ), mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ), mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0), mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0), u, v); return mx_gradient_scale2d(result); } vec3 mx_perlin_noise_vec3(vec3 p) { int X, Y, Z; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float fz = mx_floorfrac(p.z, Z); float u = mx_fade(fx); float v = mx_fade(fy); float w = mx_fade(fz); vec3 result = mx_trilerp( mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ), mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ), mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ), mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ), mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0), mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0), mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0), mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0), u, v, w); return mx_gradient_scale3d(result); } float mx_cell_noise_float(float p) { int ix = mx_floor(p); return mx_bits_to_01(mx_hash_int(ix)); } float mx_cell_noise_float(vec2 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); return mx_bits_to_01(mx_hash_int(ix, iy)); } float mx_cell_noise_float(vec3 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); return mx_bits_to_01(mx_hash_int(ix, iy, iz)); } float mx_cell_noise_float(vec4 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); int iw = mx_floor(p.w); return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw)); } vec3 mx_cell_noise_vec3(float p) { int ix = mx_floor(p); return vec3( mx_bits_to_01(mx_hash_int(ix, 0)), mx_bits_to_01(mx_hash_int(ix, 1)), mx_bits_to_01(mx_hash_int(ix, 2)) ); } vec3 mx_cell_noise_vec3(vec2 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); return vec3( mx_bits_to_01(mx_hash_int(ix, iy, 0)), mx_bits_to_01(mx_hash_int(ix, iy, 1)), mx_bits_to_01(mx_hash_int(ix, iy, 2)) ); } vec3 mx_cell_noise_vec3(vec3 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); return vec3( mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)), mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)), mx_bits_to_01(mx_hash_int(ix, iy, iz, 2)) ); } vec3 mx_cell_noise_vec3(vec4 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); int iw = mx_floor(p.w); return vec3( mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)), mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)), mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2)) ); } float mx_fractal_noise_float(vec3 p, int octaves, float lacunarity, float diminish) { float result = 0.0; float amplitude = 1.0; for (int i = 0; i < octaves; ++i) { result += amplitude * mx_perlin_noise_float(p); amplitude *= diminish; p *= lacunarity; } return result; } vec3 mx_fractal_noise_vec3(vec3 p, int octaves, float lacunarity, float diminish) { vec3 result = vec3(0.0); float amplitude = 1.0; for (int i = 0; i < octaves; ++i) { result += amplitude * mx_perlin_noise_vec3(p); amplitude *= diminish; p *= lacunarity; } return result; } vec2 mx_fractal_noise_vec2(vec3 p, int octaves, float lacunarity, float diminish) { return vec2(mx_fractal_noise_float(p, octaves, lacunarity, diminish), mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish)); } vec4 mx_fractal_noise_vec4(vec3 p, int octaves, float lacunarity, float diminish) { vec3 c = mx_fractal_noise_vec3(p, octaves, lacunarity, diminish); float f = mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish); return vec4(c, f); } float mx_worley_distance(vec2 p, int x, int y, int xoff, int yoff, float jitter, int metric) { vec3 tmp = mx_cell_noise_vec3(vec2(x+xoff, y+yoff)); vec2 off = vec2(tmp.x, tmp.y); off -= 0.5f; off *= jitter; off += 0.5f; vec2 cellpos = vec2(float(x), float(y)) + off; vec2 diff = cellpos - p; if (metric == 2) return abs(diff.x) + abs(diff.y); // Manhattan distance if (metric == 3) return max(abs(diff.x), abs(diff.y)); // Chebyshev distance // Either Euclidian or Distance^2 return dot(diff, diff); } float mx_worley_distance(vec3 p, int x, int y, int z, int xoff, int yoff, int zoff, float jitter, int metric) { vec3 off = mx_cell_noise_vec3(vec3(x+xoff, y+yoff, z+zoff)); off -= 0.5f; off *= jitter; off += 0.5f; vec3 cellpos = vec3(float(x), float(y), float(z)) + off; vec3 diff = cellpos - p; if (metric == 2) return abs(diff.x) + abs(diff.y) + abs(diff.z); // Manhattan distance if (metric == 3) return max(max(abs(diff.x), abs(diff.y)), abs(diff.z)); // Chebyshev distance // Either Euclidian or Distance^2 return dot(diff, diff); } float mx_worley_noise_float(vec2 p, float jitter, int metric) { int X, Y; vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y)); float sqdist = 1e6f; // Some big number for jitter > 1 (not all GPUs may be IEEE) for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric); sqdist = min(sqdist, dist); } } if (metric == 0) sqdist = sqrt(sqdist); return sqdist; } vec2 mx_worley_noise_vec2(vec2 p, float jitter, int metric) { int X, Y; vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y)); vec2 sqdist = vec2(1e6f, 1e6f); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric); if (dist < sqdist.x) { sqdist.y = sqdist.x; sqdist.x = dist; } else if (dist < sqdist.y) { sqdist.y = dist; } } } if (metric == 0) sqdist = sqrt(sqdist); return sqdist; } vec3 mx_worley_noise_vec3(vec2 p, float jitter, int metric) { int X, Y; vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y)); vec3 sqdist = vec3(1e6f, 1e6f, 1e6f); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric); if (dist < sqdist.x) { sqdist.z = sqdist.y; sqdist.y = sqdist.x; sqdist.x = dist; } else if (dist < sqdist.y) { sqdist.z = sqdist.y; sqdist.y = dist; } else if (dist < sqdist.z) { sqdist.z = dist; } } } if (metric == 0) sqdist = sqrt(sqdist); return sqdist; } float mx_worley_noise_float(vec3 p, float jitter, int metric) { int X, Y, Z; vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z)); float sqdist = 1e6f; for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { for (int z = -1; z <= 1; ++z) { float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric); sqdist = min(sqdist, dist); } } } if (metric == 0) sqdist = sqrt(sqdist); return sqdist; } vec2 mx_worley_noise_vec2(vec3 p, float jitter, int metric) { int X, Y, Z; vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z)); vec2 sqdist = vec2(1e6f, 1e6f); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { for (int z = -1; z <= 1; ++z) { float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric); if (dist < sqdist.x) { sqdist.y = sqdist.x; sqdist.x = dist; } else if (dist < sqdist.y) { sqdist.y = dist; } } } } if (metric == 0) sqdist = sqrt(sqdist); return sqdist; } vec3 mx_worley_noise_vec3(vec3 p, float jitter, int metric) { int X, Y, Z; vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z)); vec3 sqdist = vec3(1e6f, 1e6f, 1e6f); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { for (int z = -1; z <= 1; ++z) { float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric); if (dist < sqdist.x) { sqdist.z = sqdist.y; sqdist.y = sqdist.x; sqdist.x = dist; } else if (dist < sqdist.y) { sqdist.z = sqdist.y; sqdist.y = dist; } else if (dist < sqdist.z) { sqdist.z = dist; } } } } if (metric == 0) sqdist = sqrt(sqdist); return sqdist; }` ); const includes = [ mx_noise ]; export const mx_perlin_noise_float = fn( 'float mx_perlin_noise_float( any p )', includes ); export const mx_perlin_noise_vec2 = fn( 'vec2 mx_perlin_noise_vec2( any p )', includes ); export const mx_perlin_noise_vec3 = fn( 'vec3 mx_perlin_noise_vec3( any p )', includes ); export const mx_cell_noise_float = fn( 'float mx_cell_noise_float( vec3 p )', includes ); export const mx_worley_noise_float = fn( 'float mx_worley_noise_float( any p, float jitter, int metric )', includes ); export const mx_worley_noise_vec2 = fn( 'float mx_worley_noise_vec2( any p, float jitter, int metric )', includes ); export const mx_worley_noise_vec3 = fn( 'float mx_worley_noise_vec3( any p, float jitter, int metric )', includes ); export const mx_fractal_noise_float = fn( 'float mx_fractal_noise_float( vec3 p, int octaves, float lacunarity, float diminish )', includes ); export const mx_fractal_noise_vec2 = fn( 'float mx_fractal_noise_vec2( vec3 p, int octaves, float lacunarity, float diminish )', includes ); export const mx_fractal_noise_vec3 = fn( 'float mx_fractal_noise_vec3( vec3 p, int octaves, float lacunarity, float diminish )', includes ); export const mx_fractal_noise_vec4 = fn( 'float mx_fractal_noise_vec4( vec3 p, int octaves, float lacunarity, float diminish )', includes );