playcanvas
Version:
PlayCanvas WebGL game engine
100 lines (72 loc) • 3.65 kB
JavaScript
var wgsltaaResolvePS = /* wgsl */ `
var sourceTexture: texture_2d<f32>;
var sourceTextureSampler: sampler;
var historyTexture: texture_2d<f32>;
var historyTextureSampler: sampler;
uniform matrix_viewProjectionPrevious: mat4x4f;
uniform matrix_viewProjectionInverse: mat4x4f;
uniform jitters: vec4f; // xy: current frame, zw: previous frame
uniform textureSize: vec2f;
varying uv0: vec2f;
fn reproject(uv: vec2f, depth: f32) -> vec2f {
var ndc = vec4f(uv * 2.0 - 1.0, depth, 1.0);
// remove jitter from the current frame
ndc = vec4f(ndc.xy - uniform.jitters.xy, ndc.zw);
// Transform NDC to world space of the current frame
var worldPosition = uniform.matrix_viewProjectionInverse * ndc;
worldPosition = worldPosition / worldPosition.w;
// world position to screen space of the previous frame
let screenPrevious = uniform.matrix_viewProjectionPrevious * worldPosition;
return (screenPrevious.xy / screenPrevious.w) * 0.5 + 0.5;
}
fn colorClamp(uv: vec2f, historyColor: vec4f) -> vec4f {
// out of range numbers
var minColor = vec3f(9999.0);
var maxColor = vec3f(-9999.0);
// sample a 3x3 neighborhood
for (var ix: i32 = -1; ix <= 1; ix = ix + 1) {
for (var iy: i32 = -1; iy <= 1; iy = iy + 1) {
let color_sample = textureSample(sourceTexture, sourceTextureSampler, uv + vec2f(f32(ix), f32(iy)) / uniform.textureSize).rgb;
minColor = min(minColor, color_sample);
maxColor = max(maxColor, color_sample);
}
}
// clamp the history color to min/max bounding box
let clamped = clamp(historyColor.rgb, minColor, maxColor);
return vec4f(clamped, historyColor.a);
}
@fragment
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
var output: FragmentOutput;
var uv = input.uv0;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// This hack is needed on webgpu, which makes TAA work but the resulting image is upside-down.
// We could flip the image in the following pass, but ideally a better solution should be found.
uv.y = 1.0 - uv.y;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// current frame
let srcColor = textureSample(sourceTexture, sourceTextureSampler, uv);
// current depth is in linear space, convert it to non-linear space
let linearDepth = getLinearScreenDepth(uv0);
let depth = delinearizeDepth(linearDepth);
// previous frame
let historyUv = reproject(uv0, depth);
// high quality history, sharper result
var historyColor: vec4f = SampleTextureCatmullRom(historyTexture, historyTextureSampler, historyUv, uniform.textureSize);
// single sample history, more blurry result
var historyColor: vec4f = textureSample(historyTexture, historyTextureSampler, historyUv);
// handle disocclusion by clamping the history color
let historyColorClamped = colorClamp(uv, historyColor);
// handle history buffer outside of the frame
let mixFactor_condition = historyUv.x < 0.0 || historyUv.x > 1.0 || historyUv.y < 0.0 || historyUv.y > 1.0;
let mixFactor = select(0.05, 1.0, mixFactor_condition);
output.color = mix(historyColorClamped, srcColor, mixFactor);
return output;
}
`;
export { wgsltaaResolvePS as default };