CultMath: snoise(float3) takes the upstream simplex-boundary fix
Kernel radius^2 0.6 -> 0.5, scale 42 -> 105, permute (34x+1)x -> (34x+10)x, per stegu/webgl-noise 22434e04d7 (fix 21d9fe23d7, scale ff3b5d34ea, permute a3e6d57095). snoise_grad, fbm_grad, ridged_grad follow. C#, both HLSL copies, design.md and THIRD-PARTY-NOTICES.md change together. The 2D snoise shares the permute helper, so its values change too. Golden values are regenerated in the next commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
4ca1cf388a
commit
8703b44636
6 changed files with 76 additions and 64 deletions
|
|
@ -6,11 +6,15 @@
|
|||
simplex-noise functions by Ian McEwan and Ashima Arts, as distributed by Stefan
|
||||
Gustavson's `webgl-noise` project and Unity Mathematics.
|
||||
|
||||
`math.snoise(float3)` and `cultmath_snoise(float3)` follow `src/noise3D.glsl` and
|
||||
`math.snoise(float2)` follows `src/noise2D.glsl` of stegu/webgl-noise at commit
|
||||
`22434e04d7753f7e949e8d724ab3da2864c17a0f` (the simplex-boundary fix is
|
||||
`21d9fe23d7`, scale `ff3b5d34ea`, permutation `a3e6d57095`).
|
||||
|
||||
`math.snoise_grad`/`cultmath_snoise_grad` (and the `fbm_grad`/`ridged_grad`
|
||||
octave sums built on them) follow the analytic-gradient differentiation from
|
||||
the same project's `src/noise3Dgrad.glsl`, retargeted onto this file's own
|
||||
`snoise(float3)` falloff radius, permutation, and scale constants (see the
|
||||
`math.snoise_grad` doc comment).
|
||||
the same project's `src/noise3Dgrad.glsl`, with the same kernel radius,
|
||||
permutation and scale as `snoise(float3)`.
|
||||
|
||||
- Copyright 2011 Ashima Arts.
|
||||
- Upstream: <https://github.com/ashima/webgl-noise>
|
||||
|
|
|
|||
|
|
@ -233,15 +233,25 @@ together, laid out as `float4(gradient.xyz, value.w)`, with no value-only twin
|
|||
paths are proven bit-identical (`PrunedSearchIsBitIdenticalToTheUnprunedFiveCubedSearch`,
|
||||
which samples `[-50, 50]^3`, is comfortably inside that domain).
|
||||
|
||||
- `snoise(float3)` follows webgl-noise `src/noise3D.glsl` as of
|
||||
stegu/webgl-noise `22434e04d7`: kernel radius squared `0.5`, output scale
|
||||
`105`, permutation `mod289((34x+10)x)`. Upstream's `21d9fe23d7` (2020-10-14,
|
||||
"an age-old bug that caused slight discontinuities along simplex
|
||||
boundaries") moved the radius from `0.6` to `0.5`; at `0.6` a lattice vertex
|
||||
outside the four summed corners can lie inside the kernel, leaving a value
|
||||
jump at the cell boundary (3.1e-3 over the committed probe's 400 lines). `ff3b5d34ea` set the scale
|
||||
to `105` and `a3e6d57095` (2021-06-30) the permutation; the 2D `snoise` shares
|
||||
that permutation helper, so its values changed too, while its own kernel
|
||||
(`0.5`) and scale (`130`) already matched upstream.
|
||||
`NoiseGradTests.SnoiseAndItsGradientAreContinuousAcrossSimplexCellBoundaries`
|
||||
pins the continuity.
|
||||
- `snoise_grad(float3 p)` carries `snoise(float3)` to value-and-gradient form,
|
||||
following the differentiation in webgl-noise `src/noise3Dgrad.glsl` (Ashima
|
||||
Arts / Ian McEwan, MIT) but retargeted onto `snoise(float3)`'s own falloff
|
||||
radius (`0.6`), permutation (`mod289((34x+1)x)`), and scale (`42`) rather
|
||||
than upstream's `0.5`/`mod289((34x+10)x)`/`105`: the differentiation itself
|
||||
does not depend on those constants (per corner, `m0 = max(radius -
|
||||
dot(x,x), 0)`, so `d(m0^4 * dot(p,x))/dx = -8*m0^3*dot(p,x)*x + m0^4*p`,
|
||||
summed over the four corners and scaled by the same constant as the value),
|
||||
so `.w` stays exactly `snoise(float3)`'s own field. `fbm_grad` and
|
||||
Arts / Ian McEwan, MIT), which uses the same radius, permutation and scale as
|
||||
`snoise(float3)`: per corner, `m0 = max(0.5 - dot(x,x), 0)`, so
|
||||
`d(m0^4 * dot(p,x))/dx = -8*m0^3*dot(p,x)*x + m0^4*p`, summed over the four
|
||||
corners and scaled by the same constant as the value, so `.w` is exactly
|
||||
`snoise(float3)`. `fbm_grad` and
|
||||
`ridged_grad` are octave sums of `snoise_grad`: each octave samples at
|
||||
`p * frequency`, so by the chain rule its gradient scales by that same
|
||||
frequency, and both amplitude (`gain`) and frequency (`lacunarity`) compound
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ float cultmath_smootherstep(float value)
|
|||
float2 cultmath_snoise_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; }
|
||||
float3 cultmath_snoise_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; }
|
||||
float4 cultmath_snoise_mod289(float4 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; }
|
||||
float3 cultmath_snoise_permute(float3 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); }
|
||||
float4 cultmath_snoise_permute(float4 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); }
|
||||
float3 cultmath_snoise_permute(float3 value) { return cultmath_snoise_mod289(((value * 34.0) + 10.0) * value); }
|
||||
float4 cultmath_snoise_permute(float4 value) { return cultmath_snoise_mod289(((value * 34.0) + 10.0) * value); }
|
||||
|
||||
// Ashima Arts / Ian McEwan 3D simplex noise (MIT), same float32 evaluation
|
||||
// order as the C# math.snoise(float3) mirror.
|
||||
|
|
@ -125,9 +125,9 @@ float cultmath_snoise(float3 value)
|
|||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m = max(0.5 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
m = m * m;
|
||||
return 42.0 * dot(m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
return 105.0 * dot(m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
}
|
||||
|
||||
float cultmath_snoise(float2 value)
|
||||
|
|
@ -162,12 +162,11 @@ float cultmath_snoise(float2 value)
|
|||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
// Ashima Arts / Ian McEwan 3D simplex noise with analytic gradient (invariant 8), following the
|
||||
// differentiation in webgl-noise src/noise3Dgrad.glsl (Ashima Arts / McEwan, MIT; confirmed at
|
||||
// stegu/webgl-noise today), retargeted onto cultmath_snoise(float3)'s own constants (0.6 falloff
|
||||
// radius, permute(x) = mod289((34x+1)x), scale 42; see the C# math.snoise_grad comment for the full
|
||||
// derivation and why the differentiation itself is unaffected by that retargeting). Every local up
|
||||
// through p0..p3 and x0..x3 is cultmath_snoise(float3)'s own derivation verbatim.
|
||||
// Analytic gradient of cultmath_snoise(float3) (invariant 8), following webgl-noise
|
||||
// src/noise3Dgrad.glsl (Ashima Arts / McEwan, MIT); same kernel, permutation and scale as
|
||||
// cultmath_snoise(float3) (stegu/webgl-noise 22434e04d7). See the C# math.snoise_grad comment for the
|
||||
// derivation. Every local up through p0..p3 and x0..x3 is cultmath_snoise(float3)'s own derivation
|
||||
// verbatim.
|
||||
float4 cultmath_snoise_grad(float3 value)
|
||||
{
|
||||
float2 c = float2(1.0 / 6.0, 1.0 / 3.0);
|
||||
|
|
@ -212,18 +211,18 @@ float4 cultmath_snoise_grad(float3 value)
|
|||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
float4 m0 = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m0 = max(0.5 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m2 = m0 * m0;
|
||||
float4 m3 = m2 * m0;
|
||||
float4 m4 = m2 * m2;
|
||||
|
||||
float4 px = float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3));
|
||||
float value2 = 42.0 * dot(m4, px);
|
||||
float value2 = 105.0 * dot(m4, px);
|
||||
float3 grad = -8.0 * m3.x * x0 * px.x + m4.x * p0
|
||||
+ -8.0 * m3.y * x1 * px.y + m4.y * p1
|
||||
+ -8.0 * m3.z * x2 * px.z + m4.z * p2
|
||||
+ -8.0 * m3.w * x3 * px.w + m4.w * p3;
|
||||
grad *= 42.0;
|
||||
grad *= 105.0;
|
||||
|
||||
return float4(grad, value2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -595,7 +595,10 @@ public static partial class math
|
|||
}
|
||||
|
||||
// Ashima Arts / Ian McEwan 3D simplex noise (MIT), mirrored by cultmath_snoise(float3)
|
||||
// in shaders/CultMath.hlsl with the same float32 evaluation order.
|
||||
// in shaders/CultMath.hlsl with the same float32 evaluation order. Follows stegu/webgl-noise
|
||||
// src/noise3D.glsl at 22434e04d7 (kernel radius^2 0.5, scale 105, permute (34x+10)x); see
|
||||
// THIRD-PARTY-NOTICES.md. Continuous across simplex-cell boundaries
|
||||
// (NoiseGradTests.SnoiseAndItsGradientAreContinuousAcrossSimplexCellBoundaries).
|
||||
public static float snoise(float3 value)
|
||||
{
|
||||
const float cx = 1.0f / 6.0f;
|
||||
|
|
@ -642,9 +645,9 @@ public static partial class math
|
|||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
var m = max(0.6f - new float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0f);
|
||||
var m = max(0.5f - new float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0f);
|
||||
m = m * m;
|
||||
return 42.0f * dot(m * m, new float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
return 105.0f * dot(m * m, new float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
}
|
||||
|
||||
// Ashima Arts / Ian McEwan 2D simplex noise, kept component-explicit so
|
||||
|
|
@ -690,25 +693,18 @@ public static partial class math
|
|||
private static float2 snoise_mod289(float2 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f;
|
||||
private static float3 snoise_mod289(float3 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f;
|
||||
private static float4 snoise_mod289(float4 value) => value - floor(value * (1.0f / 289.0f)) * 289.0f;
|
||||
private static float3 snoise_permute(float3 value) => snoise_mod289(((value * 34.0f) + 1.0f) * value);
|
||||
private static float4 snoise_permute(float4 value) => snoise_mod289(((value * 34.0f) + 1.0f) * value);
|
||||
private static float3 snoise_permute(float3 value) => snoise_mod289(((value * 34.0f) + 10.0f) * value);
|
||||
private static float4 snoise_permute(float4 value) => snoise_mod289(((value * 34.0f) + 10.0f) * value);
|
||||
|
||||
// Ashima Arts / Ian McEwan 3D simplex noise with analytic gradient (invariant 8), following the
|
||||
// differentiation in webgl-noise src/noise3Dgrad.glsl (Ashima Arts / McEwan, MIT; confirmed at
|
||||
// stegu/webgl-noise today), retargeted onto THIS file's own snoise(float3) constants: upstream's
|
||||
// noise3Dgrad.glsl uses a 0.5 falloff radius, permute(x) = mod289((34x+10)x) and an overall scale
|
||||
// of 105, while this repo's existing snoise(float3) (and cultmath_snoise) use 0.6, permute(x) =
|
||||
// mod289((34x+1)x) and scale 42 (Ashima's alternate, second-order-artifact-reduced noise3D.glsl
|
||||
// constants, already load-bearing here and pinned by the existing snoise tests): every local up
|
||||
// through p0..p3 and x0..x3 below is snoise(float3)'s own derivation verbatim, unchanged, so .w
|
||||
// stays that same field (NoiseGradTests.SnoiseGradValueIsBitEqualToSnoise pins the two bit-equal
|
||||
// over a random sweep, stronger than the spec's 1e-6 floor). The differentiation itself carries
|
||||
// over unchanged from upstream, because it never
|
||||
// depends on the threshold or permutation constants: per corner, m0 = max(radius - dot(x,x), 0),
|
||||
// so dm0/dx = -2x, and d(m0^4 * dot(p,x))/dx = 4*m0^3*(-2x)*dot(p,x) + m0^4*p
|
||||
// = -8*m0^3*dot(p,x)*x + m0^4*p (p is that corner's own gradient constant, independent of x, so
|
||||
// only the dot(p,x) factor contributes there); summed over the four corners and scaled by the
|
||||
// same 42 as the value.
|
||||
// Analytic gradient of snoise(float3) (invariant 8), following the differentiation in
|
||||
// webgl-noise src/noise3Dgrad.glsl (Ashima Arts / McEwan, MIT), which uses the same kernel
|
||||
// radius^2 0.5, permutation (34x+10)x and scale 105 as snoise(float3) (stegu/webgl-noise
|
||||
// 22434e04d7). Every local up through p0..p3 and x0..x3 below is snoise(float3)'s own derivation
|
||||
// verbatim, so .w is that same field (NoiseGradTests.SnoiseGradValueIsBitEqualToSnoise pins the
|
||||
// two bit-equal over a random sweep). Per corner, m0 = max(0.5 - dot(x,x), 0), so dm0/dx = -2x,
|
||||
// and d(m0^4 * dot(p,x))/dx = 4*m0^3*(-2x)*dot(p,x) + m0^4*p = -8*m0^3*dot(p,x)*x + m0^4*p (p is
|
||||
// that corner's own gradient constant, independent of x); summed over the four corners and
|
||||
// scaled by the same 105 as the value.
|
||||
public static float4 snoise_grad(float3 value)
|
||||
{
|
||||
const float cx = 1.0f / 6.0f;
|
||||
|
|
@ -755,18 +751,18 @@ public static partial class math
|
|||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
var m0 = max(0.6f - new float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0f);
|
||||
var m0 = max(0.5f - new float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0f);
|
||||
var m2 = m0 * m0;
|
||||
var m3 = m2 * m0;
|
||||
var m4 = m2 * m2;
|
||||
|
||||
var px = new float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3));
|
||||
var value2 = 42.0f * dot(m4, px);
|
||||
var value2 = 105.0f * dot(m4, px);
|
||||
var grad = -8.0f * m3.x * x0 * px.x + m4.x * p0
|
||||
+ -8.0f * m3.y * x1 * px.y + m4.y * p1
|
||||
+ -8.0f * m3.z * x2 * px.z + m4.z * p2
|
||||
+ -8.0f * m3.w * x3 * px.w + m4.w * p3;
|
||||
grad *= 42.0f;
|
||||
grad *= 105.0f;
|
||||
|
||||
return new float4(grad, value2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ float cultmath_smootherstep(float value)
|
|||
float2 cultmath_snoise_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; }
|
||||
float3 cultmath_snoise_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; }
|
||||
float4 cultmath_snoise_mod289(float4 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; }
|
||||
float3 cultmath_snoise_permute(float3 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); }
|
||||
float4 cultmath_snoise_permute(float4 value) { return cultmath_snoise_mod289(((value * 34.0) + 1.0) * value); }
|
||||
float3 cultmath_snoise_permute(float3 value) { return cultmath_snoise_mod289(((value * 34.0) + 10.0) * value); }
|
||||
float4 cultmath_snoise_permute(float4 value) { return cultmath_snoise_mod289(((value * 34.0) + 10.0) * value); }
|
||||
|
||||
// Ashima Arts / Ian McEwan 3D simplex noise (MIT), same float32 evaluation
|
||||
// order as the C# math.snoise(float3) mirror.
|
||||
|
|
@ -125,9 +125,9 @@ float cultmath_snoise(float3 value)
|
|||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m = max(0.5 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
m = m * m;
|
||||
return 42.0 * dot(m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
return 105.0 * dot(m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
}
|
||||
|
||||
float cultmath_snoise(float2 value)
|
||||
|
|
@ -162,12 +162,11 @@ float cultmath_snoise(float2 value)
|
|||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
// Ashima Arts / Ian McEwan 3D simplex noise with analytic gradient (invariant 8), following the
|
||||
// differentiation in webgl-noise src/noise3Dgrad.glsl (Ashima Arts / McEwan, MIT; confirmed at
|
||||
// stegu/webgl-noise today), retargeted onto cultmath_snoise(float3)'s own constants (0.6 falloff
|
||||
// radius, permute(x) = mod289((34x+1)x), scale 42; see the C# math.snoise_grad comment for the full
|
||||
// derivation and why the differentiation itself is unaffected by that retargeting). Every local up
|
||||
// through p0..p3 and x0..x3 is cultmath_snoise(float3)'s own derivation verbatim.
|
||||
// Analytic gradient of cultmath_snoise(float3) (invariant 8), following webgl-noise
|
||||
// src/noise3Dgrad.glsl (Ashima Arts / McEwan, MIT); same kernel, permutation and scale as
|
||||
// cultmath_snoise(float3) (stegu/webgl-noise 22434e04d7). See the C# math.snoise_grad comment for the
|
||||
// derivation. Every local up through p0..p3 and x0..x3 is cultmath_snoise(float3)'s own derivation
|
||||
// verbatim.
|
||||
float4 cultmath_snoise_grad(float3 value)
|
||||
{
|
||||
float2 c = float2(1.0 / 6.0, 1.0 / 3.0);
|
||||
|
|
@ -212,18 +211,18 @@ float4 cultmath_snoise_grad(float3 value)
|
|||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
float4 m0 = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m0 = max(0.5 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m2 = m0 * m0;
|
||||
float4 m3 = m2 * m0;
|
||||
float4 m4 = m2 * m2;
|
||||
|
||||
float4 px = float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3));
|
||||
float value2 = 42.0 * dot(m4, px);
|
||||
float value2 = 105.0 * dot(m4, px);
|
||||
float3 grad = -8.0 * m3.x * x0 * px.x + m4.x * p0
|
||||
+ -8.0 * m3.y * x1 * px.y + m4.y * p1
|
||||
+ -8.0 * m3.z * x2 * px.z + m4.z * p2
|
||||
+ -8.0 * m3.w * x3 * px.w + m4.w * p3;
|
||||
grad *= 42.0;
|
||||
grad *= 105.0;
|
||||
|
||||
return float4(grad, value2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,15 @@
|
|||
simplex-noise functions by Ian McEwan and Ashima Arts, as distributed by Stefan
|
||||
Gustavson's `webgl-noise` project and Unity Mathematics.
|
||||
|
||||
`math.snoise(float3)` and `cultmath_snoise(float3)` follow `src/noise3D.glsl` and
|
||||
`math.snoise(float2)` follows `src/noise2D.glsl` of stegu/webgl-noise at commit
|
||||
`22434e04d7753f7e949e8d724ab3da2864c17a0f` (the simplex-boundary fix is
|
||||
`21d9fe23d7`, scale `ff3b5d34ea`, permutation `a3e6d57095`).
|
||||
|
||||
`math.snoise_grad`/`cultmath_snoise_grad` (and the `fbm_grad`/`ridged_grad`
|
||||
octave sums built on them) follow the analytic-gradient differentiation from
|
||||
the same project's `src/noise3Dgrad.glsl`, retargeted onto this file's own
|
||||
`snoise(float3)` falloff radius, permutation, and scale constants (see the
|
||||
`math.snoise_grad` doc comment).
|
||||
the same project's `src/noise3Dgrad.glsl`, with the same kernel radius,
|
||||
permutation and scale as `snoise(float3)`.
|
||||
|
||||
- Copyright 2011 Ashima Arts.
|
||||
- Upstream: <https://github.com/ashima/webgl-noise>
|
||||
|
|
|
|||
Loading…
Reference in a new issue