// What a post effect's code is passed and what it may read. Two seams:
// one for an effect's own values and code, one for the scene depth
// binding, whose
// type follows the sample count. The depth resolve is drawn from this
// shader with the first seam filled by a `draw` that returns the pixel it
// was passed.
struct Pixel {
// What the chain holds at this pixel.
color: vec4<f32>,
// Across the frame, zero at its top left.
uv: vec2<f32>,
// Physical pixels, the center of each at a half.
position: vec2<f32>,
// The frame, in physical pixels.
size: vec2<f32>,
}
// The view the frame was drawn under: its size in physical pixels, the
// near and far clip in meters, and whether the lens foreshortens.
struct Frame {
size: vec2<f32>,
near: f32,
far: f32,
// 1 where the lens foreshortens, 0 otherwise.
perspective: u32,
}
const FORESHORTENED: u32 = 1u;
@group(1) @binding(0) var source: texture_2d<f32>;
@group(1) @binding(1) var source_sampler: sampler;
@group(1) @binding(2) var resolved: texture_2d<f32>;
// mirage-engine:depth
@group(2) @binding(1) var<uniform> frame: Frame;
struct Fragment {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
}
// One triangle large enough to cover the target, so no vertex buffer is bound.
@vertex
fn fullscreen(@builtin(vertex_index) index: u32) -> Fragment {
let corner = vec2<f32>(f32((index << 1u) & 2u), f32(index & 2u));
var fragment: Fragment;
fragment.position = vec4<f32>(corner * 2.0 - 1.0, 0.0, 1.0);
fragment.uv = vec2<f32>(corner.x, 1.0 - corner.y);
return fragment;
}
// What the chain holds anywhere, held at the edges past the frame.
fn color_at(uv: vec2<f32>) -> vec4<f32> {
return textureSample(source, source_sampler, uv);
}
// The view depth in meters the scene was drawn at, held at the edges past
// the frame, and the far clip where nothing was drawn.
fn depth_at(uv: vec2<f32>) -> f32 {
let held = clamp(uv * frame.size, vec2<f32>(0.0), frame.size - 1.0);
return textureLoad(resolved, vec2<i32>(held), 0).r;
}
// mirage-engine:effect
// The seam's own `draw` over every pixel of the frame. Named so that it
// cannot meet `effect`, which is what a post effect's values are bound as.
@fragment
fn effect_fragment(fragment: Fragment) -> @location(0) vec4<f32> {
var pixel: Pixel;
pixel.color = textureSample(source, source_sampler, fragment.uv);
pixel.uv = fragment.uv;
pixel.position = fragment.position.xy;
pixel.size = frame.size;
return draw(pixel);
}
// The scene's own depth, taken to one sample per pixel and to meters along
// the view.
@fragment
fn resolve(fragment: Fragment) -> @location(0) vec4<f32> {
let at = vec2<i32>(fragment.position.xy);
let written = textureLoad(scene, at, 0);
let span = frame.far - frame.near;
var meters = frame.near + written * span;
if frame.perspective == FORESHORTENED {
meters = frame.near * frame.far / (frame.far - written * span);
}
return vec4<f32>(meters, 0.0, 0.0, 1.0);
}