Make erosion cell hashing cross-device deterministic

This commit is contained in:
Meta Vens 2026-07-12 19:57:24 +01:00
commit 33c017b284
3 changed files with 22 additions and 8 deletions

View file

@ -15,7 +15,10 @@ Rune Skovbo Johansen's **Advanced Terrain Erosion Filter**:
The CultMath files retain the MPL notice and separate the faithful planar
filter from CultMath's spherical adaptation. Modifications include C#/HLSL
type lowering, explicit bounded octave counts, finite zero-rounding behavior,
and CultMath naming.
and CultMath naming. CultMath replaces the reference floating hash with a
shared integer avalanche hash so C# and GPU evaluation select identical cell
pivots across devices; this changes the stochastic pattern, not the filter
mechanics.
The technique builds on earlier work credited by the upstream author to Fewes
and Clay John. The hash form follows the public shader math of Inigo Quilez.

View file

@ -19,11 +19,16 @@ struct CultMathAdvancedErosionResult
float3 delta; float magnitude; float ridge_map; float fade_target;
};
float cultmath_erosion_hash_unit(int2 cell, uint salt)
{
uint value = (uint)cell.x * 0x8da6b343u ^ (uint)cell.y * 0xd8163841u ^ salt;
value ^= value >> 16; value *= 0x7feb352du; value ^= value >> 15; value *= 0x846ca68bu; value ^= value >> 16;
return (value & 0x00ffffffu) * (1.0 / 16777216.0);
}
float2 cultmath_erosion_hash2(float2 input)
{
float2 k = float2(0.3183099, 0.3678794);
float2 x = input * k + k.yx;
return -1.0 + 2.0 * frac(16.0 * k * frac(x.x * x.y * (x.x + x.y)));
int2 cell = (int2)input;
return float2(cultmath_erosion_hash_unit(cell, 0x68bc21ebu), cultmath_erosion_hash_unit(cell, 0x02e5be93u)) * 2.0 - 1.0;
}
float4 cultmath_phacelle_noise(float2 position, float2 normal_direction, float frequency, float offset_cycles, float normalization)

View file

@ -109,10 +109,16 @@ public static class AdvancedErosionFilter
private static float2 Hash2(float2 input)
{
var k = new float2(0.3183099f, 0.3678794f);
var x = input * k + new float2(k.y, k.x);
var scalar = math.frac(x.x * x.y * (x.x + x.y));
return -float2.one + 2.0f * math.frac(16.0f * k * scalar);
var x = (int)input.x;
var y = (int)input.y;
return new float2(HashUnit(x, y, 0x68bc21ebu), HashUnit(x, y, 0x02e5be93u)) * 2.0f - float2.one;
}
private static float HashUnit(int x, int y, uint salt)
{
var value = unchecked((uint)x * 0x8da6b343u ^ (uint)y * 0xd8163841u ^ salt);
value ^= value >> 16; value *= 0x7feb352du; value ^= value >> 15; value *= 0x846ca68bu; value ^= value >> 16;
return (value & 0x00ffffffu) * (1.0f / 16777216.0f);
}
private static float2 SafeNormalize(float2 value)