struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
}
@vertex
fn fullscreen_vs(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
var output: VertexOutput;
let x = f32(i32(vertex_index & 1u) * 2 - 1);
let y = f32(i32(vertex_index >> 1u) * 2 - 1);
output.uv = vec2<f32>(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
output.position = vec4<f32>(x, y, 0.0, 1.0);
return output;
}
@group(0) @binding(0) var input_texture: texture_2d<f32>;
@group(0) @binding(1) var input_sampler: sampler;
@group(1) @binding(0) var<uniform> u: array<vec4<f32>, 64>;
fn get_float(index: u32) -> f32 {
return u[index / 4u][index % 4u];
}
fn get_vec4(index: u32) -> vec4<f32> {
return u[index / 4u];
}
fn region_extent() -> vec4<f32> {
let region = get_vec4(236u);
let dims = vec2<f32>(textureDimensions(input_texture));
if region.z > 0.5 && region.w > 0.5 {
return region;
}
return vec4<f32>(0.0, 0.0, dims.x, dims.y);
}
fn logical_extent() -> vec2<f32> {
let logical = get_vec4(252u).xy;
if logical.x > 0.5 && logical.y > 0.5 {
return logical;
}
return region_extent().zw;
}
struct RegionMap {
offset: vec2<f32>,
scale: vec2<f32>,
}
fn region_map() -> RegionMap {
let region = region_extent();
let dims = max(vec2<f32>(textureDimensions(input_texture)), vec2<f32>(1.0));
return RegionMap(region.xy / dims, region.zw / dims);
}
fn map_uv(map: RegionMap, local_uv: vec2<f32>) -> vec2<f32> {
return map.offset + clamp(local_uv, vec2<f32>(0.0), vec2<f32>(1.0)) * map.scale;
}
fn substrate_region(slot: u32) -> vec4<f32> {
return get_vec4(slot);
}
fn has_substrate(slot: u32) -> bool {
let region = substrate_region(slot);
return region.z > 0.5 && region.w > 0.5;
}
fn has_substrates() -> bool {
return has_substrate(232u) && has_substrate(228u) && has_substrate(224u);
}
fn substrate_tap(slot: u32, local_uv: vec2<f32>) -> vec4<f32> {
return textureSampleLevel(input_texture, input_sampler, substrate_uv(slot, local_uv), 0.0);
}
fn substrate_uv(slot: u32, local_uv: vec2<f32>) -> vec2<f32> {
let region = substrate_region(slot);
let dims = max(vec2<f32>(textureDimensions(input_texture)), vec2<f32>(1.0));
let half_texel = 0.5 / max(region.zw, vec2<f32>(1.0));
let held = clamp(local_uv, half_texel, vec2<f32>(1.0) - half_texel);
return (region.xy + held * region.zw) / dims;
}
fn sd_round_rect(p: vec2<f32>, half_size: vec2<f32>, radius: f32) -> f32 {
let q = abs(p) - half_size + vec2<f32>(radius);
return length(max(q, vec2<f32>(0.0))) + min(max(q.x, q.y), 0.0) - radius;
}
fn composite_coverage(local_uv: vec2<f32>) -> f32 {
let mask_rect = get_vec4(240u);
if mask_rect.z <= 0.5 || mask_rect.w <= 0.5 {
return 1.0;
}
let radii = get_vec4(244u);
let p = local_uv * logical_extent();
let half_size = mask_rect.zw * 0.5;
let center = mask_rect.xy + half_size;
let local = p - center;
let radius = select(
select(radii.x, radii.y, local.x > 0.0),
select(radii.z, radii.w, local.x > 0.0),
local.y > 0.0,
);
let d = sd_round_rect(local, half_size, radius);
return 1.0 - smoothstep(-0.5, 0.5, d);
}
const POISSON_OFFSETS: array<vec2<f32>, 36> = array<vec2<f32>, 36>(
vec2<f32>(0.117851, 0.000000),
vec2<f32>(-0.150515, 0.137884),
vec2<f32>(0.023039, -0.262514),
vec2<f32>(0.189714, 0.247449),
vec2<f32>(-0.348149, -0.061583),
vec2<f32>(0.329797, -0.209790),
vec2<f32>(-0.110311, 0.410350),
vec2<f32>(-0.210374, -0.405063),
vec2<f32>(0.456428, 0.166687),
vec2<f32>(-0.474837, 0.196006),
vec2<f32>(0.228903, -0.489152),
vec2<f32>(0.169153, 0.539288),
vec2<f32>(-0.509831, -0.295457),
vec2<f32>(0.598089, -0.131488),
vec2<f32>(-0.365005, 0.519181),
vec2<f32>(-0.084325, -0.650726),
vec2<f32>(0.517670, 0.436293),
vec2<f32>(-0.696621, 0.028807),
vec2<f32>(0.508132, -0.505659),
vec2<f32>(-0.033996, 0.735194),
vec2<f32>(-0.483489, -0.579381),
vec2<f32>(0.765900, 0.103051),
vec2<f32>(-0.648945, 0.451519),
vec2<f32>(0.177329, -0.788246),
vec2<f32>(0.410153, 0.715772),
vec2<f32>(-0.801810, -0.255799),
vec2<f32>(0.778857, -0.359852),
vec2<f32>(-0.337420, 0.806248),
vec2<f32>(-0.301140, -0.837246),
vec2<f32>(0.801302, 0.421142),
vec2<f32>(-0.890044, 0.234613),
vec2<f32>(0.505907, -0.786802),
vec2<f32>(0.160932, 0.936418),
vec2<f32>(-0.762677, -0.590660),
vec2<f32>(0.975603, -0.080827),
vec2<f32>(-0.674347, 0.728949),
);
@fragment
fn effect_fs(input: VertexOutput) -> @location(0) vec4<f32> {
return blur_fs(input) * composite_coverage(input.uv) * get_float(254u);
}
fn blur_fs(input: VertexOutput) -> vec4<f32> {
let map = region_map();
let texture_size = logical_extent();
let effect_rect = vec4<f32>(
get_float(248u), get_float(249u), get_float(250u), get_float(251u)
);
let local = clamp(
(input.uv * texture_size - effect_rect.xy) / max(effect_rect.zw, vec2<f32>(1.0)),
vec2<f32>(0.0),
vec2<f32>(1.0)
);
let direction = get_float(2u);
var axis = local.x;
if direction >= 0.5 && direction < 1.5 {
axis = 1.0 - local.x;
} else if direction >= 1.5 && direction < 2.5 {
axis = local.y;
} else if direction >= 2.5 {
axis = 1.0 - local.y;
}
let progress = smoothstep(0.0, 1.0, axis);
let radius_px = mix(get_float(0u), get_float(1u), progress);
if radius_px < 0.25 {
return textureSample(input_texture, input_sampler, map_uv(map, input.uv));
}
if (has_substrates()) {
let wide_radius = max(get_float(0u), get_float(1u));
let half_radius = wide_radius * 0.5;
let quarter_radius = wide_radius * 0.25;
if (radius_px <= quarter_radius) {
let sharp = textureSampleLevel(input_texture, input_sampler, map_uv(map, input.uv), 0.0);
return mix(sharp, substrate_tap(224u, input.uv), radius_px / max(quarter_radius, 0.001));
}
if (radius_px <= half_radius) {
let span = max(half_radius - quarter_radius, 0.001);
return mix(substrate_tap(224u, input.uv), substrate_tap(228u, input.uv), (radius_px - quarter_radius) / span);
}
let span = max(wide_radius - half_radius, 0.001);
return mix(substrate_tap(228u, input.uv), substrate_tap(232u, input.uv), clamp((radius_px - half_radius) / span, 0.0, 1.0));
}
let texel = vec2<f32>(radius_px) / max(texture_size, vec2<f32>(1.0));
var color = textureSample(input_texture, input_sampler, map_uv(map, input.uv)) * 1.5;
var total_weight = 1.5;
for (var i: u32 = 0u; i < 36u; i = i + 1u) {
let offset = POISSON_OFFSETS[i];
let radius_squared = dot(offset, offset);
let weight = 1.25 - 0.75 * radius_squared;
let sample_uv = clamp(
input.uv + offset * texel,
vec2<f32>(0.0),
vec2<f32>(1.0)
);
color = color + textureSample(input_texture, input_sampler, map_uv(map, sample_uv)) * weight;
total_weight = total_weight + weight;
}
return color / max(total_weight, 0.00001);
}