Skip to main content

lumen_engine/node/processing/
exposure.rs

1use crate::node::{NodeId, NodeParamEvalContext, NodeParams, PortRef};
2
3use crate::gpu::{
4    BoundFrame, CompiledOutput, FrameBindContext, GpuCompileNode, GpuCompiledNode, RasterHandle,
5    compiler,
6};
7
8pub(crate) const SHADER: &str = include_str!("exposure.wgsl");
9
10#[derive(Debug, Clone, lumen_macros::Delegate)]
11pub struct ExposureParams {
12    /// Exposure offset in stops.
13    #[meta(step = 0.01)]
14    pub exposure: f64,
15    /// Contrast multiplier.
16    #[meta(min = 0, step = 0.01)]
17    pub contrast: f64,
18    /// Linear color offset.
19    #[meta(step = 0.01)]
20    pub offset: f64,
21}
22
23impl Default for ExposureParams {
24    fn default() -> Self {
25        Self {
26            exposure: 0.0,
27            contrast: 1.0,
28            offset: 0.0,
29        }
30    }
31}
32
33/// Adjusts raster exposure, contrast, and offset.
34#[derive(Debug, Clone, lumen_macros::Node)]
35#[node(kind = "exposure", name = "Exposure", category = "processing")]
36pub struct Exposure {
37    pub id: NodeId,
38    #[params]
39    pub params: ExposureParamsDelegate,
40    #[input()]
41    pub source: PortRef,
42}
43
44impl Default for Exposure {
45    fn default() -> Self {
46        Self {
47            id: NodeId::new(0),
48            params: ExposureParamsDelegate::default(),
49            source: PortRef::empty(),
50        }
51    }
52}
53
54#[derive(Debug, Clone)]
55struct CompiledExposure {
56    node_id: NodeId,
57    params: ExposureParamsDelegate,
58    buffer: lumen_gpu::BufferId,
59}
60
61impl GpuCompiledNode for CompiledExposure {
62    fn node_id(&self) -> NodeId {
63        self.node_id
64    }
65
66    fn bind(&self, ctx: &FrameBindContext<'_>, bound: &mut BoundFrame) -> crate::Result<()> {
67        let params = self.params.eval(&NodeParamEvalContext {
68            node_id: self.node_id,
69            expr: &ctx.expr_context(self.node_id, "params"),
70        })?;
71        let params = compiler::ExposureParams {
72            exposure: params.exposure as f32,
73            contrast: params.contrast as f32,
74            offset: params.offset as f32,
75            _pad: 0.0,
76        };
77        bound.write_buffer(self.buffer, 0, bytemuck::bytes_of(&params));
78        Ok(())
79    }
80}
81
82impl GpuCompileNode for Exposure {
83    fn compile_gpu(
84        &self,
85        ctx: &mut crate::gpu::CompileContext<'_>,
86        port: &PortRef,
87    ) -> crate::Result<CompiledOutput> {
88        let (source, texture, params) = ctx.compile_unary_filter(
89            self.id,
90            &self.source,
91            port,
92            "exposure",
93            SHADER,
94            std::mem::size_of::<compiler::ExposureParams>() as u64,
95        )?;
96        ctx.register_compiled_node(CompiledExposure {
97            node_id: self.id,
98            params: self.params.clone(),
99            buffer: params,
100        });
101        Ok(CompiledOutput::Raster(RasterHandle {
102            texture,
103            domain: source.domain,
104            metadata: source.metadata,
105        }))
106    }
107}