Effects: Fractal Noise's Soft Linear has no lattice creases (#259)

Soft Linear was value noise with a quintic fade between two lattice
values per axis. That fade has zero slope across every lattice line and
at every lattice point, so the noise's contours met the lattice at right
angles and flattened around its points. The turbulent fold (1 - |s|) and
high contrast turned that into a visible square grid at each layer's cell
size (Turbulent Basic, Contrast 140, Brightness -35: gradients 1.3x as
strong along the axes as along the diagonals; Spline 1.1x).

Soft Linear is now linear interpolation softened by a one-cell box
filter, i.e. the uniform quadratic B-spline over the 3 x 3 lattice values
around the nearest one (evolution still fades between two slices): smooth
everywhere, with half the neighbours' difference as its slope on lattice
lines, and cheaper than Spline (9 taps instead of 16). Averaging
lowers the noise's spread, so a fixed gain about mid-grey restores the
quintic fade's variance and existing projects keep their contrast and
brightness (issue settings: mean 0.356 -> 0.362, std 0.147 -> 0.152,
axis bias 1.28 -> 1.05).

Only Fractal / Turbulent Noise's Soft Linear changes: generate's
value_noise and its other callers are untouched. noise3 now shares
generate's lattice hash and fade instead of copying them. The GPU
kernels (pointwise Fractal Noise and fxn_fractal) call the same
soft_linear_noise. No golden hashes pin these cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic
2026-10-08 21:00:42 +07:00
co-authored by Claude Opus 5.5
parent 1ab26e1119
commit fe0d11c5cc
4 changed files with 64 additions and 28 deletions
+1 -1
View File
@@ -528,7 +528,7 @@ fn fxn_slice(ix: i32, iy: i32, iz: i32, tx: f32, ty: f32, seed: u32, nt: u32) ->
// noise3::typed_noise.
fn fxn_typed_noise(x: f32, y: f32, z: f32, seed: u32, nt: u32) -> f32 {
if (nt == 2u) {
return value_noise(x, y, z, seed);
return soft_linear_noise(x, y, z, seed);
}
let x0 = floor(x);
let y0 = floor(y);
+31 -1
View File
@@ -535,6 +535,36 @@ fn value_noise(x: f32, y: f32, z: f32, seed: u32) -> f32 {
return a + (b - a) * fz;
}
// Fractal Noise's Soft Linear (effects::noise3::typed_noise): the quadratic B-spline over the
// 3 × 3 lattice values around the nearest one, faded along evolution, and SOFT_LINEAR_GAIN.
const SOFT_LINEAR_GAIN: f32 = (181.0 / 231.0) / 0.55;
fn bspline(t: f32) -> vec3<f32> {
let s = 1.0 - t;
return vec3<f32>(0.5 * s * s, 0.5 + t * s, 0.5 * t * t);
}
fn soft_linear_slice(ix: i32, iy: i32, iz: i32, wx: vec3<f32>, wy: vec3<f32>, seed: u32) -> f32 {
var sum = 0.0;
for (var j = 0; j < 3; j++) {
let y = iy + j - 1;
sum += wy[j] * (wx.x * lattice(ix - 1, y, iz, seed) + wx.y * lattice(ix, y, iz, seed) + wx.z * lattice(ix + 1, y, iz, seed));
}
return sum;
}
fn soft_linear_noise(x: f32, y: f32, z: f32, seed: u32) -> f32 {
let x0 = floor(x + 0.5);
let y0 = floor(y + 0.5);
let z0 = floor(z);
let wx = bspline(x + 0.5 - x0);
let wy = bspline(y + 0.5 - y0);
let a = soft_linear_slice(i32(x0), i32(y0), i32(z0), wx, wy, seed);
let b = soft_linear_slice(i32(x0), i32(y0), i32(z0) + 1, wx, wy, seed);
let n = a + (b - a) * fade(z - z0);
return 0.5 + (n - 0.5) * SOFT_LINEAR_GAIN;
}
// generate::put: blend a generated straight colour (alpha ga) over the pixel.
fn put(px: vec4<f32>, g: vec3<f32>, ga: f32, blend_orig: f32) -> vec4<f32> {
let g2 = vec4<f32>(g * ga, ga);
@@ -800,7 +830,7 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
if (o + 1u == n_oct && frac > 0.0) {
w = frac;
}
var n = value_noise(u * f, v * f, evo + f32(o) * 7.31, seed + o);
var n = soft_linear_noise(u * f, v * f, evo + f32(o) * 7.31, seed + o);
let s = n * 2.0 - 1.0;
if (kind == 1u) {
n = 1.0 - s * s;