// PPF-based deterministic noise synthesis shader
// Generates texture patterns using 360-prime residue hash
struct Metadata {
width: u32,
height: u32,
levels: u32,
_padding: u32,
}
struct Region {
x: u32,
y: u32,
w: u32,
h: u32,
seed: u32,
chaos_level: u32,
scale: u32,
persistence: u32,
noise_type: u32,
base_color_r: u32,
base_color_g: u32,
base_color_b: u32,
amplitude: u32,
}
@group(0) @binding(0) var<storage, read> regions: array<Region>;
@group(0) @binding(1) var<uniform> metadata: Metadata;
@group(0) @binding(2) var<storage, read_write> output: array<u32>;
// 360-prime residue classes for PPF hash
const PRIME_RESIDUES: array<u32, 96> = array<u32, 96>(
1u, 7u, 11u, 13u, 17u, 19u, 23u, 29u, 31u, 37u, 41u, 43u, 47u, 49u, 53u, 59u,
61u, 67u, 71u, 73u, 77u, 79u, 83u, 89u, 91u, 97u, 101u, 103u, 107u, 109u, 113u, 119u,
121u, 127u, 131u, 133u, 137u, 139u, 143u, 149u, 151u, 157u, 161u, 163u, 167u, 169u, 173u, 179u,
181u, 187u, 191u, 193u, 197u, 199u, 203u, 209u, 211u, 217u, 221u, 223u, 227u, 229u, 233u, 239u,
241u, 247u, 251u, 253u, 257u, 259u, 263u, 269u, 271u, 277u, 281u, 283u, 287u, 289u, 293u, 299u,
301u, 307u, 311u, 313u, 317u, 319u, 323u, 329u, 331u, 337u, 341u, 343u, 347u, 349u, 353u, 359u
);
// PPF hash function
fn ppf_hash(x: u32, y: u32, seed: u32) -> f32 {
let combined = (x ^ (seed * 2654435761u)) + (y ^ (seed * 2246822519u));
let hash_val = combined * 2654435761u; // Fibonacci hashing
let residue = hash_val % 360u;
// Find closest prime residue
var idx = 0u;
var min_dist = 360u;
for (var i = 0u; i < 96u; i++) {
let dist = abs(i32(residue) - i32(PRIME_RESIDUES[i]));
if (u32(dist) < min_dist) {
min_dist = u32(dist);
idx = i;
}
}
return f32(PRIME_RESIDUES[idx]) / 360.0;
}
// Fixed-point arithmetic (16.16)
fn fixed_from_f32(x: f32) -> i32 {
return i32(x * 65536.0);
}
fn fixed_mul(a: i32, b: i32) -> i32 {
let product = i64(a) * i64(b);
return i32(product >> 16);
}
// Perlin-style gradient noise
fn gradient_noise(x: f32, y: f32, seed: u32) -> f32 {
let xi = u32(floor(x));
let yi = u32(floor(y));
let xf = fract(x);
let yf = fract(y);
// Get gradient vectors from hash
let h00 = ppf_hash(xi, yi, seed);
let h10 = ppf_hash(xi + 1u, yi, seed);
let h01 = ppf_hash(xi, yi + 1u, seed);
let h11 = ppf_hash(xi + 1u, yi + 1u, seed);
// Convert to gradients
let g00 = vec2<f32>(cos(h00 * 6.28318), sin(h00 * 6.28318));
let g10 = vec2<f32>(cos(h10 * 6.28318), sin(h10 * 6.28318));
let g01 = vec2<f32>(cos(h01 * 6.28318), sin(h01 * 6.28318));
let g11 = vec2<f32>(cos(h11 * 6.28318), sin(h11 * 6.28318));
// Dot products
let d00 = dot(g00, vec2<f32>(xf, yf));
let d10 = dot(g10, vec2<f32>(xf - 1.0, yf));
let d01 = dot(g01, vec2<f32>(xf, yf - 1.0));
let d11 = dot(g11, vec2<f32>(xf - 1.0, yf - 1.0));
// Smoothstep interpolation
let u = xf * xf * (3.0 - 2.0 * xf);
let v = yf * yf * (3.0 - 2.0 * yf);
// Bilinear interpolation
let x1 = mix(d00, d10, u);
let x2 = mix(d01, d11, u);
return mix(x1, x2, v);
}
// Fractal Brownian Motion
fn fbm(x: f32, y: f32, region: Region) -> f32 {
var total = 0.0;
var amplitude = 1.0;
var frequency = 1.0 / f32(1u << region.scale);
let octaves = region.chaos_level;
let persistence_f = f32(region.persistence) / 255.0;
for (var i = 0u; i < octaves; i++) {
total += gradient_noise(x * frequency, y * frequency, region.seed + i) * amplitude;
amplitude *= persistence_f;
frequency *= 2.0;
}
return total;
}
@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
let x = global_id.x;
let y = global_id.y;
if (x >= metadata.width || y >= metadata.height) {
return;
}
let idx = y * metadata.width + x;
// Read current pixel
let current = output[idx];
var r = current & 0xFFu;
var g = (current >> 8u) & 0xFFu;
var b = (current >> 16u) & 0xFFu;
// Check if pixel is in any texture region
let region_count = arrayLength(®ions);
for (var i = 0u; i < region_count; i++) {
let region = regions[i];
if (x >= region.x && x < region.x + region.w &&
y >= region.y && y < region.y + region.h) {
// Local coordinates
let local_x = x - region.x;
let local_y = y - region.y;
// Generate noise
let noise = fbm(f32(local_x), f32(local_y), region);
// Apply to base color
let amp_f = f32(region.amplitude) / 255.0;
let noise_scaled = noise * amp_f;
// Blend with base color
r = u32(clamp(f32(region.base_color_r) + noise_scaled * 128.0, 0.0, 255.0));
g = u32(clamp(f32(region.base_color_g) + noise_scaled * 128.0, 0.0, 255.0));
b = u32(clamp(f32(region.base_color_b) + noise_scaled * 128.0, 0.0, 255.0));
break; // Only apply first matching region
}
}
// Pack result
output[idx] = r | (g << 8u) | (b << 16u) | (255u << 24u);
}