Compare commits
2 commits
8e8a3845f8
...
1b8ca71b0e
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b8ca71b0e | |||
| 46a4edaee5 |
40 changed files with 511 additions and 379 deletions
|
|
@ -18,8 +18,6 @@ pub struct ConductorBxDF {
|
||||||
pub k: SampledSpectrum,
|
pub k: SampledSpectrum,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for ConductorBxDF {}
|
|
||||||
unsafe impl Sync for ConductorBxDF {}
|
|
||||||
|
|
||||||
impl ConductorBxDF {
|
impl ConductorBxDF {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ pub struct MeasuredBxDF {
|
||||||
pub lambda: SampledWavelengths,
|
pub lambda: SampledWavelengths,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MeasuredBxDF {}
|
|
||||||
unsafe impl Sync for MeasuredBxDF {}
|
|
||||||
|
|
||||||
impl MeasuredBxDF {
|
impl MeasuredBxDF {
|
||||||
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {
|
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -1117,8 +1117,6 @@ pub struct RGBToSpectrumTable {
|
||||||
pub n_nodes: u32,
|
pub n_nodes: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for RGBToSpectrumTable {}
|
|
||||||
unsafe impl Sync for RGBToSpectrumTable {}
|
|
||||||
|
|
||||||
impl RGBToSpectrumTable {
|
impl RGBToSpectrumTable {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
||||||
|
|
@ -436,8 +436,6 @@ pub struct SpectralFilm {
|
||||||
pub bucket_splats: GVec<AtomicFloat>,
|
pub bucket_splats: GVec<AtomicFloat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for SpectralFilm {}
|
|
||||||
unsafe impl Sync for SpectralFilm {}
|
|
||||||
|
|
||||||
impl SpectralFilm {
|
impl SpectralFilm {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
@ -618,8 +616,6 @@ pub enum Film {
|
||||||
Spectral(SpectralFilm),
|
Spectral(SpectralFilm),
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Film {}
|
|
||||||
unsafe impl Sync for Film {}
|
|
||||||
|
|
||||||
impl Film {
|
impl Film {
|
||||||
pub fn base(&self) -> &FilmBase {
|
pub fn base(&self) -> &FilmBase {
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,15 @@ impl Bounds2f {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bounds3f {
|
impl Bounds3f {
|
||||||
|
/// SAH bucket index for `p` along `dim`, in `[0, n_buckets)`. `self` is the
|
||||||
|
/// centroid bounds, so `offset` is in [0,1] and only `offset == 1` needs the
|
||||||
|
/// clamp -- same as pbrt's `if (b == nBuckets) b = nBuckets - 1`.
|
||||||
|
#[inline]
|
||||||
|
pub fn sah_bucket(&self, p: &Point3f, dim: usize, n_buckets: usize) -> usize {
|
||||||
|
let offset = self.offset(p)[dim];
|
||||||
|
((n_buckets as Float * offset) as usize).min(n_buckets - 1)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn intersect_p(
|
pub fn intersect_p(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::core::light::Light;
|
use crate::core::light::Light;
|
||||||
|
use crate::core::material::Material;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
|
@ -16,8 +17,18 @@ impl Default for LightIdx {
|
||||||
impl LightIdx {
|
impl LightIdx {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(self, lights: &[Light]) -> &Light {
|
pub fn get(self, lights: &[Light]) -> &Light {
|
||||||
|
debug_assert!(!self.is_none(), "LightIdx::get on NONE handle");
|
||||||
&lights[self.0 as usize]
|
&lights[self.0 as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_get(self, lights: &[Light]) -> Option<&Light> {
|
||||||
|
if self.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
lights.get(self.0 as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -27,6 +38,21 @@ pub struct MaterialIdx(pub u32);
|
||||||
impl MaterialIdx {
|
impl MaterialIdx {
|
||||||
pub const NONE: Self = MaterialIdx(u32::MAX);
|
pub const NONE: Self = MaterialIdx(u32::MAX);
|
||||||
pub fn is_none(self) -> bool { self.0 == u32::MAX }
|
pub fn is_none(self) -> bool { self.0 == u32::MAX }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get(self, materials: &[Material]) -> &Material {
|
||||||
|
debug_assert!(!self.is_none(), "MaterialIdx::get on NONE handle");
|
||||||
|
&materials[self.0 as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_get(self, materials: &[Material]) -> Option<&Material> {
|
||||||
|
if self.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
materials.get(self.0 as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MaterialIdx {
|
impl Default for MaterialIdx {
|
||||||
|
|
|
||||||
|
|
@ -122,12 +122,15 @@ impl Pixels {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float {
|
pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float {
|
||||||
|
// SAFETY: `texel_offset` is in range by this fn's own contract.
|
||||||
|
unsafe {
|
||||||
match self.format {
|
match self.format {
|
||||||
PixelFormat::U8 => encoding.to_linear_scalar(self.read_u8(texel_offset)),
|
PixelFormat::U8 => encoding.to_linear_scalar(self.read_u8(texel_offset)),
|
||||||
PixelFormat::F16 => f16_to_f32_software(self.read_f16(texel_offset)),
|
PixelFormat::F16 => f16_to_f32_software(self.read_f16(texel_offset)),
|
||||||
PixelFormat::F32 => self.read_f32(texel_offset),
|
PixelFormat::F32 => self.read_f32(texel_offset),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn write_u8(&mut self, texel_offset: usize, val: u8) {
|
pub unsafe fn write_u8(&mut self, texel_offset: usize, val: u8) {
|
||||||
unsafe { *self.data.as_mut_ptr().add(texel_offset) = val };
|
unsafe { *self.data.as_mut_ptr().add(texel_offset) = val };
|
||||||
|
|
@ -505,12 +508,3 @@ impl FilterFunction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct ImagePyramid {
|
|
||||||
pub levels: *const Ptr<Image>,
|
|
||||||
pub level_count: u32,
|
|
||||||
pub wrap_mode: WrapMode,
|
|
||||||
pub filter: FilterFunction,
|
|
||||||
pub max_aniso: f32,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -235,8 +235,6 @@ pub struct SurfaceInteraction {
|
||||||
pub dvdy: Float,
|
pub dvdy: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for SurfaceInteraction {}
|
|
||||||
unsafe impl Sync for SurfaceInteraction {}
|
|
||||||
|
|
||||||
impl SurfaceInteraction {
|
impl SurfaceInteraction {
|
||||||
pub fn le(
|
pub fn le(
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,9 @@ impl LightBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
// Default gives phi == 0, which `union` treats as empty -- that is what the SAH
|
||||||
|
// bucket accumulation starts from.
|
||||||
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
pub struct LightBounds {
|
pub struct LightBounds {
|
||||||
pub bounds: Bounds3f,
|
pub bounds: Bounds3f,
|
||||||
pub phi: Float,
|
pub phi: Float,
|
||||||
|
|
@ -209,7 +211,8 @@ impl LightBounds {
|
||||||
|
|
||||||
impl LightBounds {
|
impl LightBounds {
|
||||||
pub fn centroid(&self) -> Point3f {
|
pub fn centroid(&self) -> Point3f {
|
||||||
self.bounds.p_min + Vector3f::from(self.bounds.p_max) / 2.
|
// (pMin + pMax) / 2 -- Point has no scalar Div, so go via Vector.
|
||||||
|
Point3f::from((Vector3f::from(self.bounds.p_min) + Vector3f::from(self.bounds.p_max)) / 2.)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn importance(&self, p: Point3f, n: Normal3f) -> Float {
|
pub fn importance(&self, p: Point3f, n: Normal3f) -> Float {
|
||||||
|
|
@ -267,11 +270,12 @@ impl LightBounds {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn union(a: &Self, b: &Self) -> Self {
|
pub fn union(a: &Self, b: &Self) -> Self {
|
||||||
|
// If one LightBounds has zero power, return the *other* (lights.h:137).
|
||||||
if a.phi == 0. {
|
if a.phi == 0. {
|
||||||
return a.clone();
|
return *b;
|
||||||
}
|
}
|
||||||
if b.phi == 0. {
|
if b.phi == 0. {
|
||||||
return b.clone();
|
return *a;
|
||||||
}
|
}
|
||||||
|
|
||||||
let a_cone = DirectionCone::new(a.w, a.cos_theta_o);
|
let a_cone = DirectionCone::new(a.w, a.cos_theta_o);
|
||||||
|
|
@ -311,9 +315,13 @@ pub trait LightTrait {
|
||||||
uv: Point2f,
|
uv: Point2f,
|
||||||
w: Vector3f,
|
w: Vector3f,
|
||||||
lambda: &SampledWavelengths,
|
lambda: &SampledWavelengths,
|
||||||
) -> SampledSpectrum;
|
) -> SampledSpectrum {
|
||||||
|
self.base().l(p, n, uv, w, lambda)
|
||||||
|
}
|
||||||
|
|
||||||
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum;
|
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
self.base().le(ray, lambda)
|
||||||
|
}
|
||||||
|
|
||||||
fn light_type(&self) -> LightType {
|
fn light_type(&self) -> LightType {
|
||||||
self.base().light_type
|
self.base().light_type
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,6 @@ pub struct MajorantGrid {
|
||||||
pub n_voxels: u32,
|
pub n_voxels: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MajorantGrid {}
|
|
||||||
unsafe impl Sync for MajorantGrid {}
|
|
||||||
|
|
||||||
impl MajorantGrid {
|
impl MajorantGrid {
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
|
@ -715,8 +713,6 @@ pub struct MediumInterface {
|
||||||
pub outside: Ptr<Medium>,
|
pub outside: Ptr<Medium>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MediumInterface {}
|
|
||||||
unsafe impl Sync for MediumInterface {}
|
|
||||||
|
|
||||||
impl Default for MediumInterface {
|
impl Default for MediumInterface {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ pub struct GeometricPrimitive {
|
||||||
pub alpha: Ptr<FloatTexture>,
|
pub alpha: Ptr<FloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for GeometricPrimitive {}
|
|
||||||
unsafe impl Sync for GeometricPrimitive {}
|
|
||||||
|
|
||||||
impl PrimitiveTrait for GeometricPrimitive {
|
impl PrimitiveTrait for GeometricPrimitive {
|
||||||
fn bounds(&self) -> Bounds3f {
|
fn bounds(&self) -> Bounds3f {
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ pub struct StandardSpectra {
|
||||||
pub d65: Ptr<DenselySampledSpectrum>,
|
pub d65: Ptr<DenselySampledSpectrum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for StandardSpectra {}
|
|
||||||
unsafe impl Sync for StandardSpectra {}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[enum_dispatch(SpectrumTrait)]
|
#[enum_dispatch(SpectrumTrait)]
|
||||||
|
|
|
||||||
|
|
@ -33,17 +33,7 @@ pub struct DiffuseAreaLight {
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DiffuseAreaLight {}
|
|
||||||
unsafe impl Sync for DiffuseAreaLight {}
|
|
||||||
|
|
||||||
impl DiffuseAreaLight {
|
impl DiffuseAreaLight {
|
||||||
// fn l_base(&self, n: Normal3f, wo: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
// if !self.two_sided && n.dot(wo.into()) <= 0.0 {
|
|
||||||
// return SampledSpectrum::new(0.0);
|
|
||||||
// }
|
|
||||||
// self.lemit.sample(lambda) * self.scale
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn alpha_masked(&self, intr: &Interaction) -> bool {
|
fn alpha_masked(&self, intr: &Interaction) -> bool {
|
||||||
if self.alpha.is_null() {
|
if self.alpha.is_null() {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -129,7 +119,7 @@ impl LightTrait for DiffuseAreaLight {
|
||||||
let mut rgb = RGB::default();
|
let mut rgb = RGB::default();
|
||||||
uv[1] = 1. - uv[1];
|
uv[1] = 1. - uv[1];
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
rgb[c] = self.image.bilerp_channel(uv, c as i32);
|
rgb[c] = self.image.bilerp_channel(uv, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
let spec = RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero());
|
let spec = RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero());
|
||||||
|
|
@ -140,10 +130,6 @@ impl LightTrait for DiffuseAreaLight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
let mut l = SampledSpectrum::new(0.);
|
let mut l = SampledSpectrum::new(0.);
|
||||||
|
|
@ -152,7 +138,7 @@ impl LightTrait for DiffuseAreaLight {
|
||||||
for x in 0..self.image.resolution().x() {
|
for x in 0..self.image.resolution().x() {
|
||||||
let mut rgb = RGB::default();
|
let mut rgb = RGB::default();
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32);
|
rgb[c] = self.image.get_channel(Point2i::new(x, y), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero())
|
l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero())
|
||||||
|
|
@ -168,9 +154,7 @@ impl LightTrait for DiffuseAreaLight {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::core::light::{LightBase, LightBounds, LightLiSample, LightSampleConte
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::{Float, Ptr, PI};
|
use crate::{Float, PI, Ptr};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -75,21 +75,6 @@ impl LightTrait for DistantLight {
|
||||||
0.
|
0.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
||||||
let (center, radius) = scene_bounds.bounding_sphere();
|
let (center, radius) = scene_bounds.bounding_sphere();
|
||||||
self.scene_center = center;
|
self.scene_center = center;
|
||||||
|
|
|
||||||
|
|
@ -51,28 +51,29 @@ impl LightTrait for GoniometricLight {
|
||||||
0.
|
0.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {}
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
todo!()
|
let mut sum_y = 0.;
|
||||||
|
for x in 0..self.image.resolution().x() {
|
||||||
|
for y in 0..self.image.resolution().y() {
|
||||||
|
sum_y += self.image.get_channel(Point2i::new(x, y), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let phi = self.scale * self.iemit.max_value() * 4. * PI * sum_y
|
||||||
|
/ (self.image.resolution().x() * self.image.resolution().y()) as f32;
|
||||||
|
let p = self
|
||||||
|
.base()
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_point(Point3f::new(0., 0., 0.));
|
||||||
|
Some(LightBounds::new(
|
||||||
|
&Bounds3f::from_points(p, p),
|
||||||
|
Vector3f::new(0., 0., 1.),
|
||||||
|
phi,
|
||||||
|
PI.cos(),
|
||||||
|
(PI / 2.).cos(),
|
||||||
|
false,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths
|
||||||
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
|
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
|
||||||
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
|
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
|
||||||
use crate::utils::sampling::{
|
use crate::utils::sampling::{
|
||||||
sample_uniform_sphere, uniform_sphere_pdf, AliasTable, PiecewiseConstant2D,
|
AliasTable, PiecewiseConstant2D, WindowedPiecewiseConstant2D, sample_uniform_sphere,
|
||||||
WindowedPiecewiseConstant2D,
|
uniform_sphere_pdf,
|
||||||
};
|
};
|
||||||
use crate::utils::{Ptr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
|
@ -32,9 +32,6 @@ pub struct UniformInfiniteLight {
|
||||||
pub scene_radius: Float,
|
pub scene_radius: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for UniformInfiniteLight {}
|
|
||||||
unsafe impl Sync for UniformInfiniteLight {}
|
|
||||||
|
|
||||||
impl UniformInfiniteLight {
|
impl UniformInfiniteLight {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -100,29 +97,16 @@ impl LightTrait for UniformInfiniteLight {
|
||||||
uniform_sphere_pdf()
|
uniform_sphere_pdf()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, _ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn le(&self, _ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
self.scale * self.lemit.sample(lambda)
|
self.scale * self.lemit.sample(lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
(self.scene_center, self.scene_radius) = scene_bounds.bounding_sphere();
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
todo!()
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
|
@ -144,9 +128,6 @@ pub struct ImageInfiniteLight {
|
||||||
pub scene_center: Point3f,
|
pub scene_center: Point3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for ImageInfiniteLight {}
|
|
||||||
unsafe impl Sync for ImageInfiniteLight {}
|
|
||||||
|
|
||||||
impl ImageInfiniteLight {
|
impl ImageInfiniteLight {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -236,17 +217,6 @@ impl LightTrait for ImageInfiniteLight {
|
||||||
pdf / (4. * PI)
|
pdf / (4. * PI)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
let w_light = self
|
let w_light = self
|
||||||
.base
|
.base
|
||||||
|
|
@ -279,14 +249,10 @@ impl LightTrait for ImageInfiniteLight {
|
||||||
4. * PI * PI * square(self.scene_radius) * self.scale * sum_l / (width * height) as Float
|
4. * PI * PI * square(self.scene_radius) * self.scale * sum_l / (width * height) as Float
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
||||||
let (scene_center, scene_radius) = scene_bounds.bounding_sphere();
|
(self.scene_center, self.scene_radius) = scene_bounds.bounding_sphere();
|
||||||
self.scene_center = scene_center;
|
|
||||||
self.scene_radius = scene_radius;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -428,17 +394,6 @@ impl LightTrait for PortalInfiniteLight {
|
||||||
pdf / duv_dw
|
pdf / duv_dw
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
let uv = self.image_from_render(ray.d.normalize());
|
let uv = self.image_from_render(ray.d.normalize());
|
||||||
let b = self.image_bounds(ray.o);
|
let b = self.image_bounds(ray.o);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::core::light::{
|
||||||
};
|
};
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::{Float, PI, Ptr, Transform};
|
use crate::{Float, INV_2_PI, PI, Ptr, Transform};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -51,30 +51,12 @@ impl LightTrait for PointLight {
|
||||||
0.
|
0.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
4. * PI * self.scale * self.i.sample(&lambda)
|
4. * PI * self.scale * self.i.sample(&lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {}
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
|
@ -88,7 +70,7 @@ impl LightTrait for PointLight {
|
||||||
Vector3f::new(0., 0., 1.),
|
Vector3f::new(0., 0., 1.),
|
||||||
phi,
|
phi,
|
||||||
PI.cos(),
|
PI.cos(),
|
||||||
(PI / 2.).cos(),
|
INV_2_PI.cos(),
|
||||||
false,
|
false,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,21 +75,6 @@ impl LightTrait for ProjectionLight {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
let mut sum = SampledSpectrum::new(0.);
|
let mut sum = SampledSpectrum::new(0.);
|
||||||
let res = self.image.resolution();
|
let res = self.image.resolution();
|
||||||
|
|
@ -118,11 +103,48 @@ impl LightTrait for ProjectionLight {
|
||||||
self.scale * self.a * sum / (res.x() * res.y()) as Float
|
self.scale * self.a * sum / (res.x() * res.y()) as Float
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {}
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
todo!()
|
let mut sum = 0.;
|
||||||
|
for v in 0..self.image.resolution().y() {
|
||||||
|
for u in 0..self.image.resolution().x() {
|
||||||
|
let uv = Point2i::new(u, v);
|
||||||
|
sum += self.image.get_channel(uv, 0).max(
|
||||||
|
self.image
|
||||||
|
.get_channel(uv, 1)
|
||||||
|
.max(self.image.get_channel(uv, 2)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let phi =
|
||||||
|
self.scale * sum / (self.image.resolution().x() * self.image.resolution().y()) as f32;
|
||||||
|
|
||||||
|
let p_corner = Point3f::new(
|
||||||
|
self.screen_bounds.p_max.x(),
|
||||||
|
self.screen_bounds.p_max.y(),
|
||||||
|
0.,
|
||||||
|
);
|
||||||
|
|
||||||
|
let w_corner = Vector3f::from(self.light_from_screen.apply_to_point(p_corner)).normalize();
|
||||||
|
let cos_total_width = cos_theta(w_corner);
|
||||||
|
|
||||||
|
let p = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_point(Point3f::new(0., 0., 0.));
|
||||||
|
let w = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_vector(Vector3f::new(0., 0., 1.));
|
||||||
|
Some(LightBounds::new(
|
||||||
|
&Bounds3f::from_points(p, p),
|
||||||
|
w,
|
||||||
|
phi,
|
||||||
|
1.,
|
||||||
|
cos_total_width,
|
||||||
|
false,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
use crate::core::LightIdx;
|
||||||
use crate::core::geometry::primitives::OctahedralVector;
|
use crate::core::geometry::primitives::OctahedralVector;
|
||||||
use crate::core::geometry::{Bounds3f, DirectionCone, Normal3f, Point3f, Vector3f, VectorLike};
|
use crate::core::geometry::{Bounds3f, DirectionCone, Normal3f, Point3f, Vector3f, VectorLike};
|
||||||
use crate::core::light::{Light, LightBounds, LightSampleContext};
|
use crate::core::light::{Light, LightBounds, LightSampleContext};
|
||||||
use crate::core::LightIdx;
|
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::{clamp, lerp, sample_discrete};
|
use crate::utils::math::{clamp, lerp, sample_discrete};
|
||||||
use crate::utils::math::{safe_sqrt, square};
|
use crate::utils::math::{safe_sqrt, square};
|
||||||
|
|
@ -164,27 +164,25 @@ impl CompactLightBounds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct SampledLight {
|
pub struct SampledLight {
|
||||||
pub light: LightIdx,
|
pub light: LightIdx,
|
||||||
pub p: Float,
|
pub p: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl SampledLight {
|
|
||||||
// pub fn new(light: Light, p: Float) -> Self {
|
|
||||||
// Self {
|
|
||||||
// light: Ptr::from(&light),
|
|
||||||
// p,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait LightSamplerTrait {
|
pub trait LightSamplerTrait {
|
||||||
fn sample_with_context(&self, ctx: &LightSampleContext, u: Float) -> Option<SampledLight>;
|
|
||||||
fn pmf_with_context(&self, ctx: &LightSampleContext, idx: LightIdx) -> Float;
|
|
||||||
fn sample(&self, u: Float) -> Option<SampledLight>;
|
fn sample(&self, u: Float) -> Option<SampledLight>;
|
||||||
fn pmf(&self, idx: LightIdx) -> Float;
|
fn pmf(&self, idx: LightIdx) -> Float;
|
||||||
|
|
||||||
|
/// Samplers that ignore the shading context inherit these.
|
||||||
|
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
|
||||||
|
self.sample(u)
|
||||||
|
}
|
||||||
|
fn pmf_with_context(&self, _ctx: &LightSampleContext, idx: LightIdx) -> Float {
|
||||||
|
self.pmf(idx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -195,7 +193,8 @@ pub enum LightSampler {
|
||||||
BVH(BVHLightSampler),
|
BVH(BVHLightSampler),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct UniformLightSampler {
|
pub struct UniformLightSampler {
|
||||||
lights_len: u32,
|
lights_len: u32,
|
||||||
}
|
}
|
||||||
|
|
@ -207,10 +206,6 @@ impl UniformLightSampler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightSamplerTrait for UniformLightSampler {
|
impl LightSamplerTrait for UniformLightSampler {
|
||||||
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
|
|
||||||
self.sample(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample(&self, u: Float) -> Option<SampledLight> {
|
fn sample(&self, u: Float) -> Option<SampledLight> {
|
||||||
if self.lights_len == 0 {
|
if self.lights_len == 0 {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -222,10 +217,6 @@ impl LightSamplerTrait for UniformLightSampler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pmf_with_context(&self, _ctx: &LightSampleContext, _idx: LightIdx) -> Float {
|
|
||||||
self.pmf(_idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pmf(&self, _idx: LightIdx) -> Float {
|
fn pmf(&self, _idx: LightIdx) -> Float {
|
||||||
if self.lights_len == 0 {
|
if self.lights_len == 0 {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
@ -237,22 +228,10 @@ impl LightSamplerTrait for UniformLightSampler {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
pub struct PowerLightSampler {
|
pub struct PowerLightSampler {
|
||||||
pub lights_len: u32,
|
|
||||||
pub alias_table: Ptr<AliasTable>,
|
pub alias_table: Ptr<AliasTable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for PowerLightSampler {}
|
|
||||||
unsafe impl Sync for PowerLightSampler {}
|
|
||||||
|
|
||||||
impl LightSamplerTrait for PowerLightSampler {
|
impl LightSamplerTrait for PowerLightSampler {
|
||||||
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
|
|
||||||
self.sample(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pmf_with_context(&self, _ctx: &LightSampleContext, idx: LightIdx) -> Float {
|
|
||||||
self.pmf(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample(&self, u: Float) -> Option<SampledLight> {
|
fn sample(&self, u: Float) -> Option<SampledLight> {
|
||||||
if self.alias_table.size() == 0 {
|
if self.alias_table.size() == 0 {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -333,17 +312,20 @@ impl LightBVHNode {
|
||||||
pub fn child_or_light_index(&self) -> u32 {
|
pub fn child_or_light_index(&self) -> u32 {
|
||||||
self.packed_data & Self::INDEX_MASK
|
self.packed_data & Self::INDEX_MASK
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, _ctx: &LightSampleContext, _u: Float) -> Option<SampledLight> {
|
|
||||||
todo!("Implement LightBVHNode::Sample logic")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Canary value stored in `bit_trails` for a light that is not a BVH leaf, i.e. an
|
||||||
|
/// infinite light or one with negative `phi`. Stands in for pbrt's
|
||||||
|
/// `lightToBitTrail.HasKey(light)`.
|
||||||
|
pub const NO_BIT_TRAIL: u64 = u64::MAX;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
pub struct BVHLightSampler {
|
pub struct BVHLightSampler {
|
||||||
pub nodes: Ptr<LightBVHNode>,
|
pub nodes: Ptr<LightBVHNode>,
|
||||||
pub lights: Ptr<Light>,
|
/// Handles of the infinite lights, in scene order.
|
||||||
pub infinite_lights: Ptr<Light>,
|
pub infinite_lights: Ptr<LightIdx>,
|
||||||
|
/// Indexed by *global* light index, matching the leaf indices stored in
|
||||||
|
/// `nodes`; `NO_BIT_TRAIL` where the light has no leaf.
|
||||||
pub bit_trails: Ptr<u64>,
|
pub bit_trails: Ptr<u64>,
|
||||||
pub nodes_len: u32,
|
pub nodes_len: u32,
|
||||||
pub lights_len: u32,
|
pub lights_len: u32,
|
||||||
|
|
@ -351,42 +333,46 @@ pub struct BVHLightSampler {
|
||||||
pub all_light_bounds: Bounds3f,
|
pub all_light_bounds: Bounds3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for BVHLightSampler {}
|
|
||||||
unsafe impl Sync for BVHLightSampler {}
|
|
||||||
|
|
||||||
impl BVHLightSampler {
|
impl BVHLightSampler {
|
||||||
|
// Each array is paired with the length stored alongside it, so the slice can
|
||||||
|
// only be formed one way and indexing past the end is a bounds check rather
|
||||||
|
// than a silent read. These three are the only `unsafe` in the sampler.
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn nodes(&self) -> &[LightBVHNode] {
|
||||||
|
unsafe { self.nodes.as_slice(self.nodes_len as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn infinite_lights(&self) -> &[LightIdx] {
|
||||||
|
unsafe {
|
||||||
|
self.infinite_lights
|
||||||
|
.as_slice(self.infinite_lights_len as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One bit trail per light, indexed by global light index.
|
||||||
|
#[inline(always)]
|
||||||
|
fn bit_trails(&self) -> &[u64] {
|
||||||
|
unsafe { self.bit_trails.as_slice(self.lights_len as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn node(&self, idx: usize) -> &LightBVHNode {
|
fn node(&self, idx: usize) -> &LightBVHNode {
|
||||||
unsafe { self.nodes.at(idx) }
|
&self.nodes()[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn light(&self, idx: usize) -> Light {
|
fn infinite_light(&self, idx: usize) -> LightIdx {
|
||||||
unsafe { *self.lights.at(idx) }
|
self.infinite_lights()[idx]
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn infinite_light(&self, idx: usize) -> Light {
|
|
||||||
unsafe { *self.infinite_lights.at(idx) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn bit_trail(&self, idx: usize) -> u64 {
|
fn bit_trail(&self, idx: usize) -> u64 {
|
||||||
unsafe { *self.bit_trails.at(idx) }
|
self.bit_trails()[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
pub fn evaluate_cost(b: &LightBounds, bounds: &Bounds3f, dim: usize) -> Float {
|
||||||
fn light_index_in(&self, base: Ptr<Light>, len: u32, light: &Light) -> Option<usize> {
|
|
||||||
let target = light as *const Light;
|
|
||||||
for i in 0..len as usize {
|
|
||||||
if unsafe { base.add(i) }.as_raw() == target {
|
|
||||||
return Some(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate_cost(&self, b: &LightBounds, bounds: &Bounds3f, dim: usize) -> Float {
|
|
||||||
let theta_o = b.cos_theta_o.acos();
|
let theta_o = b.cos_theta_o.acos();
|
||||||
let theta_e = b.cos_theta_e.acos();
|
let theta_e = b.cos_theta_e.acos();
|
||||||
let theta_w = (theta_o + theta_e).min(PI);
|
let theta_w = (theta_o + theta_e).min(PI);
|
||||||
|
|
@ -409,12 +395,14 @@ impl LightSamplerTrait for BVHLightSampler {
|
||||||
|
|
||||||
if u < p_inf {
|
if u < p_inf {
|
||||||
u /= p_inf;
|
u /= p_inf;
|
||||||
// sample uniformly from infinite lights; their global index equals their
|
// Uniformly sample an infinite light and return its handle
|
||||||
// position in the infinite_lights array (infinite lights are at 0..n_inf
|
// (`lightsamplers.h:277`: `infiniteLights[index]`).
|
||||||
// in the scene lights array by construction)
|
let ind = ((u * inf_size) as usize).min(self.infinite_lights_len as usize - 1);
|
||||||
let ind = (u * inf_size).min(inf_size - 1.) as u32;
|
|
||||||
let pmf = p_inf / inf_size;
|
let pmf = p_inf / inf_size;
|
||||||
return Some(SampledLight { light: LightIdx(ind), p: pmf });
|
return Some(SampledLight {
|
||||||
|
light: self.infinite_light(ind),
|
||||||
|
p: pmf,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.nodes_len == 0 {
|
if self.nodes_len == 0 {
|
||||||
|
|
@ -464,18 +452,18 @@ impl LightSamplerTrait for BVHLightSampler {
|
||||||
let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. };
|
let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. };
|
||||||
let n_infinite = self.infinite_lights_len as Float;
|
let n_infinite = self.infinite_lights_len as Float;
|
||||||
|
|
||||||
// Infinite lights occupy indices 0..infinite_lights_len in the global array
|
|
||||||
if idx.0 < self.infinite_lights_len {
|
|
||||||
return 1.0 / (n_infinite + empty_nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
let light_index = idx.0 as usize;
|
let light_index = idx.0 as usize;
|
||||||
if light_index >= self.lights_len as usize {
|
if light_index >= self.lights_len as usize {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bit_trail[light_index] encodes the path from root to the leaf for this light
|
// bit_trail[light_index] encodes the path from root to this light's leaf.
|
||||||
|
// Canary value to check if no leaf. No leaf, it his infinite, or its power
|
||||||
|
// was zero
|
||||||
let mut bit_trail = self.bit_trail(light_index);
|
let mut bit_trail = self.bit_trail(light_index);
|
||||||
|
if bit_trail == NO_BIT_TRAIL {
|
||||||
|
return 1.0 / (n_infinite + empty_nodes);
|
||||||
|
}
|
||||||
let p_inf = n_infinite / (n_infinite + empty_nodes);
|
let p_inf = n_infinite / (n_infinite + empty_nodes);
|
||||||
let mut pmf = 1.0 - p_inf;
|
let mut pmf = 1.0 - p_inf;
|
||||||
let mut node_ind = 0;
|
let mut node_ind = 0;
|
||||||
|
|
|
||||||
|
|
@ -65,21 +65,6 @@ impl LightTrait for SpotLight {
|
||||||
0.
|
0.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
self.scale
|
self.scale
|
||||||
|
|
@ -89,12 +74,8 @@ impl LightTrait for SpotLight {
|
||||||
* ((1. - self.cos_falloff_start) + (self.cos_falloff_start - self.cos_falloff_end) / 2.)
|
* ((1. - self.cos_falloff_start) + (self.cos_falloff_start - self.cos_falloff_end) / 2.)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {}
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
let p = self
|
let p = self
|
||||||
.base
|
.base
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,6 @@ pub struct BilinearPatchMesh {
|
||||||
pub image_distribution: Ptr<PiecewiseConstant2D>,
|
pub image_distribution: Ptr<PiecewiseConstant2D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for TriangleMesh {}
|
|
||||||
unsafe impl Sync for TriangleMesh {}
|
|
||||||
unsafe impl Send for BilinearPatchMesh {}
|
|
||||||
unsafe impl Sync for BilinearPatchMesh {}
|
|
||||||
|
|
||||||
impl TriangleMesh {
|
impl TriangleMesh {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,6 @@ pub struct RGBColorSpace {
|
||||||
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for RGBColorSpace {}
|
|
||||||
unsafe impl Sync for RGBColorSpace {}
|
|
||||||
|
|
||||||
impl RGBColorSpace {
|
impl RGBColorSpace {
|
||||||
pub fn to_xyz(&self, rgb: RGB) -> XYZ {
|
pub fn to_xyz(&self, rgb: RGB) -> XYZ {
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,6 @@ pub struct DenselySampledSpectrum {
|
||||||
pub values: GVec<Float>,
|
pub values: GVec<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DenselySampledSpectrum {}
|
|
||||||
unsafe impl Sync for DenselySampledSpectrum {}
|
|
||||||
|
|
||||||
impl DenselySampledSpectrum {
|
impl DenselySampledSpectrum {
|
||||||
pub fn new(lambda_min: i32, lambda_max: i32, values: GVec<Float>) -> Self {
|
pub fn new(lambda_min: i32, lambda_max: i32, values: GVec<Float>) -> Self {
|
||||||
|
|
@ -262,8 +260,6 @@ impl PiecewiseLinearSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for PiecewiseLinearSpectrum {}
|
|
||||||
unsafe impl Sync for PiecewiseLinearSpectrum {}
|
|
||||||
|
|
||||||
impl SpectrumTrait for PiecewiseLinearSpectrum {
|
impl SpectrumTrait for PiecewiseLinearSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ pub struct MarbleTexture {
|
||||||
pub colorspace: Ptr<RGBColorSpace>,
|
pub colorspace: Ptr<RGBColorSpace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MarbleTexture {}
|
|
||||||
unsafe impl Sync for MarbleTexture {}
|
|
||||||
|
|
||||||
impl MarbleTexture {
|
impl MarbleTexture {
|
||||||
pub fn evaluate(
|
pub fn evaluate(
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ unsafe impl Allocator for SystemAlloc {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||||
Global.deallocate(ptr, layout)
|
// SAFETY: forwarded verbatim; caller upholds Allocator's contract.
|
||||||
|
unsafe { Global.deallocate(ptr, layout) }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn grow(
|
unsafe fn grow(
|
||||||
|
|
@ -26,7 +27,8 @@ unsafe impl Allocator for SystemAlloc {
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocError> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
Global.grow(ptr, old_layout, new_layout)
|
// SAFETY: forwarded verbatim; caller upholds Allocator's contract.
|
||||||
|
unsafe { Global.grow(ptr, old_layout, new_layout) }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn shrink(
|
unsafe fn shrink(
|
||||||
|
|
@ -35,7 +37,8 @@ unsafe impl Allocator for SystemAlloc {
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocError> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
Global.shrink(ptr, old_layout, new_layout)
|
// SAFETY: forwarded verbatim; caller upholds Allocator's contract.
|
||||||
|
unsafe { Global.shrink(ptr, old_layout, new_layout) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -712,8 +712,6 @@ pub struct PiecewiseConstant1D {
|
||||||
pub func_integral: Float,
|
pub func_integral: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for PiecewiseConstant1D {}
|
|
||||||
unsafe impl Sync for PiecewiseConstant1D {}
|
|
||||||
|
|
||||||
impl PiecewiseConstant1D {
|
impl PiecewiseConstant1D {
|
||||||
pub fn new(f: &[Float]) -> Self {
|
pub fn new(f: &[Float]) -> Self {
|
||||||
|
|
@ -1101,8 +1099,6 @@ pub struct AliasTable {
|
||||||
pub bins: GVec<Bin>,
|
pub bins: GVec<Bin>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for AliasTable {}
|
|
||||||
unsafe impl Sync for AliasTable {}
|
|
||||||
|
|
||||||
impl AliasTable {
|
impl AliasTable {
|
||||||
pub fn new(weights: &[Float]) -> Self {
|
pub fn new(weights: &[Float]) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ impl<S: SoA> WorkQueue<S> {
|
||||||
i,
|
i,
|
||||||
self.size()
|
self.size()
|
||||||
);
|
);
|
||||||
self.storage.get(i)
|
// SAFETY: bounds checked above; caller guarantees the slot is initialised.
|
||||||
|
unsafe { self.storage.get(i) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -375,12 +375,7 @@ impl CreateBVH for BVHAggregate {
|
||||||
}
|
}
|
||||||
let mut buckets = [Bucket::default(); N_BUCKETS];
|
let mut buckets = [Bucket::default(); N_BUCKETS];
|
||||||
let get_bucket_idx = |node: &BVHBuildNode| -> usize {
|
let get_bucket_idx = |node: &BVHBuildNode| -> usize {
|
||||||
let offset = centroid_bounds.offset(&node.bounds().centroid())[dim];
|
centroid_bounds.sah_bucket(&node.bounds().centroid(), dim, N_BUCKETS)
|
||||||
let mut b = (N_BUCKETS as Float * offset) as usize;
|
|
||||||
if b == N_BUCKETS {
|
|
||||||
b = N_BUCKETS - 1;
|
|
||||||
}
|
|
||||||
b
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize _Bucket_ for HLBVH SAH partition buckets
|
// Initialize _Bucket_ for HLBVH SAH partition buckets
|
||||||
|
|
@ -535,11 +530,7 @@ fn build_recursive(
|
||||||
const N_BUCKETS: usize = 12;
|
const N_BUCKETS: usize = 12;
|
||||||
let mut buckets = [BVHSplitBucket::default(); N_BUCKETS];
|
let mut buckets = [BVHSplitBucket::default(); N_BUCKETS];
|
||||||
for prim in bvh_primitives.iter() {
|
for prim in bvh_primitives.iter() {
|
||||||
let mut b =
|
let b = centroid_bounds.sah_bucket(&prim.centroid, dim, N_BUCKETS);
|
||||||
(N_BUCKETS as Float * centroid_bounds.offset(&prim.centroid)[dim]) as usize;
|
|
||||||
if b == N_BUCKETS {
|
|
||||||
b = N_BUCKETS - 1;
|
|
||||||
}
|
|
||||||
buckets[b].count += 1;
|
buckets[b].count += 1;
|
||||||
buckets[b].bounds = buckets[b].bounds.union(prim.bounds);
|
buckets[b].bounds = buckets[b].bounds.union(prim.bounds);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ impl InteractionGetter for SurfaceInteraction {
|
||||||
if self.material.is_none() {
|
if self.material.is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut active_mat: &Material = &materials[self.material.0 as usize];
|
let mut active_mat: &Material = self.material.get(materials);
|
||||||
let material = {
|
let material = {
|
||||||
let tex_eval = UniversalTextureEvaluator;
|
let tex_eval = UniversalTextureEvaluator;
|
||||||
while let Material::Mix(mix) = active_mat {
|
while let Material::Mix(mix) = active_mat {
|
||||||
|
|
@ -82,7 +82,7 @@ impl InteractionGetter for SurfaceInteraction {
|
||||||
if self.material.is_none() {
|
if self.material.is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut active_mat: &Material = &materials[self.material.0 as usize];
|
let mut active_mat: &Material = self.material.get(materials);
|
||||||
let tex_eval = UniversalTextureEvaluator;
|
let tex_eval = UniversalTextureEvaluator;
|
||||||
while let Material::Mix(mix) = active_mat {
|
while let Material::Mix(mix) = active_mat {
|
||||||
let ctx = MaterialEvalContext::from(self);
|
let ctx = MaterialEvalContext::from(self);
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,8 @@ pub trait CreateMedium {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CreateMedium for Medium {}
|
||||||
|
|
||||||
fn create_homogeneous(
|
fn create_homogeneous(
|
||||||
parameters: &ParameterDictionary,
|
parameters: &ParameterDictionary,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::wavefront::integrator::CpuWavefrontRenderer;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
|
|
||||||
pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
||||||
let media = scene.create_media();
|
let media = scene.create_media(arena);
|
||||||
let textures = scene.create_textures(arena);
|
let textures = scene.create_textures(arena);
|
||||||
let (named_materials, materials, _default_mtl) = scene.create_materials(&textures, arena)?;
|
let (named_materials, materials, _default_mtl) = scene.create_materials(&textures, arena)?;
|
||||||
let (lights, al_map) = scene.create_lights(&textures, &media, arena);
|
let (lights, al_map) = scene.create_lights(&textures, &media, arena);
|
||||||
|
|
|
||||||
|
|
@ -152,11 +152,20 @@ impl BasicSceneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(scene: Arc<BasicScene>) -> Self {
|
pub fn new(scene: Arc<BasicScene>) -> Self {
|
||||||
|
// pbrt seeds material index 0 with a default "diffuse" so that a shape
|
||||||
|
// declared before any Material statement still has one (scene.cpp:102).
|
||||||
|
let default_material_index = scene.add_material(SceneEntity {
|
||||||
|
name: "diffuse".into(),
|
||||||
|
loc: FileLoc::default(),
|
||||||
|
parameters: ParameterDictionary::new(Vec::new(), None).unwrap(),
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scene,
|
scene,
|
||||||
current_block: BlockState::OptionsBlock,
|
current_block: BlockState::OptionsBlock,
|
||||||
graphics_state: GraphicsState {
|
graphics_state: GraphicsState {
|
||||||
active_transform_bits: Self::ALL_TRANSFORM_BITS,
|
active_transform_bits: Self::ALL_TRANSFORM_BITS,
|
||||||
|
current_material_index: Some(default_material_index),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
pushed_graphics_states: Vec::new(),
|
pushed_graphics_states: Vec::new(),
|
||||||
|
|
@ -512,23 +521,31 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params: ParsedParameterVector,
|
params: ParsedParameterVector,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
) -> Result<(), ParserError> {
|
) -> Result<(), ParserError> {
|
||||||
self.verify_world("MakeNamedMaterial", &loc)?;
|
|
||||||
let curr_name = normalize_utf8(name);
|
let curr_name = normalize_utf8(name);
|
||||||
|
|
||||||
if !self.named_material_names.insert(curr_name.to_string()) {
|
if !self.medium_names.insert(curr_name.clone()) {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
format!("Named material '{}' redefined.", name),
|
format!("Named medium '{}' redefined.", name),
|
||||||
loc,
|
loc,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let parameters = self.make_params(params, &loc)?;
|
|
||||||
let entity = SceneEntity {
|
let parameters = ParameterDictionary::from_array(
|
||||||
|
params,
|
||||||
|
&self.graphics_state.medium_attributes,
|
||||||
|
self.graphics_state.color_space.clone(),
|
||||||
|
)?;
|
||||||
|
let render_from_object = self.render_from_object();
|
||||||
|
let entity = MediumSceneEntity {
|
||||||
|
base: SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters,
|
parameters,
|
||||||
|
},
|
||||||
|
render_from_object,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.scene.add_named_material(&curr_name, entity);
|
self.scene.add_medium(&curr_name, entity);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -741,10 +758,15 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
) -> Result<(), ParserError> {
|
) -> Result<(), ParserError> {
|
||||||
self.verify_world("material", &loc)?;
|
self.verify_world("material", &loc)?;
|
||||||
|
let dict = ParameterDictionary::from_array(
|
||||||
|
params,
|
||||||
|
&self.graphics_state.material_attributes,
|
||||||
|
self.graphics_state.color_space.clone(),
|
||||||
|
)?;
|
||||||
let entity = SceneEntity {
|
let entity = SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters: ParameterDictionary::new(params.clone(), None).unwrap(),
|
parameters: dict,
|
||||||
};
|
};
|
||||||
let idx = self.scene.add_material(entity);
|
let idx = self.scene.add_material(entity);
|
||||||
self.graphics_state.current_material_index = Some(idx);
|
self.graphics_state.current_material_index = Some(idx);
|
||||||
|
|
@ -754,15 +776,44 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
|
|
||||||
fn make_named_material(
|
fn make_named_material(
|
||||||
&mut self,
|
&mut self,
|
||||||
_name: &str,
|
name: &str,
|
||||||
_params: ParsedParameterVector,
|
params: ParsedParameterVector,
|
||||||
_loc: FileLoc,
|
loc: FileLoc,
|
||||||
) -> Result<(), ParserError> {
|
) -> Result<(), ParserError> {
|
||||||
todo!()
|
self.verify_world("MakeNamedMaterial", &loc)?;
|
||||||
|
let curr_name = normalize_utf8(name);
|
||||||
|
|
||||||
|
if !self.named_material_names.insert(curr_name.clone()) {
|
||||||
|
return Err(ParserError::Generic(
|
||||||
|
format!("Named material '{}' redefined.", name),
|
||||||
|
loc,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn named_material(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
let parameters = ParameterDictionary::from_array(
|
||||||
todo!()
|
params,
|
||||||
|
&self.graphics_state.material_attributes,
|
||||||
|
self.graphics_state.color_space.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// pbrt stores an empty entity name here: the material type comes from the
|
||||||
|
// "type" parameter (scene.cpp:719).
|
||||||
|
self.scene.add_named_material(
|
||||||
|
&curr_name,
|
||||||
|
SceneEntity {
|
||||||
|
name: String::new(),
|
||||||
|
loc,
|
||||||
|
parameters,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn named_material(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
|
self.verify_world("NamedMaterial", &loc)?;
|
||||||
|
self.graphics_state.current_material_name = normalize_utf8(name);
|
||||||
|
self.graphics_state.current_material_index = None;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn light_source(
|
fn light_source(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use crate::core::film::FilmFactory;
|
||||||
use crate::core::filter::FilterFactory;
|
use crate::core::filter::FilterFactory;
|
||||||
use crate::core::image::{HostImage, ImageIO};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use crate::core::material::MaterialFactory;
|
use crate::core::material::MaterialFactory;
|
||||||
|
use crate::core::medium::CreateMedium;
|
||||||
use crate::core::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive};
|
use crate::core::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive};
|
||||||
use crate::core::sampler::SamplerFactory;
|
use crate::core::sampler::SamplerFactory;
|
||||||
use crate::core::shape::{ShapeFactory, ShapeWithContext};
|
use crate::core::shape::{ShapeFactory, ShapeWithContext};
|
||||||
|
|
@ -87,21 +88,21 @@ fn resolve_material(
|
||||||
MaterialRef::Name(name) => match named_materials.get(name) {
|
MaterialRef::Name(name) => match named_materials.get(name) {
|
||||||
Some(m) => *m,
|
Some(m) => *m,
|
||||||
None => {
|
None => {
|
||||||
MaterialIdx::default()
|
log::error!("{}: named material '{}' not found", loc, name);
|
||||||
// log::error!("{}: named material '{}' not found", loc, name);
|
MaterialIdx::NONE
|
||||||
// crate::core::material::default_diffuse_material(arena)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Anonymous materials are placed at the front of `materials`, so the
|
||||||
|
// index handed out by `add_material` is the handle directly.
|
||||||
MaterialRef::Index(idx) => {
|
MaterialRef::Index(idx) => {
|
||||||
if *idx < materials.len() {
|
if *idx < materials.len() {
|
||||||
MaterialIdx(*idx as u32)
|
MaterialIdx(*idx as u32)
|
||||||
} else {
|
} else {
|
||||||
MaterialIdx::default()
|
log::error!("{}: material index {} out of bounds", loc, idx);
|
||||||
// log::error!("{}: material index {} out of bounds", loc, idx);
|
MaterialIdx::NONE
|
||||||
// crate::core::material::default_diffuse_material(arena)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaterialRef::None => MaterialIdx::default(),
|
MaterialRef::None => MaterialIdx::NONE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,6 +259,11 @@ impl BasicScene {
|
||||||
state.named_materials.push((name.to_string(), material));
|
state.named_materials.push((name.to_string(), material));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_medium(&self, name: &str, medium: MediumSceneEntity) {
|
||||||
|
let mut state = self.media_state.lock();
|
||||||
|
state.entities.push((name.to_string(), medium));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_material(&self, material: SceneEntity) -> usize {
|
pub fn add_material(&self, material: SceneEntity) -> usize {
|
||||||
let mut state = self.material_state.lock();
|
let mut state = self.material_state.lock();
|
||||||
self.start_loading_normal_maps(&mut state, &material.parameters);
|
self.start_loading_normal_maps(&mut state, &material.parameters);
|
||||||
|
|
@ -498,13 +504,16 @@ impl BasicScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut materials: Vec<Material> = Vec::new();
|
// Named materials must be *created* first: an anonymous material may be a
|
||||||
let mut named_materials: HashMap<String, MaterialIdx> = HashMap::new();
|
// mix that resolves its sub-materials by name. They are *placed* after the
|
||||||
|
// anonymous ones though, so that `MaterialRef::Index(i)` — an index into
|
||||||
|
// `state.materials` handed out by `add_material` — is `MaterialIdx(i)`.
|
||||||
|
let mut named_created: Vec<(String, Material)> = Vec::new();
|
||||||
// Value map for resolving named sub-materials (e.g. mix) during creation.
|
// Value map for resolving named sub-materials (e.g. mix) during creation.
|
||||||
let mut named_values: HashMap<String, Material> = HashMap::new();
|
let mut named_values: HashMap<String, Material> = HashMap::new();
|
||||||
|
|
||||||
for (name, entity) in &state.named_materials {
|
for (name, entity) in &state.named_materials {
|
||||||
if named_materials.contains_key(name) {
|
if named_values.contains_key(name) {
|
||||||
log::error!(
|
log::error!(
|
||||||
"{}: trying to redefine named material '{}'.",
|
"{}: trying to redefine named material '{}'.",
|
||||||
entity.loc,
|
entity.loc,
|
||||||
|
|
@ -531,10 +540,8 @@ impl BasicScene {
|
||||||
arena,
|
arena,
|
||||||
) {
|
) {
|
||||||
Ok(mat) => {
|
Ok(mat) => {
|
||||||
let idx = MaterialIdx(materials.len() as u32);
|
|
||||||
materials.push(mat);
|
|
||||||
named_values.insert(name.clone(), mat);
|
named_values.insert(name.clone(), mat);
|
||||||
named_materials.insert(name.clone(), idx);
|
named_created.push((name.clone(), mat));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
|
|
@ -547,7 +554,9 @@ impl BasicScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indexed (anonymous) materials, appended after named ones.
|
// Indexed (anonymous) materials occupy [0, state.materials.len()) so that
|
||||||
|
// a `MaterialRef::Index` needs no offset.
|
||||||
|
let mut materials: Vec<Material> = Vec::with_capacity(state.materials.len());
|
||||||
for entity in &state.materials {
|
for entity in &state.materials {
|
||||||
let result: Result<Material> = (|| {
|
let result: Result<Material> = (|| {
|
||||||
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||||
|
|
@ -574,13 +583,19 @@ impl BasicScene {
|
||||||
materials.push(mat);
|
materials.push(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut named_materials: HashMap<String, MaterialIdx> = HashMap::new();
|
||||||
|
for (name, mat) in named_created {
|
||||||
|
named_materials.insert(name, MaterialIdx(materials.len() as u32));
|
||||||
|
materials.push(mat);
|
||||||
|
}
|
||||||
|
|
||||||
let default_mtl = MaterialIdx(materials.len() as u32);
|
let default_mtl = MaterialIdx(materials.len() as u32);
|
||||||
materials.push(crate::core::material::default_diffuse_material(arena));
|
materials.push(crate::core::material::default_diffuse_material(arena));
|
||||||
|
|
||||||
Ok((named_materials, materials, default_mtl))
|
Ok((named_materials, materials, default_mtl))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_media(&self) -> HashMap<String, Arc<Medium>> {
|
pub fn create_media(&self, arena: &Arena) -> HashMap<String, Arc<Medium>> {
|
||||||
let mut state = self.media_state.lock();
|
let mut state = self.media_state.lock();
|
||||||
if !state.jobs.is_empty() {
|
if !state.jobs.is_empty() {
|
||||||
let jobs: Vec<(String, AsyncJob<Medium>)> = state.jobs.drain().collect();
|
let jobs: Vec<(String, AsyncJob<Medium>)> = state.jobs.drain().collect();
|
||||||
|
|
@ -588,6 +603,29 @@ impl BasicScene {
|
||||||
state.map.insert(name, Arc::new(job.wait()));
|
state.map.insert(name, Arc::new(job.wait()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let entities: Vec<(String, MediumSceneEntity)> = state.entities.drain(..).collect();
|
||||||
|
for (name, entity) in entities {
|
||||||
|
if entity.render_from_object.is_animated() {
|
||||||
|
log::warn!(
|
||||||
|
"{}: animated media aren't supported, using start transform.",
|
||||||
|
entity.base.loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
match Medium::create(
|
||||||
|
&entity.base.name,
|
||||||
|
&entity.base.parameters,
|
||||||
|
entity.render_from_object.start_transform,
|
||||||
|
&entity.base.loc,
|
||||||
|
arena,
|
||||||
|
) {
|
||||||
|
Ok(m) => {
|
||||||
|
state.map.insert(name, Arc::new(*m));
|
||||||
|
}
|
||||||
|
Err(e) => log::error!("{}: failed to create medium: {}", entity.base.loc, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.map.clone()
|
state.map.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -711,7 +749,7 @@ impl BasicScene {
|
||||||
textures,
|
textures,
|
||||||
named_materials,
|
named_materials,
|
||||||
materials,
|
materials,
|
||||||
area_map,
|
Some(area_map),
|
||||||
media,
|
media,
|
||||||
arena,
|
arena,
|
||||||
);
|
);
|
||||||
|
|
@ -723,7 +761,6 @@ impl BasicScene {
|
||||||
textures,
|
textures,
|
||||||
named_materials,
|
named_materials,
|
||||||
materials,
|
materials,
|
||||||
area_map,
|
|
||||||
media,
|
media,
|
||||||
arena,
|
arena,
|
||||||
);
|
);
|
||||||
|
|
@ -740,7 +777,7 @@ impl BasicScene {
|
||||||
textures,
|
textures,
|
||||||
named_materials,
|
named_materials,
|
||||||
materials,
|
materials,
|
||||||
area_map,
|
None,
|
||||||
media,
|
media,
|
||||||
arena,
|
arena,
|
||||||
);
|
);
|
||||||
|
|
@ -749,7 +786,6 @@ impl BasicScene {
|
||||||
textures,
|
textures,
|
||||||
named_materials,
|
named_materials,
|
||||||
materials,
|
materials,
|
||||||
area_map,
|
|
||||||
media,
|
media,
|
||||||
arena,
|
arena,
|
||||||
);
|
);
|
||||||
|
|
@ -896,13 +932,23 @@ impl BasicScene {
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
named_materials: &HashMap<String, MaterialIdx>,
|
named_materials: &HashMap<String, MaterialIdx>,
|
||||||
materials: &[Material],
|
materials: &[Material],
|
||||||
area_map: &AreaLightMap,
|
// `None` for object-instance definitions: the map is keyed by the
|
||||||
|
// top-level shape index, so it must not be consulted for the
|
||||||
|
// independently-numbered shapes inside an instance definition.
|
||||||
|
area_map: Option<&AreaLightMap>,
|
||||||
media: &HashMap<String, Arc<Medium>>,
|
media: &HashMap<String, Arc<Medium>>,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Vec<Primitive> {
|
) -> Vec<Primitive> {
|
||||||
let mut primitives = Vec::new();
|
let mut primitives = Vec::new();
|
||||||
|
|
||||||
for (entity_idx, entity) in shapes.iter().enumerate() {
|
for (entity_idx, entity) in shapes.iter().enumerate() {
|
||||||
|
if area_map.is_none() && entity.light_index.is_some() {
|
||||||
|
log::error!(
|
||||||
|
"{}: area lights are not supported with object instancing.",
|
||||||
|
entity.base.loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let created_shapes = match Shape::create(
|
let created_shapes = match Shape::create(
|
||||||
&entity.base.name,
|
&entity.base.name,
|
||||||
*entity.render_from_object,
|
*entity.render_from_object,
|
||||||
|
|
@ -946,8 +992,7 @@ impl BasicScene {
|
||||||
for (sub_idx, shape) in created_shapes.into_iter().enumerate() {
|
for (sub_idx, shape) in created_shapes.into_iter().enumerate() {
|
||||||
// look up the pre-created light index instead of creating one
|
// look up the pre-created light index instead of creating one
|
||||||
let light_idx = area_map
|
let light_idx = area_map
|
||||||
.get(&(entity_idx, sub_idx))
|
.and_then(|m| m.get(&(entity_idx, sub_idx)).copied())
|
||||||
.copied()
|
|
||||||
.unwrap_or(LightIdx::NONE);
|
.unwrap_or(LightIdx::NONE);
|
||||||
|
|
||||||
let prim =
|
let prim =
|
||||||
|
|
@ -973,7 +1018,6 @@ impl BasicScene {
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
named_materials: &HashMap<String, MaterialIdx>,
|
named_materials: &HashMap<String, MaterialIdx>,
|
||||||
materials: &[Material],
|
materials: &[Material],
|
||||||
area_map: &AreaLightMap,
|
|
||||||
media: &HashMap<String, Arc<Medium>>,
|
media: &HashMap<String, Arc<Medium>>,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Vec<Primitive> {
|
) -> Vec<Primitive> {
|
||||||
|
|
@ -1215,10 +1259,10 @@ impl BasicScene {
|
||||||
entities: &[ShapeSceneEntity],
|
entities: &[ShapeSceneEntity],
|
||||||
loaded: Vec<ShapeWithContext>,
|
loaded: Vec<ShapeWithContext>,
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, MaterialIdx>,
|
||||||
materials: &[Material],
|
materials: &[Material],
|
||||||
media: &HashMap<String, Arc<Medium>>,
|
media: &HashMap<String, Arc<Medium>>,
|
||||||
shape_lights: &HashMap<usize, Vec<Light>>,
|
shape_lights: &AreaLightMap,
|
||||||
) -> Vec<Primitive> {
|
) -> Vec<Primitive> {
|
||||||
// TODO: GPU wavefront path — upload shapes into device-visible arena,
|
// TODO: GPU wavefront path — upload shapes into device-visible arena,
|
||||||
// build SOA primitive arrays for kernel dispatch
|
// build SOA primitive arrays for kernel dispatch
|
||||||
|
|
@ -1242,7 +1286,7 @@ impl BasicScene {
|
||||||
entities: &[AnimatedShapeSceneEntity],
|
entities: &[AnimatedShapeSceneEntity],
|
||||||
loaded: Vec<Ptr<Shape>>,
|
loaded: Vec<Ptr<Shape>>,
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, MaterialIdx>,
|
||||||
materials: &[Material],
|
materials: &[Material],
|
||||||
media: &HashMap<String, Arc<Medium>>,
|
media: &HashMap<String, Arc<Medium>>,
|
||||||
) -> Vec<Primitive> {
|
) -> Vec<Primitive> {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{LightSceneEntity, SceneEntity, TextureSceneEntity};
|
use super::{LightSceneEntity, MediumSceneEntity, SceneEntity, TextureSceneEntity};
|
||||||
use crate::core::image::HostImage;
|
use crate::core::image::HostImage;
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::parallel::AsyncJob;
|
use crate::utils::parallel::AsyncJob;
|
||||||
|
|
@ -34,6 +34,8 @@ pub struct LightState {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MediaState {
|
pub struct MediaState {
|
||||||
|
/// Media declared by `MakeNamedMedium`, not yet instantiated.
|
||||||
|
pub entities: Vec<(String, MediumSceneEntity)>,
|
||||||
pub jobs: HashMap<String, AsyncJob<Medium>>,
|
pub jobs: HashMap<String, AsyncJob<Medium>>,
|
||||||
pub map: HashMap<String, Arc<Medium>>,
|
pub map: HashMap<String, Arc<Medium>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl IntegratorBase {
|
||||||
use_mis: bool,
|
use_mis: bool,
|
||||||
) {
|
) {
|
||||||
for &idx in &self.infinite_lights {
|
for &idx in &self.infinite_lights {
|
||||||
let light = &self.lights[idx.0 as usize];
|
let light = idx.get(&self.lights);
|
||||||
let le = light.le(ray, lambda);
|
let le = light.le(ray, lambda);
|
||||||
if le.is_black() {
|
if le.is_black() {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,6 @@ pub struct PathIntegrator {
|
||||||
materials: Vec<Material>,
|
materials: Vec<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for PathIntegrator {}
|
|
||||||
unsafe impl Sync for PathIntegrator {}
|
|
||||||
|
|
||||||
impl PathIntegrator {
|
impl PathIntegrator {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
@ -108,7 +106,7 @@ impl PathIntegrator {
|
||||||
return SampledSpectrum::zero();
|
return SampledSpectrum::zero();
|
||||||
};
|
};
|
||||||
|
|
||||||
let light = &self.base.lights[sampled.light.0 as usize];
|
let light = sampled.light.get(&self.base.lights);
|
||||||
|
|
||||||
let Some(ls) = light.sample_li(&ctx, sampler.get2d(), lambda, true) else {
|
let Some(ls) = light.sample_li(&ctx, sampler.get2d(), lambda, true) else {
|
||||||
return SampledSpectrum::zero();
|
return SampledSpectrum::zero();
|
||||||
|
|
@ -240,7 +238,7 @@ impl RayIntegratorTrait for PathIntegrator {
|
||||||
state.l += state.beta * le;
|
state.l += state.beta * le;
|
||||||
} else if self.config.use_mis && !isect.area_light.is_none() {
|
} else if self.config.use_mis && !isect.area_light.is_none() {
|
||||||
let idx = isect.area_light;
|
let idx = isect.area_light;
|
||||||
let light = &self.base.lights[idx.0 as usize];
|
let light = idx.get(&self.base.lights);
|
||||||
let p_l = self.sampler.pmf_with_context(&state.prev_ctx, idx)
|
let p_l = self.sampler.pmf_with_context(&state.prev_ctx, idx)
|
||||||
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
||||||
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use shared::core::light::{Light, LightTrait};
|
|
||||||
use shared::lights::sampler::{LightSampler, PowerLightSampler, UniformLightSampler};
|
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
|
||||||
use shared::utils::sampling::AliasTable;
|
|
||||||
use shared::utils::Ptr;
|
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
|
use shared::core::LightIdx;
|
||||||
|
use shared::core::geometry::Bounds3f;
|
||||||
|
use shared::core::light::LightBounds;
|
||||||
|
use shared::core::light::{Light, LightTrait};
|
||||||
|
use shared::lights::sampler::{
|
||||||
|
BVHLightSampler, CompactLightBounds, LightBVHNode, LightSampler, NO_BIT_TRAIL,
|
||||||
|
PowerLightSampler, UniformLightSampler,
|
||||||
|
};
|
||||||
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
use shared::utils::Ptr;
|
||||||
|
use shared::utils::partition_slice;
|
||||||
|
use shared::utils::sampling::AliasTable;
|
||||||
|
|
||||||
pub fn create_light_sampler(name: &str, lights: &[Light], arena: &Arena) -> LightSampler {
|
pub fn create_light_sampler(name: &str, lights: &[Light], arena: &Arena) -> LightSampler {
|
||||||
match name {
|
match name {
|
||||||
"uniform" => LightSampler::Uniform(create_uniform(lights.len() as u32)),
|
"uniform" => LightSampler::Uniform(create_uniform(lights.len() as u32)),
|
||||||
"power" => LightSampler::Power(create_power(lights, arena)),
|
"power" => LightSampler::Power(create_power(lights, arena)),
|
||||||
"bvh" => {
|
"bvh" => LightSampler::BVH(create_bvh(lights, arena)),
|
||||||
log::warn!("BVH light sampler not yet implemented, falling back to power");
|
|
||||||
LightSampler::Power(create_power(lights, arena))
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unknown light sampler \"{}\", using power", name);
|
log::error!("Unknown light sampler \"{}\", using power", name);
|
||||||
LightSampler::Power(create_power(lights, arena))
|
LightSampler::Power(create_power(lights, arena))
|
||||||
|
|
@ -28,7 +32,6 @@ fn create_uniform(lights_len: u32) -> UniformLightSampler {
|
||||||
fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler {
|
fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler {
|
||||||
if lights.is_empty() {
|
if lights.is_empty() {
|
||||||
return PowerLightSampler {
|
return PowerLightSampler {
|
||||||
lights_len: 0,
|
|
||||||
alias_table: Ptr::null(),
|
alias_table: Ptr::null(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +54,142 @@ fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler {
|
||||||
let alias_ptr = arena.alloc(alias_table);
|
let alias_ptr = arena.alloc(alias_table);
|
||||||
|
|
||||||
PowerLightSampler {
|
PowerLightSampler {
|
||||||
lights_len: lights.len() as u32,
|
|
||||||
alias_table: alias_ptr,
|
alias_table: alias_ptr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Straight up port of original +C+ BVHLightSampler::BVHLightSampler and ::buildBVH (lightsamplers.cpp).
|
||||||
|
|
||||||
|
const N_BUCKETS: usize = 12;
|
||||||
|
|
||||||
|
/// Accumulates the flat node array and per-light bit trails during the build.
|
||||||
|
struct BVHBuilder {
|
||||||
|
nodes: Vec<LightBVHNode>,
|
||||||
|
bit_trails: Vec<u64>,
|
||||||
|
all_light_bounds: Bounds3f,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BVHBuilder {
|
||||||
|
/// Builds the subtree over `lights`, pairs of `(global light index, bounds)`,
|
||||||
|
/// reordered in place, returning its node index and combined bounds.
|
||||||
|
fn build(
|
||||||
|
&mut self,
|
||||||
|
lights: &mut [(usize, LightBounds)],
|
||||||
|
bit_trail: u64,
|
||||||
|
depth: u32,
|
||||||
|
) -> (usize, LightBounds) {
|
||||||
|
if lights.len() == 1 {
|
||||||
|
let (light_index, lb) = lights[0];
|
||||||
|
let cb = CompactLightBounds::new(&lb, &self.all_light_bounds);
|
||||||
|
self.bit_trails[light_index] = bit_trail;
|
||||||
|
self.nodes
|
||||||
|
.push(LightBVHNode::make_leaf(light_index as u32, cb));
|
||||||
|
return (self.nodes.len() - 1, lb);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mid = self.split(lights);
|
||||||
|
|
||||||
|
let node_index = self.nodes.len();
|
||||||
|
self.nodes.push(LightBVHNode::default());
|
||||||
|
debug_assert!(depth < 64);
|
||||||
|
// Traversal assumes child0 sits at node_index + 1, so only child1 is stored.
|
||||||
|
let (child0, b0) = self.build(&mut lights[..mid], bit_trail, depth + 1);
|
||||||
|
debug_assert_eq!(child0, node_index + 1);
|
||||||
|
let (child1, b1) = self.build(&mut lights[mid..], bit_trail | (1 << depth), depth + 1);
|
||||||
|
|
||||||
|
let lb = LightBounds::union(&b0, &b1);
|
||||||
|
let cb = CompactLightBounds::new(&lb, &self.all_light_bounds);
|
||||||
|
self.nodes[node_index] = LightBVHNode::make_interior(child1 as u32, cb);
|
||||||
|
(node_index, lb)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reorders `lights` at the SAH split point and returns where to cut.
|
||||||
|
fn split(&self, lights: &mut [(usize, LightBounds)]) -> usize {
|
||||||
|
let mut bounds = Bounds3f::default();
|
||||||
|
let mut centroid_bounds = Bounds3f::default();
|
||||||
|
for (_, lb) in lights.iter() {
|
||||||
|
bounds = bounds.union(lb.bounds);
|
||||||
|
centroid_bounds = centroid_bounds.union_point(lb.centroid());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut best: Option<(Float, usize, usize)> = None; // (cost, dim, bucket)
|
||||||
|
for dim in 0..3 {
|
||||||
|
if centroid_bounds.p_max[dim] == centroid_bounds.p_min[dim] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buckets = [LightBounds::default(); N_BUCKETS];
|
||||||
|
for (_, lb) in lights.iter() {
|
||||||
|
let b = centroid_bounds.sah_bucket(&lb.centroid(), dim, N_BUCKETS);
|
||||||
|
buckets[b] = LightBounds::union(&buckets[b], lb);
|
||||||
|
}
|
||||||
|
|
||||||
|
let union = |bs: &[LightBounds]| {
|
||||||
|
bs.iter()
|
||||||
|
.fold(LightBounds::default(), |a, b| LightBounds::union(&a, b))
|
||||||
|
};
|
||||||
|
// pbrt only considers splits after buckets 1..N_BUCKETS-1.
|
||||||
|
for b in 1..N_BUCKETS - 1 {
|
||||||
|
let cost = BVHLightSampler::evaluate_cost(&union(&buckets[..=b]), &bounds, dim)
|
||||||
|
+ BVHLightSampler::evaluate_cost(&union(&buckets[b + 1..]), &bounds, dim);
|
||||||
|
if cost > 0. && best.map_or(true, |(c, ..)| cost < c) {
|
||||||
|
best = Some((cost, dim, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some((_, dim, bucket)) = best else {
|
||||||
|
return lights.len() / 2;
|
||||||
|
};
|
||||||
|
let mid = partition_slice(lights, |(_, lb)| {
|
||||||
|
centroid_bounds.sah_bucket(&lb.centroid(), dim, N_BUCKETS) <= bucket
|
||||||
|
});
|
||||||
|
if mid == 0 || mid == lights.len() {
|
||||||
|
lights.len() / 2
|
||||||
|
} else {
|
||||||
|
mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_bvh(lights: &[Light], arena: &Arena) -> BVHLightSampler {
|
||||||
|
// Infinite lights are sampled uniformly; bounded lights that emit go in the BVH.
|
||||||
|
let mut infinite_lights = Vec::new();
|
||||||
|
let mut bvh_lights = Vec::new();
|
||||||
|
let mut all_light_bounds = Bounds3f::default();
|
||||||
|
|
||||||
|
for (i, light) in lights.iter().enumerate() {
|
||||||
|
match light.bounds() {
|
||||||
|
None => infinite_lights.push(LightIdx(i as u32)),
|
||||||
|
Some(lb) if lb.phi > 0. => {
|
||||||
|
all_light_bounds = all_light_bounds.union(lb.bounds);
|
||||||
|
bvh_lights.push((i, lb));
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder = BVHBuilder {
|
||||||
|
nodes: Vec::new(),
|
||||||
|
// Lights that never become a leaf keep the sentinel.
|
||||||
|
bit_trails: vec![NO_BIT_TRAIL; lights.len()],
|
||||||
|
all_light_bounds,
|
||||||
|
};
|
||||||
|
if !bvh_lights.is_empty() {
|
||||||
|
builder.build(&mut bvh_lights, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (nodes, nodes_len) = arena.alloc_slice(&builder.nodes);
|
||||||
|
let (bit_trails, _) = arena.alloc_slice(&builder.bit_trails);
|
||||||
|
let (infinite, infinite_len) = arena.alloc_slice(&infinite_lights);
|
||||||
|
|
||||||
|
BVHLightSampler {
|
||||||
|
nodes,
|
||||||
|
infinite_lights: infinite,
|
||||||
|
bit_trails,
|
||||||
|
nodes_len: nodes_len as u32,
|
||||||
|
lights_len: lights.len() as u32,
|
||||||
|
infinite_lights_len: infinite_len as u32,
|
||||||
|
all_light_bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,10 @@ impl ParameterDictionary {
|
||||||
params: &[ParsedParameter],
|
params: &[ParsedParameter],
|
||||||
color_space: Option<Arc<RGBColorSpace>>,
|
color_space: Option<Arc<RGBColorSpace>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let n_owned_params = params.len();
|
// paramdict.cpp:150 — the owned params are `p0`; the inherited attribute
|
||||||
|
// vector is appended (reversed) after `p0` has itself been reversed.
|
||||||
|
let n_owned_params = p0.len();
|
||||||
|
p0.reverse();
|
||||||
p0.extend(params.iter().rev().cloned());
|
p0.extend(params.iter().rev().cloned());
|
||||||
|
|
||||||
let dict = Self {
|
let dict = Self {
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ impl WavefrontAggregate for CpuAggregate {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Material eval queue dispatch
|
// Material eval queue dispatch
|
||||||
let material = &self.materials[intr.material.0 as usize];
|
let material = intr.material.get(&self.materials);
|
||||||
let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) {
|
let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) {
|
||||||
basic_eval_mtl_q
|
basic_eval_mtl_q
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ impl CpuWavefrontRenderer {
|
||||||
let mut l_contrib = SampledSpectrum::new(0.0);
|
let mut l_contrib = SampledSpectrum::new(0.0);
|
||||||
|
|
||||||
for idx in infinite_lights {
|
for idx in infinite_lights {
|
||||||
let light = &self.lights[idx.0 as usize];
|
let light = idx.get(&self.lights);
|
||||||
let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null());
|
let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null());
|
||||||
let le = light.le(&ray, &w.lambda);
|
let le = light.le(&ray, &w.lambda);
|
||||||
if le.is_black() {
|
if le.is_black() {
|
||||||
|
|
@ -402,7 +402,7 @@ impl CpuWavefrontRenderer {
|
||||||
if w.area_light.is_none() {
|
if w.area_light.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let light = &self.lights[w.area_light.0 as usize];
|
let light = w.area_light.get(&self.lights);
|
||||||
|
|
||||||
let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda);
|
let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda);
|
||||||
if le.is_black() {
|
if le.is_black() {
|
||||||
|
|
@ -475,7 +475,7 @@ impl CpuWavefrontRenderer {
|
||||||
if w.material.is_none() {
|
if w.material.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let material = &self.materials[w.material.0 as usize];
|
let material = w.material.get(&self.materials);
|
||||||
|
|
||||||
let pi = w.pixel_index as usize;
|
let pi = w.pixel_index as usize;
|
||||||
let rs = pixel_sample_state.samples.get(pi);
|
let rs = pixel_sample_state.samples.get(pi);
|
||||||
|
|
@ -604,7 +604,7 @@ impl CpuWavefrontRenderer {
|
||||||
DIAG_SAMPLE_LIGHT_NONE.fetch_add(1, Ordering::Relaxed);
|
DIAG_SAMPLE_LIGHT_NONE.fetch_add(1, Ordering::Relaxed);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let light = &self.lights[sampled_light.light.0 as usize];
|
let light = sampled_light.light.get(&self.lights);
|
||||||
|
|
||||||
let Some(ls) = light.sample_li(&light_ctx, rs.direct.u, &lambda, true) else {
|
let Some(ls) = light.sample_li(&light_ctx, rs.direct.u, &lambda, true) else {
|
||||||
DIAG_SAMPLE_LI_NONE.fetch_add(1, Ordering::Relaxed);
|
DIAG_SAMPLE_LI_NONE.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue