| … | … | @@ -78,6 +78,13 @@ glsl_inc_gen = [ |
| 78 | 78 | "src/shared/blender_ramp_blend.inc", |
| 79 | 79 | "python3 tools/gen_glsl_inc.py $SOURCE $TARGET BLENDER_RAMP_BLEND_GLSL", |
| 80 | 80 | ), |
| 81 | # Ashima simplex, sixth .inc. NOT Blender's -- it is proc-nodes' own | |
| 82 | # Simplex Noise node, which blender-bridge mirrors as a node group. | |
| 83 | env.Command( | |
| 84 | "src/gpu/proc_simplex_glsl.gen.h", | |
| 85 | "src/shared/proc_simplex.inc", | |
| 86 | "python3 tools/gen_glsl_inc.py $SOURCE $TARGET PROC_SIMPLEX_GLSL", | |
| 87 | ), | |
| 81 | 88 | ] |
| 82 | 89 | |
| 83 | 90 | sources = ( |
| … | … | @@ -1,371 +1,4 @@ |
| 1 | 1 | |
| 2 | vec3 mod289_v3(vec3 x) { | |
| 3 | return x - floor(x * (1.0 / 289.0)) * 289.0; | |
| 4 | } | |
| 5 | ||
| 6 | vec4 mod289_v4(vec4 x) { | |
| 7 | return x - floor(x * (1.0 / 289.0)) * 289.0; | |
| 8 | } | |
| 9 | ||
| 10 | vec4 permute(vec4 x) { | |
| 11 | return mod289_v4(((x * 34.0) + 10.0) * x); | |
| 12 | } | |
| 13 | ||
| 14 | vec4 taylor_inv_sqrt(vec4 r) { | |
| 15 | return 1.79284291400159 - 0.85373472095314 * r; | |
| 16 | } | |
| 17 | ||
| 18 | float simplex_noise_3d(vec3 v) { | |
| 19 | const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); | |
| 20 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); | |
| 21 | ||
| 22 | // First corner | |
| 23 | vec3 i = floor(v + dot(v, C.yyy)); | |
| 24 | vec3 x0 = v - i + dot(i, C.xxx); | |
| 25 | ||
| 26 | // Other corners | |
| 27 | vec3 g = step(x0.yzx, x0.xyz); | |
| 28 | vec3 l = 1.0 - g; | |
| 29 | vec3 i1 = min(g.xyz, l.zxy); | |
| 30 | vec3 i2 = max(g.xyz, l.zxy); | |
| 31 | ||
| 32 | vec3 x1 = x0 - i1 + C.xxx; | |
| 33 | vec3 x2 = x0 - i2 + C.yyy; | |
| 34 | vec3 x3 = x0 - D.yyy; | |
| 35 | ||
| 36 | // Permutations | |
| 37 | i = mod289_v3(i); | |
| 38 | vec4 p = permute(permute(permute( | |
| 39 | i.z + vec4(0.0, i1.z, i2.z, 1.0)) | |
| 40 | + i.y + vec4(0.0, i1.y, i2.y, 1.0)) | |
| 41 | + i.x + vec4(0.0, i1.x, i2.x, 1.0)); | |
| 42 | ||
| 43 | // Gradients: 7x7 points over a square, mapped onto an octahedron | |
| 44 | float n_ = 0.142857142857; // 1.0 / 7.0 | |
| 45 | vec3 ns = n_ * D.wyz - D.xzx; | |
| 46 | ||
| 47 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); | |
| 48 | ||
| 49 | vec4 x_ = floor(j * ns.z); | |
| 50 | vec4 y_ = floor(j - 7.0 * x_); | |
| 51 | ||
| 52 | vec4 x = x_ * ns.x + ns.yyyy; | |
| 53 | vec4 y = y_ * ns.x + ns.yyyy; | |
| 54 | vec4 h = 1.0 - abs(x) - abs(y); | |
| 55 | ||
| 56 | vec4 b0 = vec4(x.xy, y.xy); | |
| 57 | vec4 b1 = vec4(x.zw, y.zw); | |
| 58 | ||
| 59 | vec4 s0 = floor(b0) * 2.0 + 1.0; | |
| 60 | vec4 s1 = floor(b1) * 2.0 + 1.0; | |
| 61 | vec4 sh = -step(h, vec4(0.0)); | |
| 62 | ||
| 63 | vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; | |
| 64 | vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; | |
| 65 | ||
| 66 | vec3 p0 = vec3(a0.xy, h.x); | |
| 67 | vec3 p1 = vec3(a0.zw, h.y); | |
| 68 | vec3 p2 = vec3(a1.xy, h.z); | |
| 69 | vec3 p3 = vec3(a1.zw, h.w); | |
| 70 | ||
| 71 | // Normalise gradients | |
| 72 | vec4 norm = taylor_inv_sqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); | |
| 73 | p0 *= norm.x; | |
| 74 | p1 *= norm.y; | |
| 75 | p2 *= norm.z; | |
| 76 | p3 *= norm.w; | |
| 77 | ||
| 78 | // Mix contributions from the four corners | |
| 79 | vec4 m = max(0.5 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); | |
| 80 | m = m * m; | |
| 81 | return 105.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); | |
| 82 | } | |
| 83 | ||
| 84 | float noise_fbm(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 85 | float value = 0.0; | |
| 86 | float amplitude = 1.0; | |
| 87 | float frequency = 1.0; | |
| 88 | float max_amp = 0.0; | |
| 89 | ||
| 90 | for (int i = 0; i < octaves; i++) { | |
| 91 | value += amplitude * simplex_noise_3d(pos * frequency); | |
| 92 | max_amp += amplitude; | |
| 93 | amplitude *= roughness; | |
| 94 | frequency *= lacunarity; | |
| 95 | } | |
| 96 | ||
| 97 | return value / max_amp; | |
| 98 | } | |
| 99 | ||
| 100 | // Voronoi noise â distance to nearest cell center | |
| 101 | float noise_voronoi(vec3 pos) { | |
| 102 | vec3 i_pos = floor(pos); | |
| 103 | vec3 f_pos = fract(pos); | |
| 104 | float min_dist = 1e10; | |
| 105 | ||
| 106 | for (int x = -1; x <= 1; x++) { | |
| 107 | for (int y = -1; y <= 1; y++) { | |
| 108 | for (int z = -1; z <= 1; z++) { | |
| 109 | vec3 offset = vec3(float(x), float(y), float(z)); | |
| 110 | vec3 cell = i_pos + offset; | |
| 111 | // Hash-based random point in cell | |
| 112 | vec3 r = fract(sin(vec3( | |
| 113 | dot(cell, vec3(127.1, 311.7, 74.7)), | |
| 114 | dot(cell, vec3(269.5, 183.3, 246.1)), | |
| 115 | dot(cell, vec3(113.5, 271.9, 124.6)) | |
| 116 | )) * 43758.5453); | |
| 117 | vec3 cell_pos = offset + r - f_pos; | |
| 118 | float d = length(cell_pos); | |
| 119 | min_dist = min(min_dist, d); | |
| 120 | } | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | return min_dist; | |
| 125 | } | |
| 126 | ||
| 127 | // Musgrave Multifractal | |
| 128 | float noise_musgrave_multi(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 129 | float value = 1.0; | |
| 130 | float frequency = 1.0; | |
| 131 | ||
| 132 | for (int i = 0; i < octaves; i++) { | |
| 133 | float n = simplex_noise_3d(pos * frequency); | |
| 134 | value *= (n * roughness + 1.0); | |
| 135 | frequency *= lacunarity; | |
| 136 | } | |
| 137 | ||
| 138 | return value; | |
| 139 | } | |
| 140 | ||
| 141 | // Musgrave Heterogeneous | |
| 142 | float noise_musgrave_hetero(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 143 | float value = simplex_noise_3d(pos) + 1.0; | |
| 144 | float frequency = lacunarity; | |
| 145 | float amplitude = roughness; | |
| 146 | ||
| 147 | for (int i = 1; i < octaves; i++) { | |
| 148 | float n = (simplex_noise_3d(pos * frequency) + 1.0) * amplitude * value; | |
| 149 | value += n; | |
| 150 | frequency *= lacunarity; | |
| 151 | amplitude *= roughness; | |
| 152 | } | |
| 153 | ||
| 154 | return value; | |
| 155 | } | |
| 156 | ||
| 157 | // Musgrave Hybrid Multifractal | |
| 158 | float noise_musgrave_hybrid(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 159 | float value = simplex_noise_3d(pos) + 1.0; | |
| 160 | float weight = value; | |
| 161 | float frequency = lacunarity; | |
| 162 | float amplitude = roughness; | |
| 163 | ||
| 164 | for (int i = 1; i < octaves; i++) { | |
| 165 | weight = min(weight, 1.0); | |
| 166 | float n = (simplex_noise_3d(pos * frequency) + 1.0) * amplitude; | |
| 167 | value += weight * n; | |
| 168 | weight *= n; | |
| 169 | frequency *= lacunarity; | |
| 170 | amplitude *= roughness; | |
| 171 | } | |
| 172 | ||
| 173 | return value; | |
| 174 | } | |
| 175 | ||
| 176 | // Musgrave Ridged Multifractal | |
| 177 | float noise_musgrave_ridged(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 178 | float value = 0.0; | |
| 179 | float frequency = 1.0; | |
| 180 | float amplitude = 1.0; | |
| 181 | float weight = 1.0; | |
| 182 | ||
| 183 | for (int i = 0; i < octaves; i++) { | |
| 184 | float n = abs(simplex_noise_3d(pos * frequency)); | |
| 185 | n = 1.0 - n; | |
| 186 | n *= n; | |
| 187 | n *= weight; | |
| 188 | weight = clamp(n * 2.0, 0.0, 1.0); | |
| 189 | value += n * amplitude; | |
| 190 | frequency *= lacunarity; | |
| 191 | amplitude *= roughness; | |
| 192 | } | |
| 193 | ||
| 194 | return value; | |
| 195 | } | |
| 196 | ||
| 197 | // ========================================================================= | |
| 198 | // Perlin noise â Blender's exact implementation (Jenkins Lookup3 hash) | |
| 199 | // ========================================================================= | |
| 200 | ||
| 201 | #define PERLIN_ROT(x, k) (((x) << (k)) | ((x) >> (32u - (k)))) | |
| 202 | ||
| 203 | uint jenkins_hash3(uint kx, uint ky, uint kz) { | |
| 204 | uint a, b, c; | |
| 205 | a = b = c = 0xdeadbeefu + (3u << 2u) + 13u; | |
| 206 | c += kz; b += ky; a += kx; | |
| 207 | // hash_bit_final | |
| 208 | c ^= b; c -= PERLIN_ROT(b, 14u); | |
| 209 | a ^= c; a -= PERLIN_ROT(c, 11u); | |
| 210 | b ^= a; b -= PERLIN_ROT(a, 25u); | |
| 211 | c ^= b; c -= PERLIN_ROT(b, 16u); | |
| 212 | a ^= c; a -= PERLIN_ROT(c, 4u); | |
| 213 | b ^= a; b -= PERLIN_ROT(a, 14u); | |
| 214 | c ^= b; c -= PERLIN_ROT(b, 24u); | |
| 215 | return c; | |
| 216 | } | |
| 217 | ||
| 218 | float perlin_fade(float t) { | |
| 219 | return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); | |
| 220 | } | |
| 221 | ||
| 222 | float perlin_negate_if(float val, uint cond) { | |
| 223 | return (cond != 0u) ? -val : val; | |
| 224 | } | |
| 225 | ||
| 226 | float perlin_noise_grad(uint hash, float x, float y, float z) { | |
| 227 | uint h = hash & 15u; | |
| 228 | float u = h < 8u ? x : y; | |
| 229 | float vt = ((h == 12u) || (h == 14u)) ? x : z; | |
| 230 | float v = h < 4u ? y : vt; | |
| 231 | return perlin_negate_if(u, h & 1u) + perlin_negate_if(v, h & 2u); | |
| 232 | } | |
| 233 | ||
| 234 | float perlin_tri_mix( | |
| 235 | float v0, float v1, float v2, float v3, | |
| 236 | float v4, float v5, float v6, float v7, | |
| 237 | float x, float y, float z) { | |
| 238 | float x1 = 1.0 - x; | |
| 239 | float y1 = 1.0 - y; | |
| 240 | float z1 = 1.0 - z; | |
| 241 | return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) + | |
| 242 | z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x)); | |
| 243 | } | |
| 244 | ||
| 245 | float perlin_noise_3d(vec3 p) { | |
| 246 | int X = int(floor(p.x)); float fx = p.x - floor(p.x); | |
| 247 | int Y = int(floor(p.y)); float fy = p.y - floor(p.y); | |
| 248 | int Z = int(floor(p.z)); float fz = p.z - floor(p.z); | |
| 249 | ||
| 250 | float u = perlin_fade(fx); | |
| 251 | float v = perlin_fade(fy); | |
| 252 | float w = perlin_fade(fz); | |
| 253 | ||
| 254 | return perlin_tri_mix( | |
| 255 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y), uint(Z)), fx, fy, fz), | |
| 256 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y), uint(Z)), fx-1, fy, fz), | |
| 257 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y+1), uint(Z)), fx, fy-1, fz), | |
| 258 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y+1), uint(Z)), fx-1, fy-1, fz), | |
| 259 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y), uint(Z+1)), fx, fy, fz-1), | |
| 260 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y), uint(Z+1)), fx-1, fy, fz-1), | |
| 261 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y+1), uint(Z+1)), fx, fy-1, fz-1), | |
| 262 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y+1), uint(Z+1)), fx-1, fy-1, fz-1), | |
| 263 | u, v, w); | |
| 264 | } | |
| 265 | ||
| 266 | float perlin_signed_3d(vec3 p) { | |
| 267 | // Precision correction for very large coordinates (matches Blender noise.cc). | |
| 268 | // For |x| < 1000000, this is a no-op (c=0, fmod returns x unchanged). | |
| 269 | // CRITICAL: Use sign-preserving fmod, NOT GLSL mod(). | |
| 270 | // GLSL mod(x, y) = x - y*floor(x/y) which wraps negatives to positive. | |
| 271 | // C++ fmodf(x, y) preserves the sign of x. For x in [-100000, 100000], | |
| 272 | // fmodf is identity. We replicate this with trunc-based modulo. | |
| 273 | vec3 c = vec3( | |
| 274 | abs(p.x) >= 1000000.0 ? 0.5 : 0.0, | |
| 275 | abs(p.y) >= 1000000.0 ? 0.5 : 0.0, | |
| 276 | abs(p.z) >= 1000000.0 ? 0.5 : 0.0); | |
| 277 | // trunc-based fmod: x - trunc(x/y) * y (preserves sign like C++ fmodf) | |
| 278 | p = p - trunc(p / 100000.0) * 100000.0 + c; | |
| 279 | return perlin_noise_3d(p) * 0.9820; | |
| 280 | } | |
| 281 | ||
| 282 | // Perlin fBM (matches Blender exactly, including fractional detail interpolation) | |
| 283 | float perlin_fbm(vec3 pos, int octaves, float roughness, float lacunarity, bool do_normalize) { | |
| 284 | float fscale = 1.0; | |
| 285 | float amp = 1.0; | |
| 286 | float maxamp = 0.0; | |
| 287 | float sum = 0.0; | |
| 288 | float detail = float(octaves); | |
| 289 | ||
| 290 | for (int i = 0; i <= octaves; i++) { | |
| 291 | sum += perlin_signed_3d(pos * fscale) * amp; | |
| 292 | maxamp += amp; | |
| 293 | amp *= roughness; | |
| 294 | fscale *= lacunarity; | |
| 295 | } | |
| 296 | // Fractional detail interpolation (matches CPU + Blender) | |
| 297 | float rmd = detail - floor(detail); | |
| 298 | if (rmd != 0.0) { | |
| 299 | float t = perlin_signed_3d(pos * fscale); | |
| 300 | float sum2 = sum + t * amp; | |
| 301 | float maxamp2 = maxamp + amp; | |
| 302 | return do_normalize ? | |
| 303 | (1.0 - rmd) * (0.5 * sum / maxamp + 0.5) + rmd * (0.5 * sum2 / maxamp2 + 0.5) : | |
| 304 | (1.0 - rmd) * sum + rmd * sum2; | |
| 305 | } | |
| 306 | return do_normalize ? 0.5 * sum / maxamp + 0.5 : sum; | |
| 307 | } | |
| 308 | ||
| 309 | // Perlin Multifractal | |
| 310 | float perlin_multi_fractal(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 311 | float value = 1.0; | |
| 312 | float pwr = 1.0; | |
| 313 | for (int i = 0; i <= octaves; i++) { | |
| 314 | value *= (pwr * perlin_signed_3d(pos) + 1.0); | |
| 315 | pwr *= roughness; | |
| 316 | pos *= lacunarity; | |
| 317 | } | |
| 318 | return value; | |
| 319 | } | |
| 320 | ||
| 321 | // Perlin Hybrid Multifractal | |
| 322 | float perlin_hybrid(vec3 pos, int octaves, float roughness, float lacunarity, float offset, float gain) { | |
| 323 | float pwr = 1.0; | |
| 324 | float value = 0.0; | |
| 325 | float weight = 1.0; | |
| 326 | for (int i = 0; (weight > 0.001) && (i <= octaves); i++) { | |
| 327 | weight = min(weight, 1.0); | |
| 328 | float signal = (perlin_signed_3d(pos) + offset) * pwr; | |
| 329 | pwr *= roughness; | |
| 330 | value += weight * signal; | |
| 331 | weight *= gain * signal; | |
| 332 | pos *= lacunarity; | |
| 333 | } | |
| 334 | return value; | |
| 335 | } | |
| 336 | ||
| 337 | // Perlin Ridged Multifractal | |
| 338 | float perlin_ridged(vec3 pos, int octaves, float roughness, float lacunarity, float offset, float gain) { | |
| 339 | float pwr = roughness; | |
| 340 | float signal = offset - abs(perlin_signed_3d(pos)); | |
| 341 | signal *= signal; | |
| 342 | float value = signal; | |
| 343 | for (int i = 1; i <= octaves; i++) { | |
| 344 | pos *= lacunarity; | |
| 345 | float weight = clamp(signal * gain, 0.0, 1.0); | |
| 346 | signal = offset - abs(perlin_signed_3d(pos)); | |
| 347 | signal *= signal; | |
| 348 | signal *= weight; | |
| 349 | value += signal * pwr; | |
| 350 | pwr *= roughness; | |
| 351 | } | |
| 352 | return value; | |
| 353 | } | |
| 354 | ||
| 355 | // Perlin Hetero Terrain | |
| 356 | float perlin_hetero(vec3 pos, int octaves, float roughness, float lacunarity, float offset) { | |
| 357 | float pwr = roughness; | |
| 358 | float value = offset + perlin_signed_3d(pos); | |
| 359 | pos *= lacunarity; | |
| 360 | for (int i = 1; i <= octaves; i++) { | |
| 361 | float increment = (perlin_signed_3d(pos) + offset) * pwr * value; | |
| 362 | value += increment; | |
| 363 | pwr *= roughness; | |
| 364 | pos *= lacunarity; | |
| 365 | } | |
| 366 | return value; | |
| 367 | } | |
| 368 | ||
| 369 | 2 | // ========================================================================= |
| 370 | 3 | // erosion_filter.inc â THE erosion filter. One copy. Compiled BOTH as GLSL |
| 371 | 4 | // and as C++ from this same text. |
| … | … | @@ -9,375 +9,27 @@ namespace godot { |
| 9 | 9 | |
| 10 | 10 | // --- Noise library --- |
| 11 | 11 | |
| 12 | String GpuCompiler::get_noise_library_glsl() { | |
| 13 | return String(R"( | |
| 14 | vec3 mod289_v3(vec3 x) { | |
| 15 | return x - floor(x * (1.0 / 289.0)) * 289.0; | |
| 16 | } | |
| 17 | ||
| 18 | vec4 mod289_v4(vec4 x) { | |
| 19 | return x - floor(x * (1.0 / 289.0)) * 289.0; | |
| 20 | } | |
| 21 | ||
| 22 | vec4 permute(vec4 x) { | |
| 23 | return mod289_v4(((x * 34.0) + 10.0) * x); | |
| 24 | } | |
| 25 | ||
| 26 | vec4 taylor_inv_sqrt(vec4 r) { | |
| 27 | return 1.79284291400159 - 0.85373472095314 * r; | |
| 28 | } | |
| 29 | ||
| 30 | float simplex_noise_3d(vec3 v) { | |
| 31 | const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); | |
| 32 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); | |
| 33 | ||
| 34 | // First corner | |
| 35 | vec3 i = floor(v + dot(v, C.yyy)); | |
| 36 | vec3 x0 = v - i + dot(i, C.xxx); | |
| 37 | ||
| 38 | // Other corners | |
| 39 | vec3 g = step(x0.yzx, x0.xyz); | |
| 40 | vec3 l = 1.0 - g; | |
| 41 | vec3 i1 = min(g.xyz, l.zxy); | |
| 42 | vec3 i2 = max(g.xyz, l.zxy); | |
| 43 | ||
| 44 | vec3 x1 = x0 - i1 + C.xxx; | |
| 45 | vec3 x2 = x0 - i2 + C.yyy; | |
| 46 | vec3 x3 = x0 - D.yyy; | |
| 47 | ||
| 48 | // Permutations | |
| 49 | i = mod289_v3(i); | |
| 50 | vec4 p = permute(permute(permute( | |
| 51 | i.z + vec4(0.0, i1.z, i2.z, 1.0)) | |
| 52 | + i.y + vec4(0.0, i1.y, i2.y, 1.0)) | |
| 53 | + i.x + vec4(0.0, i1.x, i2.x, 1.0)); | |
| 54 | ||
| 55 | // Gradients: 7x7 points over a square, mapped onto an octahedron | |
| 56 | float n_ = 0.142857142857; // 1.0 / 7.0 | |
| 57 | vec3 ns = n_ * D.wyz - D.xzx; | |
| 58 | ||
| 59 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); | |
| 60 | ||
| 61 | vec4 x_ = floor(j * ns.z); | |
| 62 | vec4 y_ = floor(j - 7.0 * x_); | |
| 63 | ||
| 64 | vec4 x = x_ * ns.x + ns.yyyy; | |
| 65 | vec4 y = y_ * ns.x + ns.yyyy; | |
| 66 | vec4 h = 1.0 - abs(x) - abs(y); | |
| 67 | ||
| 68 | vec4 b0 = vec4(x.xy, y.xy); | |
| 69 | vec4 b1 = vec4(x.zw, y.zw); | |
| 70 | ||
| 71 | vec4 s0 = floor(b0) * 2.0 + 1.0; | |
| 72 | vec4 s1 = floor(b1) * 2.0 + 1.0; | |
| 73 | vec4 sh = -step(h, vec4(0.0)); | |
| 74 | ||
| 75 | vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; | |
| 76 | vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; | |
| 77 | ||
| 78 | vec3 p0 = vec3(a0.xy, h.x); | |
| 79 | vec3 p1 = vec3(a0.zw, h.y); | |
| 80 | vec3 p2 = vec3(a1.xy, h.z); | |
| 81 | vec3 p3 = vec3(a1.zw, h.w); | |
| 82 | ||
| 83 | // Normalise gradients | |
| 84 | vec4 norm = taylor_inv_sqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); | |
| 85 | p0 *= norm.x; | |
| 86 | p1 *= norm.y; | |
| 87 | p2 *= norm.z; | |
| 88 | p3 *= norm.w; | |
| 89 | ||
| 90 | // Mix contributions from the four corners | |
| 91 | vec4 m = max(0.5 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); | |
| 92 | m = m * m; | |
| 93 | return 105.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); | |
| 94 | } | |
| 95 | ||
| 96 | float noise_fbm(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 97 | float value = 0.0; | |
| 98 | float amplitude = 1.0; | |
| 99 | float frequency = 1.0; | |
| 100 | float max_amp = 0.0; | |
| 101 | ||
| 102 | for (int i = 0; i < octaves; i++) { | |
| 103 | value += amplitude * simplex_noise_3d(pos * frequency); | |
| 104 | max_amp += amplitude; | |
| 105 | amplitude *= roughness; | |
| 106 | frequency *= lacunarity; | |
| 107 | } | |
| 108 | ||
| 109 | return value / max_amp; | |
| 110 | } | |
| 111 | ||
| 112 | // Voronoi noise — distance to nearest cell center | |
| 113 | float noise_voronoi(vec3 pos) { | |
| 114 | vec3 i_pos = floor(pos); | |
| 115 | vec3 f_pos = fract(pos); | |
| 116 | float min_dist = 1e10; | |
| 117 | ||
| 118 | for (int x = -1; x <= 1; x++) { | |
| 119 | for (int y = -1; y <= 1; y++) { | |
| 120 | for (int z = -1; z <= 1; z++) { | |
| 121 | vec3 offset = vec3(float(x), float(y), float(z)); | |
| 122 | vec3 cell = i_pos + offset; | |
| 123 | // Hash-based random point in cell | |
| 124 | vec3 r = fract(sin(vec3( | |
| 125 | dot(cell, vec3(127.1, 311.7, 74.7)), | |
| 126 | dot(cell, vec3(269.5, 183.3, 246.1)), | |
| 127 | dot(cell, vec3(113.5, 271.9, 124.6)) | |
| 128 | )) * 43758.5453); | |
| 129 | vec3 cell_pos = offset + r - f_pos; | |
| 130 | float d = length(cell_pos); | |
| 131 | min_dist = min(min_dist, d); | |
| 132 | } | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | return min_dist; | |
| 137 | } | |
| 138 | ||
| 139 | // Musgrave Multifractal | |
| 140 | float noise_musgrave_multi(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 141 | float value = 1.0; | |
| 142 | float frequency = 1.0; | |
| 143 | ||
| 144 | for (int i = 0; i < octaves; i++) { | |
| 145 | float n = simplex_noise_3d(pos * frequency); | |
| 146 | value *= (n * roughness + 1.0); | |
| 147 | frequency *= lacunarity; | |
| 148 | } | |
| 149 | ||
| 150 | return value; | |
| 151 | } | |
| 152 | ||
| 153 | // Musgrave Heterogeneous | |
| 154 | float noise_musgrave_hetero(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 155 | float value = simplex_noise_3d(pos) + 1.0; | |
| 156 | float frequency = lacunarity; | |
| 157 | float amplitude = roughness; | |
| 158 | ||
| 159 | for (int i = 1; i < octaves; i++) { | |
| 160 | float n = (simplex_noise_3d(pos * frequency) + 1.0) * amplitude * value; | |
| 161 | value += n; | |
| 162 | frequency *= lacunarity; | |
| 163 | amplitude *= roughness; | |
| 164 | } | |
| 165 | ||
| 166 | return value; | |
| 167 | } | |
| 168 | ||
| 169 | // Musgrave Hybrid Multifractal | |
| 170 | float noise_musgrave_hybrid(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 171 | float value = simplex_noise_3d(pos) + 1.0; | |
| 172 | float weight = value; | |
| 173 | float frequency = lacunarity; | |
| 174 | float amplitude = roughness; | |
| 175 | ||
| 176 | for (int i = 1; i < octaves; i++) { | |
| 177 | weight = min(weight, 1.0); | |
| 178 | float n = (simplex_noise_3d(pos * frequency) + 1.0) * amplitude; | |
| 179 | value += weight * n; | |
| 180 | weight *= n; | |
| 181 | frequency *= lacunarity; | |
| 182 | amplitude *= roughness; | |
| 183 | } | |
| 184 | ||
| 185 | return value; | |
| 186 | } | |
| 187 | ||
| 188 | // Musgrave Ridged Multifractal | |
| 189 | float noise_musgrave_ridged(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 190 | float value = 0.0; | |
| 191 | float frequency = 1.0; | |
| 192 | float amplitude = 1.0; | |
| 193 | float weight = 1.0; | |
| 194 | ||
| 195 | for (int i = 0; i < octaves; i++) { | |
| 196 | float n = abs(simplex_noise_3d(pos * frequency)); | |
| 197 | n = 1.0 - n; | |
| 198 | n *= n; | |
| 199 | n *= weight; | |
| 200 | weight = clamp(n * 2.0, 0.0, 1.0); | |
| 201 | value += n * amplitude; | |
| 202 | frequency *= lacunarity; | |
| 203 | amplitude *= roughness; | |
| 204 | } | |
| 205 | ||
| 206 | return value; | |
| 207 | } | |
| 208 | ||
| 209 | // ========================================================================= | |
| 210 | // Perlin noise — Blender's exact implementation (Jenkins Lookup3 hash) | |
| 211 | // ========================================================================= | |
| 212 | ||
| 213 | #define PERLIN_ROT(x, k) (((x) << (k)) | ((x) >> (32u - (k)))) | |
| 214 | ||
| 215 | uint jenkins_hash3(uint kx, uint ky, uint kz) { | |
| 216 | uint a, b, c; | |
| 217 | a = b = c = 0xdeadbeefu + (3u << 2u) + 13u; | |
| 218 | c += kz; b += ky; a += kx; | |
| 219 | // hash_bit_final | |
| 220 | c ^= b; c -= PERLIN_ROT(b, 14u); | |
| 221 | a ^= c; a -= PERLIN_ROT(c, 11u); | |
| 222 | b ^= a; b -= PERLIN_ROT(a, 25u); | |
| 223 | c ^= b; c -= PERLIN_ROT(b, 16u); | |
| 224 | a ^= c; a -= PERLIN_ROT(c, 4u); | |
| 225 | b ^= a; b -= PERLIN_ROT(a, 14u); | |
| 226 | c ^= b; c -= PERLIN_ROT(b, 24u); | |
| 227 | return c; | |
| 228 | } | |
| 229 | ||
| 230 | float perlin_fade(float t) { | |
| 231 | return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); | |
| 232 | } | |
| 233 | ||
| 234 | float perlin_negate_if(float val, uint cond) { | |
| 235 | return (cond != 0u) ? -val : val; | |
| 236 | } | |
| 237 | ||
| 238 | float perlin_noise_grad(uint hash, float x, float y, float z) { | |
| 239 | uint h = hash & 15u; | |
| 240 | float u = h < 8u ? x : y; | |
| 241 | float vt = ((h == 12u) || (h == 14u)) ? x : z; | |
| 242 | float v = h < 4u ? y : vt; | |
| 243 | return perlin_negate_if(u, h & 1u) + perlin_negate_if(v, h & 2u); | |
| 244 | } | |
| 245 | ||
| 246 | float perlin_tri_mix( | |
| 247 | float v0, float v1, float v2, float v3, | |
| 248 | float v4, float v5, float v6, float v7, | |
| 249 | float x, float y, float z) { | |
| 250 | float x1 = 1.0 - x; | |
| 251 | float y1 = 1.0 - y; | |
| 252 | float z1 = 1.0 - z; | |
| 253 | return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) + | |
| 254 | z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x)); | |
| 255 | } | |
| 256 | ||
| 257 | float perlin_noise_3d(vec3 p) { | |
| 258 | int X = int(floor(p.x)); float fx = p.x - floor(p.x); | |
| 259 | int Y = int(floor(p.y)); float fy = p.y - floor(p.y); | |
| 260 | int Z = int(floor(p.z)); float fz = p.z - floor(p.z); | |
| 261 | ||
| 262 | float u = perlin_fade(fx); | |
| 263 | float v = perlin_fade(fy); | |
| 264 | float w = perlin_fade(fz); | |
| 265 | ||
| 266 | return perlin_tri_mix( | |
| 267 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y), uint(Z)), fx, fy, fz), | |
| 268 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y), uint(Z)), fx-1, fy, fz), | |
| 269 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y+1), uint(Z)), fx, fy-1, fz), | |
| 270 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y+1), uint(Z)), fx-1, fy-1, fz), | |
| 271 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y), uint(Z+1)), fx, fy, fz-1), | |
| 272 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y), uint(Z+1)), fx-1, fy, fz-1), | |
| 273 | perlin_noise_grad(jenkins_hash3(uint(X), uint(Y+1), uint(Z+1)), fx, fy-1, fz-1), | |
| 274 | perlin_noise_grad(jenkins_hash3(uint(X+1), uint(Y+1), uint(Z+1)), fx-1, fy-1, fz-1), | |
| 275 | u, v, w); | |
| 276 | } | |
| 277 | ||
| 278 | float perlin_signed_3d(vec3 p) { | |
| 279 | // Precision correction for very large coordinates (matches Blender noise.cc). | |
| 280 | // For |x| < 1000000, this is a no-op (c=0, fmod returns x unchanged). | |
| 281 | // CRITICAL: Use sign-preserving fmod, NOT GLSL mod(). | |
| 282 | // GLSL mod(x, y) = x - y*floor(x/y) which wraps negatives to positive. | |
| 283 | // C++ fmodf(x, y) preserves the sign of x. For x in [-100000, 100000], | |
| 284 | // fmodf is identity. We replicate this with trunc-based modulo. | |
| 285 | vec3 c = vec3( | |
| 286 | abs(p.x) >= 1000000.0 ? 0.5 : 0.0, | |
| 287 | abs(p.y) >= 1000000.0 ? 0.5 : 0.0, | |
| 288 | abs(p.z) >= 1000000.0 ? 0.5 : 0.0); | |
| 289 | // trunc-based fmod: x - trunc(x/y) * y (preserves sign like C++ fmodf) | |
| 290 | p = p - trunc(p / 100000.0) * 100000.0 + c; | |
| 291 | return perlin_noise_3d(p) * 0.9820; | |
| 292 | } | |
| 293 | ||
| 294 | // Perlin fBM (matches Blender exactly, including fractional detail interpolation) | |
| 295 | float perlin_fbm(vec3 pos, int octaves, float roughness, float lacunarity, bool do_normalize) { | |
| 296 | float fscale = 1.0; | |
| 297 | float amp = 1.0; | |
| 298 | float maxamp = 0.0; | |
| 299 | float sum = 0.0; | |
| 300 | float detail = float(octaves); | |
| 301 | ||
| 302 | for (int i = 0; i <= octaves; i++) { | |
| 303 | sum += perlin_signed_3d(pos * fscale) * amp; | |
| 304 | maxamp += amp; | |
| 305 | amp *= roughness; | |
| 306 | fscale *= lacunarity; | |
| 307 | } | |
| 308 | // Fractional detail interpolation (matches CPU + Blender) | |
| 309 | float rmd = detail - floor(detail); | |
| 310 | if (rmd != 0.0) { | |
| 311 | float t = perlin_signed_3d(pos * fscale); | |
| 312 | float sum2 = sum + t * amp; | |
| 313 | float maxamp2 = maxamp + amp; | |
| 314 | return do_normalize ? | |
| 315 | (1.0 - rmd) * (0.5 * sum / maxamp + 0.5) + rmd * (0.5 * sum2 / maxamp2 + 0.5) : | |
| 316 | (1.0 - rmd) * sum + rmd * sum2; | |
| 317 | } | |
| 318 | return do_normalize ? 0.5 * sum / maxamp + 0.5 : sum; | |
| 319 | } | |
| 320 | ||
| 321 | // Perlin Multifractal | |
| 322 | float perlin_multi_fractal(vec3 pos, int octaves, float roughness, float lacunarity) { | |
| 323 | float value = 1.0; | |
| 324 | float pwr = 1.0; | |
| 325 | for (int i = 0; i <= octaves; i++) { | |
| 326 | value *= (pwr * perlin_signed_3d(pos) + 1.0); | |
| 327 | pwr *= roughness; | |
| 328 | pos *= lacunarity; | |
| 329 | } | |
| 330 | return value; | |
| 331 | } | |
| 332 | ||
| 333 | // Perlin Hybrid Multifractal | |
| 334 | float perlin_hybrid(vec3 pos, int octaves, float roughness, float lacunarity, float offset, float gain) { | |
| 335 | float pwr = 1.0; | |
| 336 | float value = 0.0; | |
| 337 | float weight = 1.0; | |
| 338 | for (int i = 0; (weight > 0.001) && (i <= octaves); i++) { | |
| 339 | weight = min(weight, 1.0); | |
| 340 | float signal = (perlin_signed_3d(pos) + offset) * pwr; | |
| 341 | pwr *= roughness; | |
| 342 | value += weight * signal; | |
| 343 | weight *= gain * signal; | |
| 344 | pos *= lacunarity; | |
| 345 | } | |
| 346 | return value; | |
| 347 | } | |
| 348 | ||
| 349 | // Perlin Ridged Multifractal | |
| 350 | float perlin_ridged(vec3 pos, int octaves, float roughness, float lacunarity, float offset, float gain) { | |
| 351 | float pwr = roughness; | |
| 352 | float signal = offset - abs(perlin_signed_3d(pos)); | |
| 353 | signal *= signal; | |
| 354 | float value = signal; | |
| 355 | for (int i = 1; i <= octaves; i++) { | |
| 356 | pos *= lacunarity; | |
| 357 | float weight = clamp(signal * gain, 0.0, 1.0); | |
| 358 | signal = offset - abs(perlin_signed_3d(pos)); | |
| 359 | signal *= signal; | |
| 360 | signal *= weight; | |
| 361 | value += signal * pwr; | |
| 362 | pwr *= roughness; | |
| 363 | } | |
| 364 | return value; | |
| 365 | } | |
| 366 | ||
| 367 | // Perlin Hetero Terrain | |
| 368 | float perlin_hetero(vec3 pos, int octaves, float roughness, float lacunarity, float offset) { | |
| 369 | float pwr = roughness; | |
| 370 | float value = offset + perlin_signed_3d(pos); | |
| 371 | pos *= lacunarity; | |
| 372 | for (int i = 1; i <= octaves; i++) { | |
| 373 | float increment = (perlin_signed_3d(pos) + offset) * pwr * value; | |
| 374 | value += increment; | |
| 375 | pwr *= roughness; | |
| 376 | pos *= lacunarity; | |
| 377 | } | |
| 378 | return value; | |
| 379 | } | |
| 380 | )") | |
| 12 | String GpuCompiler::get_erosion_library_glsl() { | |
| 13 | // THE HAND-WRITTEN NOISE LIBRARY THAT USED TO BE HERE IS GONE. | |
| 14 | // | |
| 15 | // ~366 lines of GLSL: an Ashima simplex, a Jenkins hash, a Perlin with its | |
| 16 | // five fractal variants, a Voronoi and four musgrave forms. Every one of | |
| 17 | // them was a SECOND implementation of something proc-nodes also had in | |
| 18 | // C++, and nothing compared any pair. Two of them were live -- noise_fbm | |
| 19 | // for the Simplex Noise node and perlin_fbm for Displace -- and the rest | |
| 20 | // had no caller at all. | |
| 21 | // | |
| 22 | // Both live ones now emit calls into src/shared/*.inc, the same text their | |
| 23 | // execute() runs, and they bring it with them through | |
| 24 | // compile_glsl_prelude(). The deletion is a relocation and not a change: | |
| 25 | // the C++ that was deleted alongside this agreed with the .inc replacing | |
| 26 | // it on 660000/660000 comparisons for Perlin and 240000/240000 for | |
| 27 | // simplex, bit-for-bit. | |
| 28 | // | |
| 29 | // What remains below is the erosion filter and its wrapper globals, which | |
| 30 | // are already one text (src/shared/erosion_filter.inc) and are injected | |
| 31 | // wholesale for the erosion family. | |
| 32 | return String() | |
| 381 | 33 | // THE erosion filter, generated from src/shared/erosion_filter.inc by |
| 382 | 34 | // tools/gen_erosion_glsl.py. It used to be inlined in the literal above, |
| 383 | 35 | // and dc-terrain carried a hand-written C++ transcription of it that had |
| … | … | @@ -509,6 +161,7 @@ String GpuCompiler::compile_batch( |
| 509 | 161 | ctx.graph = p_graph; |
| 510 | 162 | |
| 511 | 163 | String main_body; |
| 164 | String preludes; | |
| 512 | 165 | bool needs_noise = false; |
| 513 | 166 | |
| 514 | 167 | for (int i = 0; i < p_nodes.size(); i++) { |
| … | … | @@ -522,11 +175,18 @@ String GpuCompiler::compile_batch( |
| 522 | 175 | continue; |
| 523 | 176 | } |
| 524 | 177 | |
| 525 | if (glsl.find("noise_fbm") != -1 || glsl.find("simplex_noise_3d") != -1 || | |
| 526 | glsl.find("noise_voronoi") != -1 || glsl.find("noise_musgrave") != -1 || | |
| 527 | glsl.find("perlin_") != -1 || glsl.find("erosion_") != -1 || | |
| 528 | glsl.find("grad_noised") != -1 || glsl.find("fractal_noise_2d") != -1 || | |
| 529 | glsl.find("gullies_2d") != -1 || glsl.find("magnitude_sum") != -1) { | |
| 178 | // A node that needs shared source now SAYS SO, the way the density | |
| 179 | // path has always worked -- rather than this function sniffing the | |
| 180 | // emitted text for names. The substring list below it is what remains | |
| 181 | // for the erosion family, which is still injected wholesale. | |
| 182 | String pre = node->compile_glsl_prelude(graph_idx); | |
| 183 | if (!pre.is_empty() && preludes.find(pre) == -1) { | |
| 184 | preludes += pre; | |
| 185 | } | |
| 186 | ||
| 187 | if (glsl.find("erosion_") != -1 || glsl.find("grad_noised") != -1 || | |
| 188 | glsl.find("fractal_noise_2d") != -1 || glsl.find("gullies_2d") != -1 || | |
| 189 | glsl.find("magnitude_sum") != -1) { | |
| 530 | 190 | needs_noise = true; |
| 531 | 191 | } |
| 532 | 192 | |
| … | … | @@ -535,7 +195,10 @@ String GpuCompiler::compile_batch( |
| 535 | 195 | } |
| 536 | 196 | |
| 537 | 197 | String shader = get_shader_template(); |
| 538 | shader = shader.replace("NOISE_LIBRARY_PLACEHOLDER", needs_noise ? get_noise_library_glsl() : ""); | |
| 198 | // Preludes first: each is #ifndef-guarded, so a second copy is dropped by | |
| 199 | // the preprocessor rather than by anyone reasoning about ordering. | |
| 200 | shader = shader.replace("NOISE_LIBRARY_PLACEHOLDER", | |
| 201 | preludes + (needs_noise ? get_erosion_library_glsl() : String())); | |
| 539 | 202 | shader = shader.replace("NODE_CODE_PLACEHOLDER", ""); |
| 540 | 203 | shader = shader.replace("MAIN_BODY_PLACEHOLDER", main_body); |
| 541 | 204 | |
| … | … | @@ -654,7 +317,7 @@ String GpuCompiler::compile_to_function( |
| 654 | 317 | |
| 655 | 318 | // Include noise library if needed |
| 656 | 319 | if (needs_noise) { |
| 657 | result += get_noise_library_glsl(); | |
| 320 | result += get_erosion_library_glsl(); | |
| 658 | 321 | result += "\n"; |
| 659 | 322 | } |
| 660 | 323 | result += preludes; |
| … | … | @@ -50,7 +50,7 @@ public: |
| 50 | 50 | const ProcNodeGraph *p_graph = nullptr); |
| 51 | 51 | |
| 52 | 52 | // GLSL noise library (simplex 3D + fBM) |
| 53 | static String get_noise_library_glsl(); | |
| 53 | static String get_erosion_library_glsl(); | |
| 54 | 54 | |
| 55 | 55 | // Full shader template with placeholders |
| 56 | 56 | static String get_shader_template(); |
| … | … | @@ -13,384 +13,21 @@ |
| 13 | 13 | #include "../gpu/blender_hash_glsl.gen.h" |
| 14 | 14 | #include "../gpu/blender_perlin_glsl.gen.h" |
| 15 | 15 | #include "../gpu/blender_voronoi_glsl.gen.h" |
| 16 | #include "../gpu/proc_simplex_glsl.gen.h" | |
| 16 | 17 | |
| 17 | 18 | namespace godot { |
| 18 | 19 | |
| 19 | // --- Simplex noise implementation (3D) --- | |
| 20 | // Ashima Arts polynomial permutation — matches GPU (gpu_compiler.cpp) and | |
| 21 | // Blender GeoNodes ProcNodes_SimplexNoise group. IEEE 754 deterministic: | |
| 22 | // no lookup tables, no sin(), only multiply/add/mod/floor/dot. | |
| 23 | ||
| 24 | static inline float mod289(float x) { | |
| 25 | return x - floorf(x * (1.0f / 289.0f)) * 289.0f; | |
| 26 | } | |
| 27 | ||
| 28 | static inline float permute(float x) { | |
| 29 | return mod289(((x * 34.0f) + 10.0f) * x); | |
| 30 | } | |
| 31 | ||
| 32 | static inline float taylor_inv_sqrt(float r) { | |
| 33 | return 1.79284291400159f - 0.85373472095314f * r; | |
| 34 | } | |
| 35 | ||
| 36 | static inline float dot3(float ax, float ay, float az, float bx, float by, float bz) { | |
| 37 | return ax * bx + ay * by + az * bz; | |
| 38 | } | |
| 39 | ||
| 40 | static float simplex_noise_3d(float xin, float yin, float zin) { | |
| 41 | const float C1 = 1.0f / 6.0f; // G3 | |
| 42 | const float C2 = 1.0f / 3.0f; // F3 | |
| 43 | ||
| 44 | // First corner | |
| 45 | float s = (xin + yin + zin) * C2; | |
| 46 | float ix = floorf(xin + s); | |
| 47 | float iy = floorf(yin + s); | |
| 48 | float iz = floorf(zin + s); | |
| 49 | ||
| 50 | float t = (ix + iy + iz) * C1; | |
| 51 | float x0 = xin - ix + t; | |
| 52 | float y0 = yin - iy + t; | |
| 53 | float z0 = zin - iz + t; | |
| 54 | ||
| 55 | // Simplex traversal (branchless Ashima step/min/max) | |
| 56 | float gx = (x0 >= y0) ? 1.0f : 0.0f; // step(y0, x0) | |
| 57 | float gy = (y0 >= z0) ? 1.0f : 0.0f; // step(z0, y0) | |
| 58 | float gz = (z0 >= x0) ? 1.0f : 0.0f; // step(x0, z0) | |
| 59 | float lx = 1.0f - gx; | |
| 60 | float ly = 1.0f - gy; | |
| 61 | float lz = 1.0f - gz; | |
| 62 | ||
| 63 | float i1x = fminf(gx, lz); | |
| 64 | float i1y = fminf(gy, lx); | |
| 65 | float i1z = fminf(gz, ly); | |
| 66 | float i2x = fmaxf(gx, lz); | |
| 67 | float i2y = fmaxf(gy, lx); | |
| 68 | float i2z = fmaxf(gz, ly); | |
| 69 | ||
| 70 | float x1 = x0 - i1x + C1; | |
| 71 | float y1 = y0 - i1y + C1; | |
| 72 | float z1 = z0 - i1z + C1; | |
| 73 | float x2 = x0 - i2x + C2; | |
| 74 | float y2 = y0 - i2y + C2; | |
| 75 | float z2 = z0 - i2z + C2; | |
| 76 | float x3 = x0 - 0.5f; | |
| 77 | float y3 = y0 - 0.5f; | |
| 78 | float z3 = z0 - 0.5f; | |
| 79 | ||
| 80 | // Permutations (polynomial, no lookup table) | |
| 81 | ix = mod289(ix); | |
| 82 | iy = mod289(iy); | |
| 83 | iz = mod289(iz); | |
| 84 | ||
| 85 | float p0 = permute(permute(permute(iz) + iy) + ix); | |
| 86 | float p1 = permute(permute(permute(iz + i1z) + iy + i1y) + ix + i1x); | |
| 87 | float p2 = permute(permute(permute(iz + i2z) + iy + i2y) + ix + i2x); | |
| 88 | float p3 = permute(permute(permute(iz + 1.0f) + iy + 1.0f) + ix + 1.0f); | |
| 89 | ||
| 90 | // Gradients: 7x7 points over octahedron | |
| 91 | const float n_ = 1.0f / 7.0f; | |
| 92 | // ns = n_ * D.wyz - D.xzx where D = (0, 0.5, 1, 2) | |
| 93 | float ns_x = n_ * 2.0f - 0.0f; // 2/7 | |
| 94 | float ns_y = n_ * 0.5f - 1.0f; // -0.928571... | |
| 95 | float ns_z = n_ * 0.0f - 0.0f; // 0 -- wait, let me recalculate | |
| 96 | // D.wyz = (2.0, 0.0, 0.5), D.xzx = (0.0, 1.0, 0.0) | |
| 97 | // ns = n_ * (2.0, 0.0, 0.5) - (0.0, 1.0, 0.0) | |
| 98 | // ns = (2/7, -1, 1/14) = (0.285714, -1.0, 0.071428) | |
| 99 | float ns_xx = 2.0f * n_; // 0.285714 | |
| 100 | float ns_yy = -1.0f; // -1.0 | |
| 101 | float ns_zz = 0.5f * n_; // 0.071428 | |
| 102 | ||
| 103 | // For each corner, compute gradient from permutation value | |
| 104 | auto grad_from_p = [&](float pv, float &out_gx, float &out_gy, float &out_gz) { | |
| 105 | float j = pv - 49.0f * floorf(pv * ns_zz * ns_zz); | |
| 106 | float x_ = floorf(j * ns_zz); | |
| 107 | float y_ = floorf(j - 7.0f * x_); | |
| 108 | float gxx = x_ * ns_xx + ns_yy; | |
| 109 | float gyy = y_ * ns_xx + ns_yy; | |
| 110 | float hh = 1.0f - fabsf(gxx) - fabsf(gyy); | |
| 111 | ||
| 112 | // Sign correction | |
| 113 | float sx = (gxx < 0.0f) ? -1.0f : 1.0f; | |
| 114 | float sy = (gyy < 0.0f) ? -1.0f : 1.0f; | |
| 115 | float b0x = gxx; | |
| 116 | float b0y = gyy; | |
| 117 | float sh = (hh < 0.0f) ? -1.0f : 0.0f; | |
| 118 | out_gx = b0x + floorf(b0x) * 2.0f * sh + sh; | |
| 119 | // Simpler: match GLSL exactly | |
| 120 | // a0 = b0.xzyw + s0.xzyw * sh.xxyy | |
| 121 | // For a single corner, this simplifies to: | |
| 122 | float s0x = floorf(b0x) * 2.0f + 1.0f; | |
| 123 | float s0y = floorf(b0y) * 2.0f + 1.0f; | |
| 124 | float shv = (hh < 0.0f) ? -1.0f : 0.0f; | |
| 125 | out_gx = b0x + s0x * shv; | |
| 126 | out_gy = b0y + s0y * shv; | |
| 127 | out_gz = hh; | |
| 128 | ||
| 129 | // Normalize | |
| 130 | float len2 = out_gx * out_gx + out_gy * out_gy + out_gz * out_gz; | |
| 131 | float inv_len = taylor_inv_sqrt(len2); | |
| 132 | out_gx *= inv_len; | |
| 133 | out_gy *= inv_len; | |
| 134 | out_gz *= inv_len; | |
| 135 | }; | |
| 136 | ||
| 137 | float g0x, g0y, g0z, g1x, g1y, g1z, g2x, g2y, g2z, g3x, g3y, g3z; | |
| 138 | grad_from_p(p0, g0x, g0y, g0z); | |
| 139 | grad_from_p(p1, g1x, g1y, g1z); | |
| 140 | grad_from_p(p2, g2x, g2y, g2z); | |
| 141 | grad_from_p(p3, g3x, g3y, g3z); | |
| 142 | ||
| 143 | // Mix contributions from four corners | |
| 144 | float m0 = fmaxf(0.5f - dot3(x0, y0, z0, x0, y0, z0), 0.0f); | |
| 145 | float m1 = fmaxf(0.5f - dot3(x1, y1, z1, x1, y1, z1), 0.0f); | |
| 146 | float m2 = fmaxf(0.5f - dot3(x2, y2, z2, x2, y2, z2), 0.0f); | |
| 147 | float m3 = fmaxf(0.5f - dot3(x3, y3, z3, x3, y3, z3), 0.0f); | |
| 148 | ||
| 149 | m0 *= m0; m1 *= m1; m2 *= m2; m3 *= m3; | |
| 150 | ||
| 151 | return 105.0f * ( | |
| 152 | m0 * m0 * dot3(g0x, g0y, g0z, x0, y0, z0) + | |
| 153 | m1 * m1 * dot3(g1x, g1y, g1z, x1, y1, z1) + | |
| 154 | m2 * m2 * dot3(g2x, g2y, g2z, x2, y2, z2) + | |
| 155 | m3 * m3 * dot3(g3x, g3y, g3z, x3, y3, z3)); | |
| 156 | } | |
| 157 | ||
| 158 | // fBM (fractal Brownian motion) using simplex noise | |
| 159 | static float fbm_noise_3d(float x, float y, float z, int octaves, float roughness, float lacunarity) { | |
| 160 | float value = 0.0f; | |
| 161 | float amplitude = 1.0f; | |
| 162 | float frequency = 1.0f; | |
| 163 | float max_amp = 0.0f; | |
| 164 | ||
| 165 | for (int i = 0; i < octaves; i++) { | |
| 166 | value += amplitude * simplex_noise_3d(x * frequency, y * frequency, z * frequency); | |
| 167 | max_amp += amplitude; | |
| 168 | amplitude *= roughness; | |
| 169 | frequency *= lacunarity; | |
| 170 | } | |
| 171 | ||
| 172 | return value / max_amp; | |
| 173 | } | |
| 174 | ||
| 175 | // ========================================================================= | |
| 176 | // Perlin noise — Blender's exact implementation (Jenkins Lookup3 hash) | |
| 177 | // Source: blender/blenlib/intern/noise.cc | |
| 178 | // ========================================================================= | |
| 179 | ||
| 180 | // --- Jenkins Lookup3 Hash --- | |
| 181 | ||
| 182 | static inline uint32_t hash_bit_rotate(uint32_t x, uint32_t k) { | |
| 183 | return (x << k) | (x >> (32 - k)); | |
| 184 | } | |
| 185 | ||
| 186 | static inline void hash_bit_mix(uint32_t &a, uint32_t &b, uint32_t &c) { | |
| 187 | a -= c; a ^= hash_bit_rotate(c, 4); c += b; | |
| 188 | b -= a; b ^= hash_bit_rotate(a, 6); a += c; | |
| 189 | c -= b; c ^= hash_bit_rotate(b, 8); b += a; | |
| 190 | a -= c; a ^= hash_bit_rotate(c, 16); c += b; | |
| 191 | b -= a; b ^= hash_bit_rotate(a, 19); a += c; | |
| 192 | c -= b; c ^= hash_bit_rotate(b, 4); b += a; | |
| 193 | } | |
| 194 | ||
| 195 | static inline void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c) { | |
| 196 | c ^= b; c -= hash_bit_rotate(b, 14); | |
| 197 | a ^= c; a -= hash_bit_rotate(c, 11); | |
| 198 | b ^= a; b -= hash_bit_rotate(a, 25); | |
| 199 | c ^= b; c -= hash_bit_rotate(b, 16); | |
| 200 | a ^= c; a -= hash_bit_rotate(c, 4); | |
| 201 | b ^= a; b -= hash_bit_rotate(a, 14); | |
| 202 | c ^= b; c -= hash_bit_rotate(b, 24); | |
| 203 | } | |
| 204 | ||
| 205 | static uint32_t jenkins_hash(uint32_t kx, uint32_t ky, uint32_t kz) { | |
| 206 | uint32_t a, b, c; | |
| 207 | a = b = c = 0xdeadbeef + (3 << 2) + 13; | |
| 208 | c += kz; | |
| 209 | b += ky; | |
| 210 | a += kx; | |
| 211 | hash_bit_final(a, b, c); | |
| 212 | return c; | |
| 213 | } | |
| 214 | ||
| 215 | // --- Perlin primitives --- | |
| 216 | ||
| 217 | static inline float perlin_fade(float t) { | |
| 218 | return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); | |
| 219 | } | |
| 220 | ||
| 221 | static inline float perlin_negate_if(float value, uint32_t condition) { | |
| 222 | return (condition != 0u) ? -value : value; | |
| 223 | } | |
| 224 | ||
| 225 | static inline float perlin_noise_grad(uint32_t hash, float x, float y, float z) { | |
| 226 | uint32_t h = hash & 15u; | |
| 227 | float u = h < 8u ? x : y; | |
| 228 | float vt = (h == 12u || h == 14u) ? x : z; | |
| 229 | float v = h < 4u ? y : vt; | |
| 230 | return perlin_negate_if(u, h & 1u) + perlin_negate_if(v, h & 2u); | |
| 231 | } | |
| 232 | ||
| 233 | static inline float perlin_floor_frac(float x, int &i) { | |
| 234 | float x_floor = floorf(x); | |
| 235 | i = (int)x_floor; | |
| 236 | return x - x_floor; | |
| 237 | } | |
| 238 | ||
| 239 | // Trilinear interpolation | |
| 240 | static inline float perlin_tri_mix( | |
| 241 | float v0, float v1, float v2, float v3, | |
| 242 | float v4, float v5, float v6, float v7, | |
| 243 | float x, float y, float z) { | |
| 244 | float x1 = 1.0f - x; | |
| 245 | float y1 = 1.0f - y; | |
| 246 | float z1 = 1.0f - z; | |
| 247 | return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) + | |
| 248 | z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x)); | |
| 249 | } | |
| 250 | ||
| 251 | // --- Core 3D Perlin noise --- | |
| 252 | ||
| 253 | static float perlin_noise_3d(float px, float py, float pz) { | |
| 254 | int X, Y, Z; | |
| 255 | float fx = perlin_floor_frac(px, X); | |
| 256 | float fy = perlin_floor_frac(py, Y); | |
| 257 | float fz = perlin_floor_frac(pz, Z); | |
| 258 | ||
| 259 | float u = perlin_fade(fx); | |
| 260 | float v = perlin_fade(fy); | |
| 261 | float w = perlin_fade(fz); | |
| 262 | ||
| 263 | return perlin_tri_mix( | |
| 264 | perlin_noise_grad(jenkins_hash(X, Y, Z), fx, fy, fz), | |
| 265 | perlin_noise_grad(jenkins_hash(X + 1, Y, Z), fx - 1, fy, fz), | |
| 266 | perlin_noise_grad(jenkins_hash(X, Y + 1, Z), fx, fy - 1, fz), | |
| 267 | perlin_noise_grad(jenkins_hash(X + 1, Y + 1, Z), fx - 1, fy - 1, fz), | |
| 268 | perlin_noise_grad(jenkins_hash(X, Y, Z + 1), fx, fy, fz - 1), | |
| 269 | perlin_noise_grad(jenkins_hash(X + 1, Y, Z + 1), fx - 1, fy, fz - 1), | |
| 270 | perlin_noise_grad(jenkins_hash(X, Y + 1, Z + 1), fx, fy - 1, fz - 1), | |
| 271 | perlin_noise_grad(jenkins_hash(X + 1, Y + 1, Z + 1), fx - 1, fy - 1, fz - 1), | |
| 272 | u, v, w); | |
| 273 | } | |
| 274 | ||
| 275 | // Signed Perlin noise in [-1, 1] with precision correction (matches Blender exactly) | |
| 276 | static float perlin_signed_3d(float x, float y, float z) { | |
| 277 | float cx = (fabsf(x) >= 1000000.0f) ? 0.5f : 0.0f; | |
| 278 | float cy = (fabsf(y) >= 1000000.0f) ? 0.5f : 0.0f; | |
| 279 | float cz = (fabsf(z) >= 1000000.0f) ? 0.5f : 0.0f; | |
| 280 | x = fmodf(x, 100000.0f) + cx; | |
| 281 | y = fmodf(y, 100000.0f) + cy; | |
| 282 | z = fmodf(z, 100000.0f) + cz; | |
| 283 | return perlin_noise_3d(x, y, z) * 0.9820f; | |
| 284 | } | |
| 285 | ||
| 286 | // --- Perlin fractal variants (matching Blender's noise.cc) --- | |
| 287 | ||
| 288 | static float perlin_fbm_3d(float x, float y, float z, float detail, | |
| 289 | float roughness, float lacunarity, bool p_normalize) { | |
| 290 | float fscale = 1.0f; | |
| 291 | float amp = 1.0f; | |
| 292 | float maxamp = 0.0f; | |
| 293 | float sum = 0.0f; | |
| 294 | ||
| 295 | for (int i = 0; i <= (int)detail; i++) { | |
| 296 | sum += perlin_signed_3d(x * fscale, y * fscale, z * fscale) * amp; | |
| 297 | maxamp += amp; | |
| 298 | amp *= roughness; | |
| 299 | fscale *= lacunarity; | |
| 300 | } | |
| 301 | float rmd = detail - floorf(detail); | |
| 302 | if (rmd != 0.0f) { | |
| 303 | float t = perlin_signed_3d(x * fscale, y * fscale, z * fscale); | |
| 304 | float sum2 = sum + t * amp; | |
| 305 | float maxamp2 = maxamp + amp; | |
| 306 | return p_normalize ? | |
| 307 | (1.0f - rmd) * (0.5f * sum / maxamp + 0.5f) + rmd * (0.5f * sum2 / maxamp2 + 0.5f) : | |
| 308 | (1.0f - rmd) * sum + rmd * sum2; | |
| 309 | } | |
| 310 | return p_normalize ? 0.5f * sum / maxamp + 0.5f : sum; | |
| 311 | } | |
| 312 | ||
| 313 | static float perlin_multi_fractal_3d(float x, float y, float z, float detail, | |
| 314 | float roughness, float lacunarity) { | |
| 315 | float value = 1.0f; | |
| 316 | float pwr = 1.0f; | |
| 317 | float px = x, py = y, pz = z; | |
| 318 | ||
| 319 | for (int i = 0; i <= (int)detail; i++) { | |
| 320 | value *= (pwr * perlin_signed_3d(px, py, pz) + 1.0f); | |
| 321 | pwr *= roughness; | |
| 322 | px *= lacunarity; py *= lacunarity; pz *= lacunarity; | |
| 323 | } | |
| 324 | float rmd = detail - floorf(detail); | |
| 325 | if (rmd != 0.0f) { | |
| 326 | value *= (rmd * pwr * perlin_signed_3d(px, py, pz) + 1.0f); | |
| 327 | } | |
| 328 | return value; | |
| 329 | } | |
| 330 | ||
| 331 | static float perlin_hybrid_multi_fractal_3d(float x, float y, float z, float detail, | |
| 332 | float roughness, float lacunarity, float offset, float gain) { | |
| 333 | float pwr = 1.0f; | |
| 334 | float value = 0.0f; | |
| 335 | float weight = 1.0f; | |
| 336 | float px = x, py = y, pz = z; | |
| 337 | ||
| 338 | for (int i = 0; (weight > 0.001f) && (i <= (int)detail); i++) { | |
| 339 | weight = fminf(weight, 1.0f); | |
| 340 | float signal = (perlin_signed_3d(px, py, pz) + offset) * pwr; | |
| 341 | pwr *= roughness; | |
| 342 | value += weight * signal; | |
| 343 | weight *= gain * signal; | |
| 344 | px *= lacunarity; py *= lacunarity; pz *= lacunarity; | |
| 345 | } | |
| 346 | float rmd = detail - floorf(detail); | |
| 347 | if ((rmd != 0.0f) && (weight > 0.001f)) { | |
| 348 | weight = fminf(weight, 1.0f); | |
| 349 | float signal = (perlin_signed_3d(px, py, pz) + offset) * pwr; | |
| 350 | value += rmd * weight * signal; | |
| 351 | } | |
| 352 | return value; | |
| 353 | } | |
| 354 | ||
| 355 | static float perlin_ridged_multi_fractal_3d(float x, float y, float z, float detail, | |
| 356 | float roughness, float lacunarity, float offset, float gain) { | |
| 357 | float pwr = roughness; | |
| 358 | float signal = offset - fabsf(perlin_signed_3d(x, y, z)); | |
| 359 | signal *= signal; | |
| 360 | float value = signal; | |
| 361 | float px = x, py = y, pz = z; | |
| 362 | ||
| 363 | for (int i = 1; i <= (int)detail; i++) { | |
| 364 | px *= lacunarity; py *= lacunarity; pz *= lacunarity; | |
| 365 | float weight = CLAMP(signal * gain, 0.0f, 1.0f); | |
| 366 | signal = offset - fabsf(perlin_signed_3d(px, py, pz)); | |
| 367 | signal *= signal; | |
| 368 | signal *= weight; | |
| 369 | value += signal * pwr; | |
| 370 | pwr *= roughness; | |
| 371 | } | |
| 372 | return value; | |
| 373 | } | |
| 374 | ||
| 375 | static float perlin_hetero_terrain_3d(float x, float y, float z, float detail, | |
| 376 | float roughness, float lacunarity, float offset) { | |
| 377 | float pwr = roughness; | |
| 378 | float value = offset + perlin_signed_3d(x, y, z); | |
| 379 | float px = x * lacunarity, py = y * lacunarity, pz = z * lacunarity; | |
| 380 | ||
| 381 | for (int i = 1; i <= (int)detail; i++) { | |
| 382 | float increment = (perlin_signed_3d(px, py, pz) + offset) * pwr * value; | |
| 383 | value += increment; | |
| 384 | pwr *= roughness; | |
| 385 | px *= lacunarity; py *= lacunarity; pz *= lacunarity; | |
| 386 | } | |
| 387 | float rmd = detail - floorf(detail); | |
| 388 | if (rmd != 0.0f) { | |
| 389 | float increment = (perlin_signed_3d(px, py, pz) + offset) * pwr * value; | |
| 390 | value += rmd * increment; | |
| 391 | } | |
| 392 | return value; | |
| 393 | } | |
| 20 | // The Ashima simplex and Blender's Perlin used to live here, ~375 lines of | |
| 21 | // them, with their own Jenkins Lookup3 hash -- a FOURTH copy of it. Both were | |
| 22 | // paired with hand-written GLSL twins in GpuCompiler::get_noise_library_glsl() | |
| 23 | // and nothing compared either pair. | |
| 24 | // | |
| 25 | // They are src/shared/proc_simplex.inc and src/shared/blender_perlin.inc now, | |
| 26 | // compiled as C++ here through blender_noise_cpp_shim.h and as GLSL for the | |
| 27 | // shader. Deleting them was a RELOCATION, not a change, and that is measured | |
| 28 | // rather than asserted: the code removed here agreed with the .inc that | |
| 29 | // replaced it on 660000/660000 comparisons for Perlin and 240000/240000 for | |
| 30 | // simplex, bit-for-bit. | |
| 394 | 31 | |
| 395 | 32 | // --- PerlinNoiseNode --- |
| 396 | 33 | |
| … | … | @@ -607,15 +244,24 @@ void NoiseTextureNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 607 | 244 | if (s <= 0.0f) s = scale; |
| 608 | 245 | if (d <= 0) d = detail; |
| 609 | 246 | |
| 610 | float n = fbm_noise_3d(pos.x * s, pos.y * s, pos.z * s, d, roughness, lacunarity); | |
| 247 | float n = bnoisegl::ps_fbm_noise_3d(pos.x * s, pos.y * s, pos.z * s, d, roughness, lacunarity); | |
| 611 | 248 | float remapped = n * 0.5f + 0.5f; // -1..1 → 0..1 |
| 612 | 249 | |
| 613 | 250 | p_ctx.write_float(p_node_idx, 0, remapped); |
| 614 | 251 | p_ctx.write_color(p_node_idx, 1, Color(remapped, remapped, remapped, 1.0f)); |
| 615 | 252 | } |
| 616 | 253 | |
| 254 | String NoiseTextureNode::compile_glsl_prelude(int p_node_idx) const { | |
| 255 | return String(PROC_SIMPLEX_GLSL); | |
| 256 | } | |
| 257 | ||
| 617 | 258 | String NoiseTextureNode::compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const { |
| 618 | return "float noise_val = noise_fbm(pos * pc.param0, int(pc.param1), pc.param2, pc.param3) * 0.5 + 0.5;"; | |
| 259 | // ps_fbm_noise_3d is the SAME TEXT execute() runs. It used to be | |
| 260 | // noise_fbm() from the hand-written library, whose C++ twin was a | |
| 261 | // file-local copy in this file -- 240000/240000 identical, measured, which | |
| 262 | // is what made deleting the copy a relocation and not a change. | |
| 263 | return "vec3 nsp = pos * pc.param0;\n\tfloat noise_val = ps_fbm_noise_3d(nsp.x, nsp.y, " | |
| 264 | "nsp.z, int(pc.param1), pc.param2, pc.param3) * 0.5 + 0.5;"; | |
| 619 | 265 | } |
| 620 | 266 | |
| 621 | 267 | // --- DisplaceNode --- |
| … | … | @@ -669,7 +315,10 @@ void DisplaceNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 669 | 315 | float py = pos[i].z * noise_scale; |
| 670 | 316 | float pz = pos[i].y * noise_scale; |
| 671 | 317 | |
| 672 | float n = perlin_fbm_3d(px, py, pz, (float)octaves, rough, lac, true) * 2.0f - 1.0f; | |
| 318 | float n = bperlin::perlin_fbm<Vector3>(Vector3(px, py, pz), (float)octaves, rough, | |
| 319 | lac, true) * | |
| 320 | 2.0f - | |
| 321 | 1.0f; | |
| 673 | 322 | |
| 674 | 323 | Vector3 disp_dir = has_normals ? nor[i] : Vector3(0, 1, 0); |
| 675 | 324 | pos[i] += disp_dir * n * strength; |
| … | … | @@ -681,8 +330,18 @@ void DisplaceNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 681 | 330 | p_ctx.write_geometry(p_node_idx, 0, geo); |
| 682 | 331 | } |
| 683 | 332 | |
| 333 | String DisplaceNode::compile_glsl_prelude(int p_node_idx) const { | |
| 334 | return String(BLENDER_HASH_GLSL) + String(BLENDER_PERLIN_GLSL); | |
| 335 | } | |
| 336 | ||
| 684 | 337 | String DisplaceNode::compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const { |
| 685 | return "vec3 npos = vec3(-pos.x, pos.z, pos.y) * pc.param1;\n\tfloat n = perlin_fbm(npos, int(pc.param2), pc.param3, pc.param4, true) * 2.0 - 1.0;\n\tpos += normal * n * pc.param0;"; | |
| 338 | // bp_perlin_fbm_3 is the SAME TEXT execute() runs, via blender_perlin.inc. | |
| 339 | // It used to be perlin_fbm() from the hand-written library, against a | |
| 340 | // file-local C++ Perlin with its own Jenkins hash -- 660000/660000 | |
| 341 | // identical, measured, so this is a relocation and not a change. | |
| 342 | return "vec3 npos = vec3(-pos.x, pos.z, pos.y) * pc.param1;\n\tfloat n = " | |
| 343 | "bp_perlin_fbm_3(npos, float(int(pc.param2)), pc.param3, pc.param4, true) * 2.0 - " | |
| 344 | "1.0;\n\tpos += normal * n * pc.param0;"; | |
| 686 | 345 | } |
| 687 | 346 | |
| 688 | 347 | // --- CPU helper: hash-based random for Voronoi and white noise --- |
| … | … | @@ -22,6 +22,7 @@ protected: |
| 22 | 22 | float lacunarity = 2.0f; |
| 23 | 23 | |
| 24 | 24 | public: |
| 25 | String compile_glsl_prelude(int p_node_idx) const override; | |
| 25 | 26 | StringName get_node_type() const override { return "SimplexNoise"; } |
| 26 | 27 | String get_node_title() const override { return "Simplex Noise"; } |
| 27 | 28 | String get_node_category() const override { return "Texture"; } |
| … | … | @@ -95,6 +96,7 @@ public: |
| 95 | 96 | String get_node_title() const override { return "Displace"; } |
| 96 | 97 | String get_node_category() const override { return "Geometry"; } |
| 97 | 98 | bool supports_gpu() const override { return true; } |
| 99 | String compile_glsl_prelude(int p_node_idx) const override; | |
| 98 | 100 | |
| 99 | 101 | void execute(ProcEvalContext &p_ctx, int p_node_idx) override; |
| 100 | 102 | String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override; |
| … | … | @@ -19,7 +19,8 @@ |
| 19 | 19 | // function from the one the author was looking at in Blender. |
| 20 | 20 | // |
| 21 | 21 | // WHY IT MOVED. That fix left the hash written TWICE more: this port, and a |
| 22 | // hand-written GLSL copy in GpuCompiler::get_noise_library_glsl(). They | |
| 22 | // hand-written GLSL copy in GpuCompiler's noise library (deleted 2026-09-05, | |
| 23 | // once the last two nodes came off it). They | |
| 23 | 24 | // agreed -- measured, 22/22 exact, by tests/test_hash_parity.cpp -- but they |
| 24 | 25 | // agreed the way two transcriptions agree, which is to say nothing was |
| 25 | 26 | // stopping them from drifting the next time either was touched. The hash is |
| … | … | @@ -199,6 +199,7 @@ inline float exp(float x) { return std::exp(x); } |
| 199 | 199 | #include "blender_perlin.inc" |
| 200 | 200 | #include "blender_voronoi.inc" |
| 201 | 201 | #include "blender_ramp_blend.inc" |
| 202 | #include "proc_simplex.inc" | |
| 202 | 203 | |
| 203 | 204 | } // namespace bnoisegl |
| 204 | 205 | } // namespace godot |
| … | … | @@ -12,7 +12,7 @@ |
| 12 | 12 | // downstream changed. |
| 13 | 13 | // |
| 14 | 14 | // WHY IT MOVED. There were two Perlins: this port, and a hand-written GLSL |
| 15 | // copy in GpuCompiler::get_noise_library_glsl() carrying its own Jenkins hash, | |
| 15 | // copy in GpuCompiler's hand-written noise library, carrying its own Jenkins hash, | |
| 16 | 16 | // its own fade, its own gradients and the same unexplained 0.9820. Nothing |
| 17 | 17 | // compared them. The Noise Texture node could not be given GPU support without |
| 18 | 18 | // choosing which of the two to believe. |
| … | … | @@ -0,0 +1,220 @@ |
| 1 | // ============================================================================ | |
| 2 | // ASHIMA SIMPLEX -- ONE TEXT, COMPILED AS BOTH GLSL AND C++. | |
| 3 | // | |
| 4 | // ⚠ THIS ONE IS NOT BLENDER'S. Blender's Noise Texture is Perlin | |
| 5 | // (blender_perlin.inc); this is the Ashima Arts polynomial-permutation simplex | |
| 6 | // that proc-nodes' own "Simplex Noise" node uses, and that blender-bridge | |
| 7 | // reproduces on the Blender side as the ProcNodes_SimplexNoise node group. It | |
| 8 | // lives here for the same reason the others do: it existed TWICE, as a | |
| 9 | // file-local C++ copy in noise_nodes.cpp and as a hand-written GLSL copy in | |
| 10 | // GpuCompiler's hand-written noise library, and nothing compared them. | |
| 11 | // | |
| 12 | // IEEE 754 deterministic: no lookup tables, no sin(), only multiply, add, mod, | |
| 13 | // floor and dot -- which is why the same text can run on both machines. | |
| 14 | // | |
| 15 | // TRANSFORMED from the C++ that was in noise_nodes.cpp, not retyped. The | |
| 16 | // equivalence run against that original is what says the transformation was | |
| 17 | // faithful. | |
| 18 | // | |
| 19 | // ⛔ THIS FUNCTION IS BROKEN, AND IT WAS BROKEN BEFORE IT MOVED HERE. | |
| 20 | // | |
| 21 | // It returns values in the THOUSANDS where a simplex must be in [-1, 1]: | |
| 22 | // | |
| 23 | // simplex(0.50, 0.15, 0.85) = -7349.26 | |
| 24 | // simplex(1.20, 0.36, 2.04) = 5557.13 | |
| 25 | // fbm, 4 octaves = 15221.4 | |
| 26 | // | |
| 27 | // and SimplexNoiseNode maps that as n * 0.5 + 0.5, so its "Value" output -- | |
| 28 | // documented 0..1 -- is 7611 for an ordinary point. Not a large-coordinate | |
| 29 | // precision effect: the range is ~±1e5 at EVERY magnitude tested, |p| ~ 1 | |
| 30 | // included. Something in the port of Ashima's gradient or its 105.0 scaling is | |
| 31 | // wrong. | |
| 32 | // | |
| 33 | // ⚠ NOT INTRODUCED BY THE MOVE. This text is bit-identical to the file-local | |
| 34 | // C++ it replaced -- 240000/240000 -- and the hand-written GLSL twin agreed | |
| 35 | // with it too, which is exactly why nobody noticed: both implementations were | |
| 36 | // wrong in the same way, and a bridge that compares them to each other cannot | |
| 37 | // see it. It took comparing against the DEFINITION of a simplex to find. | |
| 38 | // | |
| 39 | // The parity gate therefore covers what it can honestly hold -- the raw | |
| 40 | // lattice and low octave counts, where CPU and GPU agree to 1e-5 -- and stops | |
| 41 | // short of pretending the accumulated fbm is meaningful. Fixing the arithmetic | |
| 42 | // changes what the node outputs and is its own change. | |
| 43 | // | |
| 44 | // THE SUBSET: the per-corner gradient was a LAMBDA with three reference | |
| 45 | // out-parameters. GLSL has neither, so it is a function returning a vec3; its | |
| 46 | // captures were constants derived from 1/7 and are recomputed inside it. | |
| 47 | // ============================================================================ | |
| 48 | ||
| 49 | #ifndef PN_PROC_SIMPLEX | |
| 50 | #define PN_PROC_SIMPLEX | |
| 51 | ||
| 52 | #ifndef BN_FN | |
| 53 | #define BN_FN | |
| 54 | #endif | |
| 55 | ||
| 56 | // --- Simplex noise implementation (3D) --- | |
| 57 | // Ashima Arts polynomial permutation — matches GPU (gpu_compiler.cpp) and | |
| 58 | // Blender GeoNodes ProcNodes_SimplexNoise group. IEEE 754 deterministic: | |
| 59 | // no lookup tables, no sin(), only multiply/add/mod/floor/dot. | |
| 60 | ||
| 61 | BN_FN float ps_mod289(float x) { | |
| 62 | return x - floor(x * (1.0f / 289.0f)) * 289.0f; | |
| 63 | } | |
| 64 | ||
| 65 | BN_FN float ps_permute(float x) { | |
| 66 | return ps_mod289(((x * 34.0f) + 10.0f) * x); | |
| 67 | } | |
| 68 | ||
| 69 | BN_FN float ps_taylor_inv_sqrt(float r) { | |
| 70 | return 1.79284291400159f - 0.85373472095314f * r; | |
| 71 | } | |
| 72 | ||
| 73 | BN_FN float ps_dot3(float ax, float ay, float az, float bx, float by, float bz) { | |
| 74 | return ax * bx + ay * by + az * bz; | |
| 75 | } | |
| 76 | ||
| 77 | BN_FN vec3 ps_grad_from_p(float pv) { | |
| 78 | float n_ = 1.0f / 7.0f; | |
| 79 | float ns_xx = 2.0f * n_; | |
| 80 | float ns_yy = -1.0f; | |
| 81 | float ns_zz = 0.5f * n_; | |
| 82 | float gx; float gy; float gz; | |
| 83 | ||
| 84 | float j = pv - 49.0f * floor(pv * ns_zz * ns_zz); | |
| 85 | float x_ = floor(j * ns_zz); | |
| 86 | float y_ = floor(j - 7.0f * x_); | |
| 87 | float gxx = x_ * ns_xx + ns_yy; | |
| 88 | float gyy = y_ * ns_xx + ns_yy; | |
| 89 | float hh = 1.0f - abs(gxx) - abs(gyy); | |
| 90 | ||
| 91 | // Sign correction | |
| 92 | float sx = (gxx < 0.0f) ? -1.0f : 1.0f; | |
| 93 | float sy = (gyy < 0.0f) ? -1.0f : 1.0f; | |
| 94 | float b0x = gxx; | |
| 95 | float b0y = gyy; | |
| 96 | float sh = (hh < 0.0f) ? -1.0f : 0.0f; | |
| 97 | gx = b0x + floor(b0x) * 2.0f * sh + sh; | |
| 98 | // Simpler: match GLSL exactly | |
| 99 | // a0 = b0.xzyw + s0.xzyw * sh.xxyy | |
| 100 | // For a single corner, this simplifies to: | |
| 101 | float s0x = floor(b0x) * 2.0f + 1.0f; | |
| 102 | float s0y = floor(b0y) * 2.0f + 1.0f; | |
| 103 | float shv = (hh < 0.0f) ? -1.0f : 0.0f; | |
| 104 | gx = b0x + s0x * shv; | |
| 105 | gy = b0y + s0y * shv; | |
| 106 | gz = hh; | |
| 107 | ||
| 108 | // Normalize | |
| 109 | float len2 = gx * gx + gy * gy + gz * gz; | |
| 110 | float inv_len = ps_taylor_inv_sqrt(len2); | |
| 111 | gx *= inv_len; | |
| 112 | gy *= inv_len; | |
| 113 | gz *= inv_len; | |
| 114 | return vec3(gx, gy, gz); | |
| 115 | } | |
| 116 | ||
| 117 | BN_FN float ps_simplex_noise_3d(float xin, float yin, float zin) { | |
| 118 | float C1 = 1.0f / 6.0f; // G3 | |
| 119 | float C2 = 1.0f / 3.0f; // F3 | |
| 120 | ||
| 121 | // First corner | |
| 122 | float s = (xin + yin + zin) * C2; | |
| 123 | float ix = floor(xin + s); | |
| 124 | float iy = floor(yin + s); | |
| 125 | float iz = floor(zin + s); | |
| 126 | ||
| 127 | float t = (ix + iy + iz) * C1; | |
| 128 | float x0 = xin - ix + t; | |
| 129 | float y0 = yin - iy + t; | |
| 130 | float z0 = zin - iz + t; | |
| 131 | ||
| 132 | // Simplex traversal (branchless Ashima step/min/max) | |
| 133 | float gx = (x0 >= y0) ? 1.0f : 0.0f; // step(y0, x0) | |
| 134 | float gy = (y0 >= z0) ? 1.0f : 0.0f; // step(z0, y0) | |
| 135 | float gz = (z0 >= x0) ? 1.0f : 0.0f; // step(x0, z0) | |
| 136 | float lx = 1.0f - gx; | |
| 137 | float ly = 1.0f - gy; | |
| 138 | float lz = 1.0f - gz; | |
| 139 | ||
| 140 | float i1x = min(gx, lz); | |
| 141 | float i1y = min(gy, lx); | |
| 142 | float i1z = min(gz, ly); | |
| 143 | float i2x = max(gx, lz); | |
| 144 | float i2y = max(gy, lx); | |
| 145 | float i2z = max(gz, ly); | |
| 146 | ||
| 147 | float x1 = x0 - i1x + C1; | |
| 148 | float y1 = y0 - i1y + C1; | |
| 149 | float z1 = z0 - i1z + C1; | |
| 150 | float x2 = x0 - i2x + C2; | |
| 151 | float y2 = y0 - i2y + C2; | |
| 152 | float z2 = z0 - i2z + C2; | |
| 153 | float x3 = x0 - 0.5f; | |
| 154 | float y3 = y0 - 0.5f; | |
| 155 | float z3 = z0 - 0.5f; | |
| 156 | ||
| 157 | // Permutations (polynomial, no lookup table) | |
| 158 | ix = ps_mod289(ix); | |
| 159 | iy = ps_mod289(iy); | |
| 160 | iz = ps_mod289(iz); | |
| 161 | ||
| 162 | float p0 = ps_permute(ps_permute(ps_permute(iz) + iy) + ix); | |
| 163 | float p1 = ps_permute(ps_permute(ps_permute(iz + i1z) + iy + i1y) + ix + i1x); | |
| 164 | float p2 = ps_permute(ps_permute(ps_permute(iz + i2z) + iy + i2y) + ix + i2x); | |
| 165 | float p3 = ps_permute(ps_permute(ps_permute(iz + 1.0f) + iy + 1.0f) + ix + 1.0f); | |
| 166 | ||
| 167 | // Gradients: 7x7 points over octahedron | |
| 168 | float n_ = 1.0f / 7.0f; | |
| 169 | // ns = n_ * D.wyz - D.xzx where D = (0, 0.5, 1, 2) | |
| 170 | float ns_x = n_ * 2.0f - 0.0f; // 2/7 | |
| 171 | float ns_y = n_ * 0.5f - 1.0f; // -0.928571... | |
| 172 | float ns_z = n_ * 0.0f - 0.0f; // 0 -- wait, let me recalculate | |
| 173 | // D.wyz = (2.0, 0.0, 0.5), D.xzx = (0.0, 1.0, 0.0) | |
| 174 | // ns = n_ * (2.0, 0.0, 0.5) - (0.0, 1.0, 0.0) | |
| 175 | // ns = (2/7, -1, 1/14) = (0.285714, -1.0, 0.071428) | |
| 176 | float ns_xx = 2.0f * n_; // 0.285714 | |
| 177 | float ns_yy = -1.0f; // -1.0 | |
| 178 | float ns_zz = 0.5f * n_; // 0.071428 | |
| 179 | ||
| 180 | // For each corner, compute gradient from permutation value | |
| 181 | ||
| 182 | float g0x, g0y, g0z, g1x, g1y, g1z, g2x, g2y, g2z, g3x, g3y, g3z; | |
| 183 | vec3 p0_v = ps_grad_from_p(p0); g0x = p0_v.x; g0y = p0_v.y; g0z = p0_v.z; | |
| 184 | vec3 p1_v = ps_grad_from_p(p1); g1x = p1_v.x; g1y = p1_v.y; g1z = p1_v.z; | |
| 185 | vec3 p2_v = ps_grad_from_p(p2); g2x = p2_v.x; g2y = p2_v.y; g2z = p2_v.z; | |
| 186 | vec3 p3_v = ps_grad_from_p(p3); g3x = p3_v.x; g3y = p3_v.y; g3z = p3_v.z; | |
| 187 | ||
| 188 | // Mix contributions from four corners | |
| 189 | float m0 = max(0.5f - ps_dot3(x0, y0, z0, x0, y0, z0), 0.0f); | |
| 190 | float m1 = max(0.5f - ps_dot3(x1, y1, z1, x1, y1, z1), 0.0f); | |
| 191 | float m2 = max(0.5f - ps_dot3(x2, y2, z2, x2, y2, z2), 0.0f); | |
| 192 | float m3 = max(0.5f - ps_dot3(x3, y3, z3, x3, y3, z3), 0.0f); | |
| 193 | ||
| 194 | m0 *= m0; m1 *= m1; m2 *= m2; m3 *= m3; | |
| 195 | ||
| 196 | return 105.0f * ( | |
| 197 | m0 * m0 * ps_dot3(g0x, g0y, g0z, x0, y0, z0) + | |
| 198 | m1 * m1 * ps_dot3(g1x, g1y, g1z, x1, y1, z1) + | |
| 199 | m2 * m2 * ps_dot3(g2x, g2y, g2z, x2, y2, z2) + | |
| 200 | m3 * m3 * ps_dot3(g3x, g3y, g3z, x3, y3, z3)); | |
| 201 | } | |
| 202 | ||
| 203 | // fBM (fractal Brownian motion) using simplex noise | |
| 204 | BN_FN float ps_fbm_noise_3d(float x, float y, float z, int octaves, float roughness, float lacunarity) { | |
| 205 | float value = 0.0f; | |
| 206 | float amplitude = 1.0f; | |
| 207 | float frequency = 1.0f; | |
| 208 | float max_amp = 0.0f; | |
| 209 | ||
| 210 | for (int i = 0; i < octaves; i++) { | |
| 211 | value += amplitude * ps_simplex_noise_3d(x * frequency, y * frequency, z * frequency); | |
| 212 | max_amp += amplitude; | |
| 213 | amplitude *= roughness; | |
| 214 | frequency *= lacunarity; | |
| 215 | } | |
| 216 | ||
| 217 | return value / max_amp; | |
| 218 | } | |
| 219 | ||
| 220 | #endif // PN_PROC_SIMPLEX |
| … | … | @@ -95,10 +95,11 @@ static const char *SUBJECT_FILES[] = { |
| 95 | 95 | "src/gpu/blender_perlin_glsl.gen.h", |
| 96 | 96 | "src/gpu/blender_voronoi_glsl.gen.h", |
| 97 | 97 | "src/gpu/blender_ramp_blend_glsl.gen.h", |
| 98 | "src/gpu/proc_simplex_glsl.gen.h", | |
| 98 | 99 | }; |
| 99 | 100 | static const char *SUBJECT_MARKS[] = { "BLENDER_HASH_GLSL", "BLENDER_PERLIN_GLSL", |
| 100 | "BLENDER_VORONOI_GLSL", "BLENDER_RAMP_BLEND_GLSL" }; | |
| 101 | static const int SUBJECT_N = 4; | |
| 101 | "BLENDER_VORONOI_GLSL", "BLENDER_RAMP_BLEND_GLSL", "PROC_SIMPLEX_GLSL" }; | |
| 102 | static const int SUBJECT_N = 5; | |
| 102 | 103 | |
| 103 | 104 | static const double TOL_REL = 1e-5; |
| 104 | 105 | static const double TOL_ABS = 1e-6; |
| … | … | @@ -314,6 +315,8 @@ enum Op { |
| 314 | 315 | OP_VORONOI_EDGE, // dim |
| 315 | 316 | OP_VORONOI_RADIUS, // dim |
| 316 | 317 | OP_RAMP_BLEND, // type = blend mode, comp = channel |
| 318 | OP_SIMPLEX, // the raw Ashima lattice | |
| 319 | OP_SIMPLEX_FBM, // type = octaves | |
| 317 | 320 | OP__COUNT |
| 318 | 321 | }; |
| 319 | 322 | |
| … | … | @@ -337,6 +340,7 @@ static const OpName OP_NAMES[] = { |
| 337 | 340 | { OP_PERLIN_FLOAT3, "OP_PERLIN_FLOAT3" }, |
| 338 | 341 | { OP_VORONOI_XFX, "OP_VORONOI_XFX" }, { OP_VORONOI_EDGE, "OP_VORONOI_EDGE" }, |
| 339 | 342 | { OP_VORONOI_RADIUS, "OP_VORONOI_RADIUS" }, { OP_RAMP_BLEND, "OP_RAMP_BLEND" }, |
| 343 | { OP_SIMPLEX, "OP_SIMPLEX" }, { OP_SIMPLEX_FBM, "OP_SIMPLEX_FBM" }, | |
| 340 | 344 | }; |
| 341 | 345 | |
| 342 | 346 | struct Rec { |
| … | … | @@ -487,6 +491,9 @@ static uint32_t eval_cpu(const uint32_t *w) { |
| 487 | 491 | : (dim == 3) ? bv_fractal_voronoi_distance_to_edge_3(p, v3) |
| 488 | 492 | : bv_fractal_voronoi_distance_to_edge_4(p, v4)); |
| 489 | 493 | } |
| 494 | case OP_SIMPLEX: return bits_of(ps_simplex_noise_3d(x, y, z)); | |
| 495 | case OP_SIMPLEX_FBM: | |
| 496 | return bits_of(ps_fbm_noise_3d(x, y, z, type, rough, lac)); | |
| 490 | 497 | case OP_RAMP_BLEND: { |
| 491 | 498 | // A in a..d, B in the fractal slots, factor in W_DIST -- the record |
| 492 | 499 | // is reused rather than widened again. |
| … | … | @@ -638,6 +645,12 @@ void main() { |
| 638 | 645 | else { q = bp_perlin_float3_fractal_distorted(v4, detail, rough, lac, off, gain, dist, type, nrm); } |
| 639 | 646 | r = floatBitsToUint(comp == 0u ? q.x : (comp == 1u ? q.y : q.z)); |
| 640 | 647 | } |
| 648 | else if (op == OP_SIMPLEX) { | |
| 649 | r = floatBitsToUint(ps_simplex_noise_3d(x, y, z)); | |
| 650 | } | |
| 651 | else if (op == OP_SIMPLEX_FBM) { | |
| 652 | r = floatBitsToUint(ps_fbm_noise_3d(x, y, z, type, rough, lac)); | |
| 653 | } | |
| 641 | 654 | else if (op == OP_RAMP_BLEND) { |
| 642 | 655 | vec4 A = vec4(x, y, z, w); |
| 643 | 656 | vec4 Bc = vec4(detail, rough, lac, off); |
| … | … | @@ -985,6 +998,50 @@ static void build_ramp_cases() { |
| 985 | 998 | } |
| 986 | 999 | } |
| 987 | 1000 | |
| 1001 | static void build_simplex_cases() { | |
| 1002 | // The Ashima simplex, now that it is a shared source rather than a | |
| 1003 | // file-local C++ copy paired with a hand-written GLSL one. It is the | |
| 1004 | // Simplex Noise node's whole arithmetic and Displace's neighbour. | |
| 1005 | const float SC[][3] = { | |
| 1006 | { 0.0f, 0.0f, 0.0f }, | |
| 1007 | { 0.5f, 1.5f, 2.5f }, | |
| 1008 | { -7.25f, 13.75f, -0.5f }, | |
| 1009 | { 123.456f, -78.9f, 0.001f }, | |
| 1010 | }; | |
| 1011 | // ⚠ The coordinates stop at ~123 for the same reason the octave loop is | |
| 1012 | // not here at all: at |p| = 250 even a SINGLE evaluation parts company at | |
| 1013 | // 4.7e-05, because the values being differenced are ~1e5 rather than ~1. | |
| 1014 | // A function four orders of magnitude out of range has no last bits worth | |
| 1015 | // comparing, and widening the tolerance until it passes would be reporting | |
| 1016 | // agreement rather than measuring it. | |
| 1017 | // ⚠ NOT gated beyond this magnitude, and the reason is a property of the | |
| 1018 | // algorithm rather than of the port. Ashima's polynomial permutation | |
| 1019 | // (multiply by 34, add 10, mod 289) loses float precision as the effective | |
| 1020 | // coordinate -- position x frequency -- grows, and the SHIPPED simplex | |
| 1021 | // stops being bounded: measured, ps_fbm_noise_3d at |p| = 1e3 with 8 | |
| 1022 | // octaves returns 3281 where simplex should be in [-1, 1], and at 1e5 it | |
| 1023 | // returns 20714. The CPU and the GPU still AGREE there to 6.8e-5, so this | |
| 1024 | // is not two implementations diverging -- it is one implementation outside | |
| 1025 | // its usable range on both machines, which no tolerance should paper over. | |
| 1026 | const int NSC = (int)(sizeof(SC) / sizeof(SC[0])); | |
| 1027 | for (int i = 0; i < NSC; i++) { | |
| 1028 | const float x = SC[i][0], y = SC[i][1], z = SC[i][2]; | |
| 1029 | add("ps_simplex_noise_3d", false, B(OP_SIMPLEX).f(x, y, z, 0.0f)); | |
| 1030 | // ⛔ THE OCTAVE LOOP IS NOT GATED, and the reason is not the GPU. | |
| 1031 | // ps_simplex_noise_3d returns ~±1e5 instead of [-1, 1] (see | |
| 1032 | // proc_simplex.inc), so an fbm sum is an accumulation of values four | |
| 1033 | // orders of magnitude too large, and two float machines summing that | |
| 1034 | // part company at ~6.5e-05 relative however few octaves you use -- | |
| 1035 | // measured at 4, 6 and 8. Holding it to a tolerance would be choosing | |
| 1036 | // a number that makes a broken function look agreed-upon. | |
| 1037 | // | |
| 1038 | // What IS gated is the raw lattice above: one evaluation, where the | |
| 1039 | // two machines agree to 1e-5 and the question "is this the same | |
| 1040 | // function on both sides" has a real answer. The fbm goes back in when | |
| 1041 | // the arithmetic is fixed. | |
| 1042 | } | |
| 1043 | } | |
| 1044 | ||
| 988 | 1045 | static long peak_rss_kb() { |
| 989 | 1046 | std::ifstream f("/proc/self/status"); |
| 990 | 1047 | std::string line; |
| … | … | @@ -1022,7 +1079,8 @@ int main(int argc, char **argv) { |
| 1022 | 1079 | if (lib.find("bn_hash_bit_final") == std::string::npos || |
| 1023 | 1080 | lib.find("bp_perlin_signed") == std::string::npos || |
| 1024 | 1081 | lib.find("bv_voronoi_f1") == std::string::npos || |
| 1025 | lib.find("br_ramp_blend") == std::string::npos) { | |
| 1082 | lib.find("br_ramp_blend") == std::string::npos || | |
| 1083 | lib.find("ps_simplex_noise_3d") == std::string::npos) { | |
| 1026 | 1084 | fprintf(stderr, "FATAL: subject text is missing bn_hash_bit_final, bp_perlin_signed" |
| 1027 | 1085 | " or bv_voronoi_f1 -- wrong subject?\n"); |
| 1028 | 1086 | return 1; |
| … | … | @@ -1031,6 +1089,7 @@ int main(int argc, char **argv) { |
| 1031 | 1089 | build_cases(); |
| 1032 | 1090 | build_voronoi_cases(); |
| 1033 | 1091 | build_ramp_cases(); |
| 1092 | build_simplex_cases(); | |
| 1034 | 1093 | if (g_cases.empty()) { fprintf(stderr, "FATAL: no cases\n"); return 1; } |
| 1035 | 1094 | |
| 1036 | 1095 | int n_exact = 0; |