1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! Depth-of-field post-processing configuration.
//!
//! Simulates camera depth-of-field by blurring regions that are out of focus
//! based on their distance from a focal plane.
/// Depth-of-field configuration.
///
/// Controls the focal distance, aperture (blur strength), and maximum blur
/// radius for the post-processing effect.
#[derive(Debug, Clone)]
pub struct DofConfig {
/// Whether depth-of-field is enabled.
pub enabled: bool,
/// Distance from the camera to the focal plane. Default: 10.0
pub focal_distance: f32,
/// Aperture size controlling blur intensity. Default: 0.05
pub aperture: f32,
/// Maximum blur radius in pixels. Default: 10.0
pub max_blur: f32,
}
impl Default for DofConfig {
fn default() -> Self {
Self {
enabled: false,
focal_distance: 10.0,
aperture: 0.05,
max_blur: 10.0,
}
}
}
impl DofConfig {
/// Create enabled DoF with default settings.
pub fn new() -> Self {
Self { enabled: true, ..Default::default() }
}
/// Set focal distance.
pub fn with_focal_distance(mut self, dist: f32) -> Self {
self.focal_distance = dist;
self
}
/// Set aperture.
pub fn with_aperture(mut self, aperture: f32) -> Self {
self.aperture = aperture;
self
}
/// Set maximum blur radius.
pub fn with_max_blur(mut self, max_blur: f32) -> Self {
self.max_blur = max_blur;
self
}
/// Compute the circle of confusion diameter for a given depth.
///
/// CoC = aperture * |depth - focal_distance| / depth
/// The result is clamped to [0, max_blur].
pub fn circle_of_confusion(&self, depth: f32) -> f32 {
if depth <= 0.0 {
return 0.0;
}
let coc = self.aperture * (depth - self.focal_distance).abs() / depth;
coc.min(self.max_blur)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_disabled() {
let c = DofConfig::default();
assert!(!c.enabled);
assert_eq!(c.focal_distance, 10.0);
}
#[test]
fn coc_at_focal_distance_is_zero() {
let c = DofConfig::new().with_focal_distance(5.0).with_aperture(0.1);
let coc = c.circle_of_confusion(5.0);
assert!(coc.abs() < 1e-6, "CoC at focal distance should be zero, got {}", coc);
}
}