Implement initial area light

This commit is contained in:
hal8174 2025-09-09 21:47:35 +02:00
parent 95092b1571
commit 333acab099
Signed by: hal8174
SSH key fingerprint: SHA256:NN98ZYwnrreQLSOV/g+amY7C3yL/mS1heD7bi5t6PPw
3 changed files with 102 additions and 22 deletions

View file

@ -17,6 +17,20 @@ impl<A, B> Either<A, B> {
Either::B(b) => Either::B(f(b)), Either::B(b) => Either::B(f(b)),
} }
} }
pub fn get_a(&self) -> Option<&A> {
match self {
Either::A(a) => Some(a),
Either::B(_) => None,
}
}
pub fn get_b(&self) -> Option<&B> {
match self {
Either::A(_) => None,
Either::B(b) => Some(b),
}
}
} }
impl<A, B, E> Either<A, Result<B, E>> { impl<A, B, E> Either<A, Result<B, E>> {

View file

@ -1,4 +1,5 @@
use crate::{ use crate::{
either::Either,
scene::PbrtScene, scene::PbrtScene,
shape::{Shape, ShapeAlpha, ShapeType}, shape::{Shape, ShapeAlpha, ShapeType},
tokenizer::{Token, Tokenizer}, tokenizer::{Token, Tokenizer},
@ -8,6 +9,7 @@ use miette::{IntoDiagnostic, Result, bail, miette};
use ray_tracing_core::{ use ray_tracing_core::{
affine_transform::AffineTransform, affine_transform::AffineTransform,
color::Color, color::Color,
light::Light,
math::{Dir3, Pos3}, math::{Dir3, Pos3},
prelude::{Float, Rng}, prelude::{Float, Rng},
}; };
@ -83,6 +85,22 @@ enum Statement<R> {
Material(Arc<dyn PbrtMaterial<R>>), Material(Arc<dyn PbrtMaterial<R>>),
MakeNamedMaterial(String, Arc<dyn PbrtMaterial<R>>), MakeNamedMaterial(String, Arc<dyn PbrtMaterial<R>>),
NamedMaterial(String), NamedMaterial(String),
AreaLight(AreaLight),
}
#[derive(Debug, Clone)]
pub struct AreaLight {
pub color: Color,
}
impl<R: Rng> Light<R> for AreaLight {
fn emit(&self, w_in: Dir3, _rng: &mut R) -> Color {
if w_in.y() > 0.0 {
self.color
} else {
Color::black()
}
}
} }
fn parse_look_at<R>(iter: &mut Tokenizer) -> Result<Statement<R>> { fn parse_look_at<R>(iter: &mut Tokenizer) -> Result<Statement<R>> {
@ -417,7 +435,9 @@ impl Lexer {
material::parse_make_named_material(&mut self.input, context) material::parse_make_named_material(&mut self.input, context)
.map(|(name, material)| Statement::MakeNamedMaterial(name, material)), .map(|(name, material)| Statement::MakeNamedMaterial(name, material)),
), ),
"NamedMaterial" => Some(self.input.parse_parameter().map(Statement::NamedMaterial)), "NamedMaterial" => {
Some(self.input.next_string_value().map(Statement::NamedMaterial))
}
"ConcatTransform" => { "ConcatTransform" => {
Some(parse_transform(&mut self.input).map(Statement::ConcatTransform)) Some(parse_transform(&mut self.input).map(Statement::ConcatTransform))
} }
@ -429,6 +449,8 @@ impl Lexer {
Ok(s) => Ok(Statement::CoordSysTransform(s)), Ok(s) => Ok(Statement::CoordSysTransform(s)),
Err(e) => Err(e), Err(e) => Err(e),
}), }),
"AreaLightSource" => Some(parse_area_light(&mut self.input)),
"WorldBegin" => Some(Ok(Statement::WorldBegin)), "WorldBegin" => Some(Ok(Statement::WorldBegin)),
_ => { _ => {
if s.chars().any(|c| !c.is_ascii_alphabetic()) { if s.chars().any(|c| !c.is_ascii_alphabetic()) {
@ -459,6 +481,22 @@ impl Lexer {
} }
} }
fn parse_area_light<R>(input: &mut Tokenizer) -> Result<Statement<R>> {
let s = input.next_string_value()?;
if s.as_str() != "diffuse" {
return Err(miette!(
labels = vec![input.last_span_labeled(Some("here"))],
"Only diffuse area light supported."
)
.with_source_code(input.get_src()));
}
Ok(Statement::AreaLight(parse_dict2!(input, AreaLight;
color, Color::white(), ["rgb L", texture::parse_rgb(input)?]
)))
}
fn parse_transform(input: &mut Tokenizer) -> Result<AffineTransform> { fn parse_transform(input: &mut Tokenizer) -> Result<AffineTransform> {
input.next_expect_bracket_open()?; input.next_expect_bracket_open()?;
let mut v = [0.0; 16]; let mut v = [0.0; 16];
@ -615,6 +653,7 @@ pub struct PbrtContext<R> {
ctm: Vec<AffineTransform>, ctm: Vec<AffineTransform>,
textures: HashMap<String, Arc<dyn PbrtTexture>>, textures: HashMap<String, Arc<dyn PbrtTexture>>,
material: Vec<Arc<dyn PbrtMaterial<R>>>, material: Vec<Arc<dyn PbrtMaterial<R>>>,
area_light: Vec<AreaLight>,
materials: HashMap<String, Arc<dyn PbrtMaterial<R>>>, materials: HashMap<String, Arc<dyn PbrtMaterial<R>>>,
} }
@ -623,6 +662,7 @@ impl<R> PbrtContext<R> {
Self { Self {
ctm: vec![AffineTransform::identity()], ctm: vec![AffineTransform::identity()],
textures: HashMap::new(), textures: HashMap::new(),
area_light: Vec::new(),
material: Vec::new(), material: Vec::new(),
materials: HashMap::new(), materials: HashMap::new(),
} }
@ -651,6 +691,11 @@ impl<R> PbrtContext<R> {
self.material self.material
.push(Arc::clone(self.material.last().unwrap())); .push(Arc::clone(self.material.last().unwrap()));
} }
if !self.area_light.is_empty() {
self.area_light
.push(self.area_light.last().unwrap().clone());
}
} }
fn pop(&mut self) -> Result<()> { fn pop(&mut self) -> Result<()> {
@ -661,6 +706,7 @@ impl<R> PbrtContext<R> {
} }
self.material.pop(); self.material.pop();
self.area_light.pop();
Ok(()) Ok(())
} }
@ -747,17 +793,26 @@ fn inner_parse_pbrt<R: Rng + std::fmt::Debug>(
} }
Statement::Shape(shape_type, shape_alpha) => { Statement::Shape(shape_type, shape_alpha) => {
dbg!(&context); dbg!(&context);
pbrt.scene.shapes.push(Shape { if context.area_light.is_empty() {
ctm: context.get_ctm(), pbrt.scene.shapes.push(Shape {
material: Arc::clone( ctm: context.get_ctm(),
context material: Either::A(Arc::clone(
.material context
.last() .material
.ok_or_else(|| miette!("No material specified"))?, .last()
), .ok_or_else(|| miette!("No material specified"))?,
obj: shape_type, )),
alpha: shape_alpha, obj: shape_type,
}); alpha: shape_alpha,
});
} else {
pbrt.scene.shapes.push(Shape {
ctm: context.get_ctm(),
material: Either::B(context.area_light.last().unwrap().clone()),
obj: shape_type,
alpha: shape_alpha,
});
}
} }
Statement::CoordinateSystem(s) => { Statement::CoordinateSystem(s) => {
named_transforms.insert(s, context.get_ctm()); named_transforms.insert(s, context.get_ctm());
@ -774,6 +829,13 @@ fn inner_parse_pbrt<R: Rng + std::fmt::Debug>(
*context.material.last_mut().unwrap() = m; *context.material.last_mut().unwrap() = m;
} }
} }
Statement::AreaLight(l) => {
if context.area_light.is_empty() {
context.area_light.push(l);
} else {
*context.area_light.last_mut().unwrap() = l;
}
}
Statement::MakeNamedMaterial(n, m) => { Statement::MakeNamedMaterial(n, m) => {
context.materials.insert(n, m); context.materials.insert(n, m);
} }
@ -801,7 +863,7 @@ fn inner_parse_pbrt<R: Rng + std::fmt::Debug>(
// dbg!(context); // dbg!(context);
Ok(pbrt) Ok(dbg!(pbrt))
} }
pub fn parse_pbrt_v4<R: Rng + std::fmt::Debug>( pub fn parse_pbrt_v4<R: Rng + std::fmt::Debug>(

View file

@ -2,13 +2,13 @@ use std::sync::Arc;
use ray_tracing_core::{affine_transform::AffineTransform, prelude::*, scene::Intersection}; use ray_tracing_core::{affine_transform::AffineTransform, prelude::*, scene::Intersection};
use crate::{material::PbrtMaterial, scene::UVMaterial}; use crate::{AreaLight, either::Either, material::PbrtMaterial, scene::UVMaterial};
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct Shape<R: Rng> { pub(crate) struct Shape<R: Rng> {
pub(crate) ctm: AffineTransform, pub(crate) ctm: AffineTransform,
pub(crate) material: Arc<dyn PbrtMaterial<R>>, pub(crate) material: Either<Arc<dyn PbrtMaterial<R>>, AreaLight>,
pub(crate) obj: ShapeType, pub(crate) obj: ShapeType,
pub(crate) alpha: ShapeAlpha, pub(crate) alpha: ShapeAlpha,
} }
@ -21,7 +21,7 @@ pub(crate) enum ShapeAlpha {
Texture(String), Texture(String),
} }
#[derive(Debug)] // #[derive(Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) enum ShapeType { pub(crate) enum ShapeType {
Sphere { Sphere {
@ -219,12 +219,14 @@ impl<R: Rng + std::fmt::Debug> Shape<R> {
t, t,
self.ctm self.ctm
.inverse_transform_normal((ray.at(t).as_dir()).normalize()), .inverse_transform_normal((ray.at(t).as_dir()).normalize()),
Some(UVMaterial { self.material.get_a().map(|m| UVMaterial {
u: phi, u: phi,
v: Float::acos(p.z() / radius), v: Float::acos(p.z() / radius),
material: self.material.as_ref(), material: m.as_ref(),
}), }),
None, self.material
.get_b()
.map(|l| l as &dyn ray_tracing_core::light::Light<R>),
0.0, 0.0,
)); ));
} }
@ -244,12 +246,14 @@ impl<R: Rng + std::fmt::Debug> Shape<R> {
return Some(Intersection::new( return Some(Intersection::new(
t, t,
Dir3::new(0.0, 0.0, 1.0), Dir3::new(0.0, 0.0, 1.0),
Some(UVMaterial { self.material.get_a().map(|m| UVMaterial {
u, u,
v, v,
material: self.material.as_ref(), material: m.as_ref(),
}), }),
None, self.material
.get_b()
.map(|l| l as &dyn ray_tracing_core::light::Light<R>),
0.0, 0.0,
)); ));
} }