struct TerrainProduceUniforms {
continental_spline: array<vec4<f32>, 4>,
erosion_spline: array<vec4<f32>, 4>,
frequencies: vec4<f32>,
warp_detail: vec4<f32>,
erosion_params: vec4<f32>,
seeds: vec4<u32>,
};
struct TerrainJob {
data: vec4<i32>,
};
@group(0) @binding(0) var<uniform> uniforms: TerrainProduceUniforms;
@group(0) @binding(1) var<storage, read> jobs: array<TerrainJob>;
@group(0) @binding(2) var cache: texture_storage_2d_array<r32float, write>;
fn terrain_height_world(world_xz: vec2<f32>) -> f32 {
let seed = uniforms.seeds.x;
let continental = clamp(
terrain_fbm(world_xz * uniforms.frequencies.x, 5u, seed + 1u) * 2.2,
-1.0,
1.0,
);
let erosion = clamp(
terrain_fbm(world_xz * uniforms.frequencies.y + vec2<f32>(311.0, 97.0), 4u, seed + 2u)
* 2.2,
-1.0,
1.0,
);
let warp = vec2<f32>(
terrain_fbm(world_xz * uniforms.frequencies.w, 3u, seed + 3u),
terrain_fbm(world_xz * uniforms.frequencies.w + vec2<f32>(57.0, 113.0), 3u, seed + 4u),
) * uniforms.warp_detail.x;
let mountain_noise = terrain_fbm_eroded(
(world_xz + warp) * uniforms.frequencies.z,
7u,
seed + 5u,
uniforms.erosion_params.x,
);
let ridge = clamp(mountain_noise + 0.5, 0.0, 1.0);
let base = terrain_spline(uniforms.continental_spline, continental);
let mountain_amplitude = terrain_spline(uniforms.erosion_spline, erosion);
let mountain_scale = max(base, 0.0) + 30.0;
let detail = terrain_fbm(world_xz * 0.06, 3u, seed + 6u) * uniforms.warp_detail.y;
let height = base + ridge * mountain_amplitude * mountain_scale + detail;
let flatten_radius = uniforms.warp_detail.w;
if flatten_radius > 0.0 {
let falloff = smoothstep(flatten_radius, flatten_radius * 2.5, length(world_xz));
return mix(-0.05, height, falloff);
}
return height;
}
@compute @workgroup_size(8, 8)
fn produce_tiles(
@builtin(workgroup_id) workgroup: vec3<u32>,
@builtin(local_invocation_id) local: vec3<u32>,
) {
let job = jobs[workgroup.z].data;
let scale_level = u32(job.z);
let layer = i32(job.w);
let texel = vec2<i32>(job.xy) + vec2<i32>(workgroup.xy * 8u + local.xy);
let texel_size = uniforms.warp_detail.z * f32(1u << scale_level);
let world_xz = (vec2<f32>(texel) + vec2<f32>(0.5, 0.5)) * texel_size;
let height = terrain_height_world(world_xz);
let wrapped = vec2<i32>(
((texel.x % TERRAIN_CACHE_SIZE) + TERRAIN_CACHE_SIZE) % TERRAIN_CACHE_SIZE,
((texel.y % TERRAIN_CACHE_SIZE) + TERRAIN_CACHE_SIZE) % TERRAIN_CACHE_SIZE,
);
textureStore(cache, wrapped, layer, vec4<f32>(height, 0.0, 0.0, 0.0));
}