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}