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 88 89 90 91 92 93 94 95 96
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Module providing render targets for both Software and Hardware accelerated image rendering.
use itertools::Itertools as _;
use rand::prelude::*;
use rand_pcg::Pcg64Mcg;
use crate::ray::RayResult;
mod software;
pub use software::Image;
#[cfg(feature = "gpu")]
mod vulkan;
#[cfg(feature = "gpu")]
mod vulkan_context;
#[cfg(feature = "gpu")]
pub use vulkan::VulkanImage;
fn calculate_scale(width: f64, height: f64, lightpower: f32, rays: usize, exposure: f64) -> f32 {
let area_scale = f64::sqrt((width * height) / (1024.0 * 576.0));
let intensity_scale = lightpower as f64 / (255.0 * 8192.0);
(f64::exp(1.0 + (10.0 as f64 * exposure)) * area_scale * intensity_scale / rays as f64) as f32
}
/// This trait provideds an render target to be passed to the renderer.
pub trait RenderImage: Send + Sync {
/// Called in every rendering thread to add a Ray to the render target
fn draw_line(&self, ray: RayResult);
/// called momentarilly before the render begins to allow the render target to set it's self up to receive calls to `draw_line`
fn prepare_render(&mut self, lightpower: f32);
/// called immediately after a render ends to allow the render target to clean up or finalize results
fn finish_render(&mut self) {}
}
/// This trait provides interfaces to extract the image data from a render target.
pub trait ExportImage {
/// Returns the image size in pixels in the format `(width, height)`
fn get_size(&self) -> (usize, usize);
/// Returns the lightpower of the scene most recently rendered to this image.
fn get_lightpower(&self) -> f32;
/// Outputs the image.
/// Serialsiing the image to a sequence of 32 bit floating point RGBA samples stored in a `Vec<f32>`,
/// suitible for use in high bit depth images such as used by blender.
fn to_rgbaf32(&self) -> Vec<f32>;
/// Outputs the image.
/// Serialsiing the image to a sequence of 8 bit RGB samples stored in a `Vec<u8>`,
/// suitible for use in file streams and other outputs.
///
/// This function also normalises the image applying exposure and gamma.
/// gamma is passed in the form of an exponent which is defined as `1.0 / gamma`
fn to_rgba8(&self, rays: usize, exposure: f64, exponent: f32) -> Vec<u8> {
let (width, height) = self.get_size();
let scale = calculate_scale(
width as f64,
height as f64,
self.get_lightpower(),
rays,
exposure,
);
let mut rng = Pcg64Mcg::new(0xcafef00dd15ea5e5);
self.to_rgbaf32()
.iter()
.tuples()
.flat_map(|(r, g, b, _a)| {
// red
let u: f32 = 0f32.max(r * scale);
let dither = rng.gen_range(0f32..1f32);
let v: f32 = 255.0 * u.powf(exponent) + dither;
let r8 = 0f32.max(255.9f32.min(v));
// green
let u: f32 = 0f32.max(g * scale);
let dither = rng.gen_range(0f32..1f32);
let v: f32 = 255.0 * u.powf(exponent) + dither;
let g8 = 0f32.max(255.9f32.min(v));
// blue
let u: f32 = 0f32.max(b * scale);
let dither = rng.gen_range(0f32..1f32);
let v: f32 = 255.0 * u.powf(exponent) + dither;
let b8 = 0f32.max(255.9f32.min(v));
[r8 as u8, g8 as u8, b8 as u8, 255u8]
})
.collect()
}
}