// ============================================================================
// ASHIMA SIMPLEX -- ONE TEXT, COMPILED AS BOTH GLSL AND C++.
//
// ⚠ NOT BLENDER'S. Blender's Noise Texture is Perlin (blender_perlin.inc);
// this is the Ashima Arts / webgl-noise `snoise` that proc-nodes' own Simplex
// Noise node uses, and that blender-bridge mirrors as the
// ProcNodes_SimplexNoise group.
//
// ---------------------------------------------------------------------------
// ⛔ THIS FILE REPLACES A BROKEN SCALAR PORT, AND THE HISTORY IS THE POINT.
//
// proc-nodes had TWO implementations: a hand-written GLSL copy of the vector
// form below, and a file-local C++ "scalarisation" of it in noise_nodes.cpp.
// They were never compared -- and they were not the same function:
//
// scalar C++ (CPU) simplex(1.20, 0.36, 2.04) = 5557.13
// vector GLSL (GPU) simplex(1.20, 0.36, 2.04) = -0.5551
//
// The CPU was out of range by four orders of magnitude, so the node's Value
// output -- documented 0..1 -- was in the thousands on the CPU and correct on
// the GPU. Every mesh baked on the CPU had different noise from the same graph
// rendered on the GPU, and nothing in the codebase could see it.
//
// TWO THINGS BROKE IN THE SCALARISATION, both visible above:
//
// 1. THE GRADIENT CONSTANTS. `ns = n_ * D.wyz - D.xzx` with D = (0, .5, 1, 2)
// is (2/7, 0.5/7 - 1, 1/7) = (0.2857, -0.9286, 0.1429). The scalar copy
// used (2/7, -1.0, 0.5/7) -- two of the three wrong.
//
// 2. THE LANE SHUFFLES CANNOT BE DONE PER CORNER. `b0 = vec4(x.xy, y.xy)`
// and `a0 = b0.xzyw + s0.xzyw * sh.xxyy` mix the x and y components of
// DIFFERENT corners into one vec4. A per-corner scalar rewrite has no way
// to express that, which is why the scalarisation was doomed rather than
// merely mistyped.
//
// So this file is the VECTOR form -- the one that was right -- with every
// swizzle written out as an explicit constructor, because GLSL has swizzles
// and C++ does not. `vec3(x0.y, x0.z, x0.x)` is what `x0.yzx` means and both
// languages accept it.
// ============================================================================
#ifndef PN_PROC_SIMPLEX
#define PN_PROC_SIMPLEX
#ifndef BN_FN
#define BN_FN
#endif
BN_FN vec3 ps_mod289_v3(vec3 x) {
return x - floor(x * (1.0f / 289.0f)) * 289.0f;
}
BN_FN vec4 ps_mod289_v4(vec4 x) {
return x - floor(x * (1.0f / 289.0f)) * 289.0f;
}
BN_FN vec4 ps_permute(vec4 x) {
return ps_mod289_v4(((x * 34.0f) + 10.0f) * x);
}
BN_FN vec4 ps_taylor_inv_sqrt(vec4 r) {
return 1.79284291400159f - 0.85373472095314f * r;
}
BN_FN float ps_simplex_noise_3d_v(vec3 v) {
// C = (1/6, 1/3), D = (0, 0.5, 1, 2) in the original; written out below
// wherever a swizzle of them was used.
float Cx = 1.0f / 6.0f;
float Cy = 1.0f / 3.0f;
// First corner
vec3 i = floor(v + vec3(dot(v, vec3(Cy, Cy, Cy))));
vec3 x0 = v - i + vec3(dot(i, vec3(Cx, Cx, Cx)));
// Other corners
vec3 g = step(vec3(x0.y, x0.z, x0.x), vec3(x0.x, x0.y, x0.z));
vec3 l = 1.0f - g;
vec3 i1 = min(vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y));
vec3 i2 = max(vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y));
vec3 x1 = x0 - i1 + vec3(Cx, Cx, Cx);
vec3 x2 = x0 - i2 + vec3(Cy, Cy, Cy);
vec3 x3 = x0 - vec3(0.5f, 0.5f, 0.5f); // D.yyy
// Permutations
i = ps_mod289_v3(i);
vec4 p = ps_permute(ps_permute(ps_permute(
vec4(i.z, i.z, i.z, i.z) + vec4(0.0f, i1.z, i2.z, 1.0f))
+ vec4(i.y, i.y, i.y, i.y) + vec4(0.0f, i1.y, i2.y, 1.0f))
+ vec4(i.x, i.x, i.x, i.x) + vec4(0.0f, i1.x, i2.x, 1.0f));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// ⛔ ns = n_ * D.wyz - D.xzx = n_ * (2, 0.5, 1) - (0, 1, 0). The scalar
// port that this file replaces got two of these three wrong.
float n_ = 0.142857142857f; // 1/7
vec3 ns = vec3(n_ * 2.0f - 0.0f, n_ * 0.5f - 1.0f, n_ * 1.0f - 0.0f);
vec4 j = p - 49.0f * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0f * x_);
vec4 x = x_ * ns.x + vec4(ns.y, ns.y, ns.y, ns.y);
vec4 y = y_ * ns.x + vec4(ns.y, ns.y, ns.y, ns.y);
vec4 h = 1.0f - abs(x) - abs(y);
// ⛔ THE LANES CROSS HERE. b0 holds x.xy and y.xy -- two components of one
// corner pair and two of another -- and a0 then shuffles them again. This
// is what a per-corner scalarisation cannot represent.
vec4 b0 = vec4(x.x, x.y, y.x, y.y);
vec4 b1 = vec4(x.z, x.w, y.z, y.w);
vec4 s0 = floor(b0) * 2.0f + 1.0f;
vec4 s1 = floor(b1) * 2.0f + 1.0f;
vec4 sh = -step(h, vec4(0.0f));
vec4 a0 = vec4(b0.x, b0.z, b0.y, b0.w) +
vec4(s0.x, s0.z, s0.y, s0.w) * vec4(sh.x, sh.x, sh.y, sh.y);
vec4 a1 = vec4(b1.x, b1.z, b1.y, b1.w) +
vec4(s1.x, s1.z, s1.y, s1.w) * vec4(sh.z, sh.z, sh.w, sh.w);
vec3 p0 = vec3(a0.x, a0.y, h.x);
vec3 p1 = vec3(a0.z, a0.w, h.y);
vec3 p2 = vec3(a1.x, a1.y, h.z);
vec3 p3 = vec3(a1.z, a1.w, h.w);
// Normalise gradients
vec4 norm = ps_taylor_inv_sqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix contributions from the four corners
vec4 m = max(0.5f - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), vec4(0.0f));
m = m * m;
return 105.0f * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
}
// The scalar entry point the nodes call, so the call sites did not change.
BN_FN float ps_simplex_noise_3d(float x, float y, float z) {
return ps_simplex_noise_3d_v(vec3(x, y, z));
}
BN_FN float ps_fbm_noise_3d(float x, float y, float z, int octaves, float roughness,
float lacunarity) {
float value = 0.0f;
float amplitude = 1.0f;
float frequency = 1.0f;
float max_amp = 0.0f;
for (int i = 0; i < octaves; i++) {
value += amplitude * ps_simplex_noise_3d_v(vec3(x, y, z) * frequency);
max_amp += amplitude;
amplitude *= roughness;
frequency *= lacunarity;
}
return value / max_amp;
}
#endif // PN_PROC_SIMPLEX