rustic_zen/scene/
light.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
5use crate::sampler::{Sampler, SamplerPoint};
6use rand::prelude::*;
7
8/// Definition of a Light Source in for a scene.
9///
10/// # Examples
11/// Manually make a warm white small round light:
12/// ```
13/// use rustic_zen::prelude::*;
14///
15/// let l = Light {
16///     power: Sampler::new_const(1.0),
17///     location: (512.0, 512.0).into(),
18///     polar_angle: Sampler::new_const(0.0),
19///     polar_distance: Sampler::new_const(0.0),
20///     ray_angle: Sampler::new_range(360.0, 0.0),
21///     wavelength: Sampler::new_blackbody(5800.0),
22/// };
23///
24/// let scene = Scene::new(1920,1080).with_light(l);
25/// ```
26#[derive(Clone)]
27pub struct Light<R: Rng> {
28    /// coordinates of the light in the scene.
29    pub location: SamplerPoint<R>,
30    /// Brightness of this light relative to other lights in the scene. This is a proportion of the total
31    /// power of all lights of the scene. So if you have 50 lights of power 1 each light has a 1 in 50
32    /// chance of being selected to spawn a ray. However adding 1 more light with power 10 will lower the
33    /// existing lights to 1 in 60 and the new light will have 1 in 6. once used this is quite intuitive,
34    /// the power will change the relative brightness of the lights, without changing the overall brightness
35    /// of the scene.
36    pub power: Sampler<f64, R>,
37    /// distance from `location` that the light ray will spawn,
38    /// this can be used to create lights that are larger than a single pixel
39    /// Fixing this to a single value will create a ring where the rays spawn,
40    /// using a range of 0 - `size` will create a filled circle of spawn points.
41    pub polar_distance: Sampler<f64, R>,
42    /// combined with `polar_distance` this creates a vector that is added to `location`
43    /// to generate the final point the light ray will spawn. Using a range of 360 - 0
44    /// will create a full circle, constraining this can create an arc.
45    pub polar_angle: Sampler<f64, R>,
46    /// Angle at which the spawned ray will fly away from the light. This is in absolute
47    /// screen angle (0 is right). Setting this to a range of 0 - 360 will create an omidirectional
48    /// light, constraining this range creates a more cone like light. Constraining it all
49    /// the way to a single fixed value will create a laser. Using a `polar_distance` > 0 is
50    /// recomended if the `ray_angle` is constrained to a single value, as a ray 1 pixel wide can be
51    /// hard to see.  
52    pub ray_angle: Sampler<f64, R>,
53    /// Wavelength of spawned ray in nanometers. Only values of  380.0 - 700.0 are visible.
54    /// Using a fixed value will create a strongly coloured light of that wavelength. To create natural
55    /// looking white lights use `Sampler::new_blackbody(<temp>)`. Blackbody tempratures of 1,000 to
56    /// 10,000 kelvin are visible.
57    ///
58    /// # Warning
59    /// __Rays are sampled until a visible wavelength is found, this is an optimisation, selecting a fixed value
60    /// or range of values outside of the visible spectrum can crash the render. So can selecting extreme
61    /// blackbody tempratures.__
62    pub wavelength: Sampler<f64, R>,
63}
64
65impl<R> Light<R>
66where
67    R: Rng,
68{
69    /// Helper function to create a Light quickly, using common casts
70    /// available in the library.
71    ///
72    /// # Example Usage
73    /// Create the same small round light as above using the helper:
74    /// ```
75    /// use rustic_zen::prelude::*;
76    ///
77    /// let l = Light::new((512.0,512.0), 1.0, 0.0, 0.0, (360.0, 0.0), Sampler::new_blackbody(5800.0));
78    ///
79    /// let scene = Scene::new(1920,1080).with_light(l);
80    /// ```
81    pub fn new<A, B, C, D, E, F>(
82        location: A,
83        power: B,
84        polar_distance: C,
85        polar_angle: D,
86        ray_angle: E,
87        wavelength: F,
88    ) -> Self
89    where
90        A: Into<SamplerPoint<R>>,
91        B: Into<Sampler<f64, R>>,
92        C: Into<Sampler<f64, R>>,
93        D: Into<Sampler<f64, R>>,
94        E: Into<Sampler<f64, R>>,
95        F: Into<Sampler<f64, R>>,
96    {
97        Self {
98            location: location.into(),
99            power: power.into(),
100            polar_distance: polar_distance.into(),
101            polar_angle: polar_angle.into(),
102            ray_angle: ray_angle.into(),
103            wavelength: wavelength.into(),
104        }
105    }
106}