From 9eeb85c838c0ac519427380704a75a51b792741b Mon Sep 17 00:00:00 2001 From: Walther Date: Tue, 3 Oct 2023 12:52:40 +0300 Subject: [PATCH] feat: add a ConeLight material --- clovers/src/materials.rs | 4 ++ clovers/src/materials/cone_light.rs | 91 +++++++++++++++++++++++++++++ scenes/dispersive.json | 87 ++++++++++----------------- 3 files changed, 127 insertions(+), 55 deletions(-) create mode 100644 clovers/src/materials/cone_light.rs diff --git a/clovers/src/materials.rs b/clovers/src/materials.rs index 7c35d412..be536cb7 100644 --- a/clovers/src/materials.rs +++ b/clovers/src/materials.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use crate::{color::Color, hitable::HitRecord, pdf::PDF, ray::Ray, Float, Vec3}; +pub mod cone_light; pub mod dielectric; pub mod diffuse_light; pub mod dispersive; @@ -12,6 +13,7 @@ pub mod isotropic; pub mod lambertian; pub mod metal; +pub use cone_light::*; pub use dielectric::*; pub use diffuse_light::*; pub use dispersive::*; @@ -93,6 +95,8 @@ pub enum Material { Dispersive(Dispersive), /// Lambertian material Lambertian(Lambertian), + /// ConeLight material + ConeLight(ConeLight), /// DiffuseLight material DiffuseLight(DiffuseLight), /// Metal material diff --git a/clovers/src/materials/cone_light.rs b/clovers/src/materials/cone_light.rs new file mode 100644 index 00000000..e12cf776 --- /dev/null +++ b/clovers/src/materials/cone_light.rs @@ -0,0 +1,91 @@ +//! A cone light material. + +use super::{MaterialTrait, ScatterRecord}; +use crate::{ + color::Color, + hitable::HitRecord, + ray::Ray, + textures::{SolidColor, Texture, TextureTrait}, + Float, Vec3, +}; +use rand::prelude::SmallRng; + +/// A cone light material. The material emits light if the incoming ray is within a certain amount of degrees from the surface normal. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))] +pub struct ConeLight { + spread: Float, + emit: Texture, +} + +impl Default for ConeLight { + /// Creates a new [`ConeLight`] with white light at intensity `100.0` and a spread of 10 degrees. + fn default() -> Self { + ConeLight { + spread: 10.0, + emit: Texture::SolidColor(SolidColor::new(Color::new(100.0, 100.0, 100.0))), + } + } +} + +impl MaterialTrait for ConeLight { + /// Scatter method for the [`ConeLight`] material. Always returns `None`, as diffuse light does not scatter. + #[allow(clippy::unused_self)] + #[must_use] + fn scatter( + &self, + _ray: &Ray, + _hit_record: &HitRecord, + _rng: &mut SmallRng, + ) -> Option { + None + } + + /// Scattering probability density function for the [`ConeLight`] material. Always returns 0, as diffuse light does not scatter. + #[allow(clippy::unused_self)] // TODO: + #[must_use] + fn scattering_pdf( + &self, + _hit_record: &HitRecord, + _scattered: &Ray, + _rng: &mut SmallRng, + ) -> Option { + None + } + + /// Emission function for [`ConeLight`]. If the given [`HitRecord`] has been hit on the `front_face`, emit a color based on the texture and surface coordinates. Otherwise, emit pure black. + #[must_use] + fn emit(&self, ray: &Ray, hit_record: &HitRecord, u: Float, v: Float, position: Vec3) -> Color { + // If we don't hit the front face, return black + if !hit_record.front_face { + return Color::new(0.0, 0.0, 0.0); + } + + // We have hit the front. Calculate the angle of incidence + let spread_radians = self.spread.to_radians(); + let angle = (-ray.direction.dot(&hit_record.normal) + / (ray.direction.magnitude() * hit_record.normal.magnitude())) + .acos(); + + let emit = self.emit.color(u, v, position); + if angle <= spread_radians { + emit + } else { + // Make sure that the front face of the lamp is tinted, even outside the main lighting angle + let scaling_factor = Vec3::from(emit).max(); + if scaling_factor > 1.0 { + emit / scaling_factor + } else { + emit + } + } + } +} + +impl ConeLight { + /// Creates a new [`ConeLight`] material with the given [Texture]. + #[must_use] + pub fn new(spread: Float, emit: Texture) -> Self { + ConeLight { spread, emit } + } +} diff --git a/scenes/dispersive.json b/scenes/dispersive.json index c25bb51c..08a08e56 100644 --- a/scenes/dispersive.json +++ b/scenes/dispersive.json @@ -2,10 +2,10 @@ "time_0": 0, "time_1": 1, "camera": { - "look_from": [-150, 278, -800], - "look_at": [450, 275, 555], + "look_from": [-600, 310, -400], + "look_at": [0, 310, 0], "up": [0, 1, 0], - "vertical_fov": 40, + "vertical_fov": 35, "aperture": 0, "focus_distance": 10 }, @@ -21,55 +21,23 @@ }, { "kind": "Quad", - "q": [0, 0, 0], - "u": [555, 0, 0], - "v": [0, 0, 555], - "material": "grey wall", - "comment": "floor" - }, - { - "kind": "Quad", - "q": [0, 555, 0], - "u": [555, 0, 0], - "v": [0, 0, 555], - "material": "grey wall", - "comment": "ceiling" + "q": [113, 555, 127], + "u": [330, 0, 0], + "v": [0, 0, 305], + "material": "big lamp", + "comment": "big ceiling light" }, { "kind": "Quad", - "q": [277, 554, 120], - "u": [2, 0, 0], - "v": [0, 0, 200], - "material": "strong lamp", + "q": [277, 554, 140], + "u": [10, 0, 0], + "v": [0, 0, 278], + "material": "narrow lamp", "comment": "narrow ceiling light" }, - { - "kind": "Quad", - "q": [279, 554, 120], - "u": [0, -40, 0], - "v": [0, 0, 200], - "material": "grey wall", - "comment": "light guide, left" - }, - { - "kind": "Quad", - "q": [277, 554, 120], - "u": [0, -40, 0], - "v": [0, 0, 200], - "material": "grey wall", - "comment": "light guide, right" - }, - { - "kind": "Quad", - "q": [0, 0, 555], - "u": [555, 0, 0], - "v": [0, 555, 0], - "material": "grey wall", - "comment": "back wall" - }, { "kind": "STL", - "center": [300, 350, 250], + "center": [300, 350, 278], "scale": 20, "rotation": [0, 0, -30], "path": "stl/prism.stl", @@ -80,15 +48,23 @@ "priority_objects": [ { "kind": "Quad", - "q": [277, 554, 120], - "u": [2, 0, 0], - "v": [0, 0, 200], - "material": "strong lamp", + "q": [113, 555, 127], + "u": [330, 0, 0], + "v": [0, 0, 305], + "material": "big lamp", + "comment": "big ceiling light" + }, + { + "kind": "Quad", + "q": [277, 554, 140], + "u": [10, 0, 0], + "v": [0, 0, 278], + "material": "narrow lamp", "comment": "narrow ceiling light" }, { "kind": "STL", - "center": [300, 350, 250], + "center": [300, 350, 278], "scale": 20, "rotation": [0, 0, -30], "path": "stl/prism.stl", @@ -146,19 +122,20 @@ "cauchy_b": 0.5 }, { - "name": "weak lamp", + "name": "big lamp", "kind": "DiffuseLight", "emit": { "kind": "SolidColor", - "color": [10, 10, 10] + "color": [2, 2, 2] } }, { - "name": "strong lamp", - "kind": "DiffuseLight", + "name": "narrow lamp", + "kind": "ConeLight", + "spread": 2.0, "emit": { "kind": "SolidColor", - "color": [1000, 1000, 1000] + "color": [500, 500, 500] } }, {