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 height_cache: texture_2d_array<f32>;
@group(0) @binding(3) var ambient_cache: texture_storage_2d_array<r32float, write>;
@compute @workgroup_size(8, 8)
fn produce_ambient(
@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 = u32(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 center = terrain_cache_load(height_cache, layer, texel);
var occlusion = 0.0;
var direction_table = array<vec2<i32>, 8>(
vec2<i32>(1, 0),
vec2<i32>(-1, 0),
vec2<i32>(0, 1),
vec2<i32>(0, -1),
vec2<i32>(1, 1),
vec2<i32>(-1, 1),
vec2<i32>(1, -1),
vec2<i32>(-1, -1),
);
for (var direction_index = 0; direction_index < 8; direction_index++) {
let direction = direction_table[direction_index];
var horizon = 0.0;
for (var tap = 0u; tap < 4u; tap++) {
let distance_texels = i32(2u << tap);
let sample = terrain_cache_load(
height_cache,
layer,
texel + direction * distance_texels,
);
let slope = (sample - center) / (f32(distance_texels) * texel_size * 1.4142);
horizon = max(horizon, slope);
}
occlusion += clamp(horizon, 0.0, 2.0) * 0.5;
}
let ambient = 1.0 - 0.5 * (occlusion / 8.0);
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(ambient_cache, wrapped, i32(layer), vec4<f32>(ambient, 0.0, 0.0, 0.0));
}