60 lines
2.5 KiB
Rust
60 lines
2.5 KiB
Rust
//! Classic 3D depth of field on the GPU (`bokeh_*` kernels): the plane buffer padded, its
|
|
//! highlights boosted, row prefix sums, then one span gather per blur level
|
|
//! ([`effectcraft_render::three_d::bokeh::kernel_spans`]) accumulated with each pixel's level
|
|
//! weight — the CPU's `progressive_blur` step for step.
|
|
|
|
use effectcraft_render::three_d::PlaneDof;
|
|
use effectcraft_render::three_d::bokeh::kernel_spans;
|
|
|
|
use crate::context::{Enc, GpuImage, Params};
|
|
|
|
/// Blur `src` (an unpadded plane buffer) as `d` says. Returns the padded, blurred image.
|
|
pub(crate) fn apply(e: &mut Enc, src: &GpuImage, d: &PlaneDof) -> Option<GpuImage> {
|
|
let (w, h) = (src.width + 2 * d.pad, src.height + 2 * d.pad);
|
|
if !e.g.fits(w + 1, h) || d.radius.len() != w as usize * h as usize || d.levels.is_empty() {
|
|
return None;
|
|
}
|
|
let padded = e.image(w, h);
|
|
e.copy_into(src, &padded, d.pad, d.pad);
|
|
let boosted = if d.highlight.gain > 0.0 {
|
|
let out = e.scratch(w, h);
|
|
let mut p = Params::default();
|
|
p.f[0] = [d.highlight.gain as f32, d.highlight.threshold as f32, d.highlight.saturation as f32, 0.0];
|
|
e.pixels("bokeh_boost", &p, &padded, None, &out, None);
|
|
out
|
|
} else {
|
|
padded
|
|
};
|
|
let prefix = e.image(w + 1, h);
|
|
e.dispatch("bokeh_prefix", &Params::default(), &boosted, None, &prefix, None, (h.div_ceil(64), 1));
|
|
// Radius map, then every level's spans.
|
|
let mut data: Vec<f32> = d.radius.as_ref().clone();
|
|
let mut levels = vec![];
|
|
for &r in &d.levels {
|
|
let (kernels, norm) = kernel_spans(&d.iris, r as f64).unwrap_or_else(|| (vec![(1.0, vec![(0, -0.5, 0.5)])], 1.0));
|
|
let off = data.len() as u32;
|
|
let mut count = 0u32;
|
|
for (wt, spans) in &kernels {
|
|
for &(dy, x0, x1) in spans {
|
|
data.extend_from_slice(&[dy as f32, x0 as f32, x1 as f32, *wt]);
|
|
count += 1;
|
|
}
|
|
}
|
|
levels.push((off, count, norm));
|
|
}
|
|
let buf = e.data(&data);
|
|
let n = d.levels.len() as u32;
|
|
let (lo, hi) = (d.levels[0], d.levels[d.levels.len() - 1]);
|
|
let mut acc = e.image(w, h);
|
|
for (k, (off, count, norm)) in levels.into_iter().enumerate() {
|
|
let out = e.scratch(w, h);
|
|
let mut p = Params::default();
|
|
p.u[0] = [off, count, k as u32, n];
|
|
p.u[1] = [u32::from(k == 0), 0, 0, 0];
|
|
p.f[0] = [1.0 / norm, lo, hi, 0.0];
|
|
e.pixels("bokeh_gather", &p, &prefix, Some(&acc), &out, Some(&buf));
|
|
acc = out;
|
|
}
|
|
Some(acc)
|
|
}
|