accumulator blur

This commit is contained in:
2025-12-24 15:55:44 -06:00
parent 971299f062
commit 37c360dc67
3 changed files with 144 additions and 6 deletions

View File

@@ -176,6 +176,7 @@ function compile_group(node, ctx, target, target_size, parent_target, parent_siz
// Apply effects if any
if (node.effects) {
for (var effect of node.effects) {
effect._node_id = node.name || `node_${ctx.target_counter}`
target = compile_effect(effect, ctx, target, target_size)
}
}
@@ -308,6 +309,10 @@ function compile_effect(effect, ctx, input_target, target_size) {
return compile_blur_effect(effect, ctx, input_target, target_size)
}
if (effect_type == 'accumulator') {
return compile_accumulator_effect(effect, ctx, input_target, target_size)
}
return input_target
}
@@ -444,6 +449,46 @@ function compile_blur_effect(effect, ctx, input_target, target_size) {
return src
}
// Compile accumulator effect (motion blur)
function compile_accumulator_effect(effect, ctx, input_target, target_size) {
var decay = effect.decay != null ? effect.decay : 0.9
var node_id = effect._node_id || ctx.target_counter++
// Create persistent targets for ping-ponging
// We use stable keys based on node_id to ensure they persist across frames in the backend
var accum_prev_key = `accum_prev_${node_id}`
var accum_curr_key = `accum_curr_${node_id}`
var accum_prev = {type: 'target', key: accum_prev_key, width: target_size.width, height: target_size.height, persistent: true}
var accum_curr = {type: 'target', key: accum_curr_key, width: target_size.width, height: target_size.height, persistent: true}
// Register them in the plan so the backend knows to create them
ctx.targets[accum_prev_key] = {width: target_size.width, height: target_size.height, name: accum_prev_key, persistent: true}
ctx.targets[accum_curr_key] = {width: target_size.width, height: target_size.height, name: accum_curr_key, persistent: true}
// Accumulation pass: curr = max(input, prev * decay)
ctx.passes.push({
type: 'shader_pass',
shader: 'accumulator',
input: input_target,
extra_inputs: [accum_prev],
output: accum_curr,
uniforms: {decay: decay}
})
// Feedback pass: copy curr back to prev for next frame
ctx.passes.push({
type: 'composite',
source: accum_curr,
dest: accum_prev,
source_size: target_size,
dest_size: target_size,
presentation: 'disabled'
})
return accum_curr
}
// ========================================================================
// HELPERS
// ========================================================================
@@ -600,11 +645,18 @@ function execute_pass(pass, renderers, backend, resolve_target) {
case 'shader_pass':
var input = resolve_target(pass.input)
var output = resolve_target(pass.output)
var extra_inputs = []
if (pass.extra_inputs) {
for (var t of pass.extra_inputs) {
extra_inputs.push(resolve_target(t))
}
}
commands.push({
cmd: 'shader_pass',
shader: pass.shader,
input: input,
output: output,
extra_inputs: extra_inputs,
uniforms: pass.uniforms
})
break