rustic_zen/image/
mod.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Module providing render targets for both Software and Hardware accelerated image rendering.
6
7use itertools::Itertools as _;
8use rand::prelude::*;
9use rand_pcg::Pcg64Mcg;
10
11use crate::ray::RayResult;
12
13mod software;
14pub use software::Image;
15
16#[cfg(feature = "gpu")]
17mod vulkan;
18#[cfg(feature = "gpu")]
19mod vulkan_context;
20#[cfg(feature = "gpu")]
21pub use vulkan::VulkanImage;
22
23fn calculate_scale(width: f64, height: f64, lightpower: f32, rays: usize, exposure: f64) -> f32 {
24    let area_scale = f64::sqrt((width * height) / (1024.0 * 576.0));
25    let intensity_scale = lightpower as f64 / (255.0 * 8192.0);
26    (f64::exp(1.0 + (10.0 as f64 * exposure)) * area_scale * intensity_scale / rays as f64) as f32
27}
28
29/// This trait provideds an render target to be passed to the renderer.
30pub trait RenderImage: Send + Sync {
31    /// Called in every rendering thread to add a Ray to the render target
32    fn draw_line(&self, ray: RayResult);
33
34    /// called momentarilly before the render begins to allow the render target to set it's self up to receive calls to `draw_line`
35    fn prepare_render(&mut self, lightpower: f32);
36
37    /// called immediately after a render ends to allow the render target to clean up or finalize results
38    fn finish_render(&mut self) {}
39}
40
41/// This trait provides interfaces to extract the image data from a render target.
42pub trait ExportImage {
43    /// Returns the image size in pixels in the format `(width, height)`
44    fn get_size(&self) -> (usize, usize);
45
46    /// Returns the lightpower of the scene most recently rendered to this image.
47    fn get_lightpower(&self) -> f32;
48
49    /// Outputs the image.
50    /// Serialsiing the image to a sequence of 32 bit floating point RGBA samples stored in a `Vec<f32>`,
51    /// suitible for use in high bit depth images such as used by blender.
52    fn to_rgbaf32(&self) -> Vec<f32>;
53
54    /// Outputs the image.
55    /// Serialsiing the image to a sequence of 8 bit RGB samples stored in a `Vec<u8>`,
56    /// suitible for use in file streams and other outputs.
57    ///
58    /// This function also normalises the image applying exposure and gamma.
59    /// gamma is passed in the form of an exponent which is defined as `1.0 / gamma`
60    fn to_rgba8(&self, rays: usize, exposure: f64, exponent: f32) -> Vec<u8> {
61        let (width, height) = self.get_size();
62        let scale = calculate_scale(
63            width as f64,
64            height as f64,
65            self.get_lightpower(),
66            rays,
67            exposure,
68        );
69        let mut rng = Pcg64Mcg::new(0xcafef00dd15ea5e5);
70        self.to_rgbaf32()
71            .iter()
72            .tuples()
73            .flat_map(|(r, g, b, _a)| {
74                // red
75                let u: f32 = 0f32.max(r * scale);
76                let dither = rng.gen_range(0f32..1f32);
77                let v: f32 = 255.0 * u.powf(exponent) + dither;
78                let r8 = 0f32.max(255.9f32.min(v));
79
80                // green
81                let u: f32 = 0f32.max(g * scale);
82                let dither = rng.gen_range(0f32..1f32);
83                let v: f32 = 255.0 * u.powf(exponent) + dither;
84                let g8 = 0f32.max(255.9f32.min(v));
85
86                // blue
87                let u: f32 = 0f32.max(b * scale);
88                let dither = rng.gen_range(0f32..1f32);
89                let v: f32 = 255.0 * u.powf(exponent) + dither;
90                let b8 = 0f32.max(255.9f32.min(v));
91
92                [r8 as u8, g8 as u8, b8 as u8, 255u8]
93            })
94            .collect()
95    }
96}