118 lines
3.4 KiB
Plaintext
118 lines
3.4 KiB
Plaintext
import package::shared::{Spotlight, MAX_SPOTLIGHTS, calculate_spotlight_data};
|
|
|
|
struct AccumulationUniforms {
|
|
terrain_min_xz: vec2<f32>,
|
|
terrain_max_xz: vec2<f32>,
|
|
decay_rate: f32,
|
|
delta_time: f32,
|
|
spotlight_count: u32,
|
|
_padding: u32,
|
|
light_view_projection: mat4x4<f32>,
|
|
shadow_bias: f32,
|
|
terrain_height_scale: f32,
|
|
_padding3: f32,
|
|
_padding4: f32,
|
|
spotlights: array<Spotlight, 4>,
|
|
}
|
|
|
|
@group(0) @binding(0)
|
|
var previous_light: texture_2d<f32>;
|
|
|
|
@group(0) @binding(1)
|
|
var light_sampler: sampler;
|
|
|
|
@group(0) @binding(2)
|
|
var<uniform> uniforms: AccumulationUniforms;
|
|
|
|
@group(0) @binding(3)
|
|
var heightmap: texture_2d<f32>;
|
|
|
|
@group(0) @binding(4)
|
|
var heightmap_sampler: sampler;
|
|
|
|
@group(0) @binding(5)
|
|
var shadow_map: texture_depth_2d;
|
|
|
|
@group(0) @binding(6)
|
|
var shadow_sampler: sampler_comparison;
|
|
|
|
@group(0) @binding(7)
|
|
var snow_depth: texture_2d<f32>;
|
|
|
|
@group(0) @binding(8)
|
|
var snow_depth_sampler: sampler;
|
|
|
|
struct VertexOutput {
|
|
@builtin(position) position: vec4<f32>,
|
|
@location(0) uv: vec2<f32>,
|
|
}
|
|
|
|
@vertex
|
|
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
|
|
var out: VertexOutput;
|
|
|
|
let x = f32((vertex_index << 1u) & 2u);
|
|
let y = f32(vertex_index & 2u);
|
|
out.position = vec4<f32>(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0);
|
|
out.uv = vec2<f32>(x, 1.0 - y);
|
|
|
|
return out;
|
|
}
|
|
|
|
fn sample_shadow_map(light_space_pos: vec4<f32>) -> f32 {
|
|
let proj_coords = light_space_pos.xyz / light_space_pos.w;
|
|
let ndc_coords = proj_coords * vec3<f32>(0.5, -0.5, 1.0) + vec3<f32>(0.5, 0.5, 0.0);
|
|
|
|
if ndc_coords.x < 0.0 || ndc_coords.x > 1.0 ||
|
|
ndc_coords.y < 0.0 || ndc_coords.y > 1.0 ||
|
|
ndc_coords.z < 0.0 || ndc_coords.z > 1.0 {
|
|
return 1.0;
|
|
}
|
|
|
|
let depth = ndc_coords.z - uniforms.shadow_bias;
|
|
let shadow = textureSampleCompare(shadow_map, shadow_sampler, ndc_coords.xy, depth);
|
|
|
|
return shadow;
|
|
}
|
|
|
|
@fragment
|
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
let prev_light = textureSample(previous_light, light_sampler, in.uv).r;
|
|
|
|
let world_xz = mix(uniforms.terrain_min_xz, uniforms.terrain_max_xz, in.uv);
|
|
let terrain_height = textureSampleLevel(heightmap, heightmap_sampler, in.uv, 0.0).r * uniforms.terrain_height_scale;
|
|
let depth = textureSampleLevel(snow_depth, snow_depth_sampler, in.uv, 0.0).r;
|
|
let snow_surface_height = terrain_height + depth;
|
|
let snow_surface_pos = vec3<f32>(world_xz.x, snow_surface_height, world_xz.y);
|
|
|
|
let light_space_position = uniforms.light_view_projection * vec4<f32>(snow_surface_pos, 1.0);
|
|
let shadow = sample_shadow_map(light_space_position);
|
|
|
|
var current_light = 0.0;
|
|
if shadow > 0.0 {
|
|
let tile_scale = 2.0;
|
|
let surface_normal = vec3<f32>(0.0, 1.0, 0.0);
|
|
|
|
for (var i = 0u; i < uniforms.spotlight_count; i++) {
|
|
let spotlight = uniforms.spotlights[i];
|
|
let data = calculate_spotlight_data(snow_surface_pos, surface_normal, spotlight, tile_scale, shadow);
|
|
let light = f32(data.is_lit);
|
|
current_light = max(current_light, light);
|
|
}
|
|
}
|
|
|
|
var accumulated: f32;
|
|
if current_light > 0.01 {
|
|
accumulated = current_light;
|
|
} else {
|
|
let decay_factor = exp(-uniforms.decay_rate * uniforms.delta_time * 60.0);
|
|
accumulated = prev_light * decay_factor;
|
|
|
|
if accumulated < 0.01 {
|
|
accumulated = 0.0;
|
|
}
|
|
}
|
|
|
|
return vec4<f32>(accumulated, 0.0, 0.0, 1.0);
|
|
}
|