This commit is contained in:
pingu 2026-01-18 16:29:27 +00:00
parent 3b3f9eb155
commit 86c9a90f2e
114 changed files with 1548 additions and 2421 deletions

View file

@ -6,13 +6,14 @@ edition = "2024"
[features]
default = []
use_f64 = []
use_gpu = []
use_nvtx = []
cuda = ["cust", "cuda_builder", "shared/cuda", ]
[dependencies]
anyhow = "1.0.100"
exr = "1.73.0"
flate2 = "1.1.5"
gpu = "0.2.3"
half = "2.7.1"
image_rs = { package = "image", version = "0.25.8" }
indicatif = "0.18.3"
@ -40,6 +41,11 @@ slice = "0.0.4"
crossbeam-channel = "0.5.15"
num_cpus = "1.17.0"
ply-rs = "0.1.3"
enum_dispatch = "0.3.13"
bytemuck = "1.24.0"
once_cell = "1.21.3"
smallvec = "1.15.1"
cuda-runtime-sys = "0.3.0-alpha.1"
[build-dependencies]
spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", branch = "main", optional = true }

View file

@ -1,447 +1,16 @@
#![no_std]
use cuda_std::prelude::*;
use shared::Float;
pub mod wavefront;
pub mod workitem;
use cust::context::{CacheConfig, CurrentContext, ResourceLimit};
use cust::device::DeviceAttribute;
use cust::memory::{DeviceCopy, DeviceMemory};
use cust::prelude::*;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::error::Error;
use std::ffi::c_void;
use std::sync::Arc;
use crate::Float;
use crate::core::geometry::{Normal, Point, Vector};
use crate::core::medium::Medium;
use crate::core::options::{PBRTOptions, get_options};
use crate::impl_gpu_traits;
use crate::impl_math_gpu_traits;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::interval::Interval;
pub use workitem::{
EscapedRayQueue, GetBSSRDFAndProbeRayQueue, HitAreaLightQueue, MaterialEvalQueue,
MediumSampleQueue, MediumScatterQueue, PixelSampleStateStorage, RayQueue, ShadowRayQueue,
SubsurfaceScatterQueue,
};
#[repr(C, align(16))]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Float4 {
pub v: [f32; 4],
}
pub type Vec4 = Vector<Float, 4>;
impl From<Vec4> for Float4 {
#[inline]
fn from(vec: Vector<f32, 4>) -> Self {
Self { v: vec.0 }
}
}
impl From<Float4> for Vec4 {
#[inline]
fn from(storage: Float4) -> Self {
Vector(storage.v)
}
}
impl_math_gpu_traits!(Vector);
impl_math_gpu_traits!(Normal);
impl_math_gpu_traits!(Point);
impl_gpu_traits!(Interval);
impl_gpu_traits!(Float4);
impl_gpu_traits!(SampledSpectrum);
impl_gpu_traits!(SampledWavelengths);
struct KernelStats {
description: String,
num_launches: usize,
sum_ms: f32,
min_ms: f32,
max_ms: f32,
}
impl KernelStats {
fn new(description: &str) -> Self {
Self {
description: description.to_string(),
num_launches: 0,
sum_ms: 0.0,
min_ms: 0.0,
max_ms: 0.0,
}
}
}
struct ProfilerEvent {
start: Event,
stop: Event,
active: bool,
stats: Option<Arc<Mutex<KernelStats>>>,
}
impl ProfilerEvent {
fn new() -> Result<Self, cust::error::CudaError> {
let start = Event::new(EventFlags::DEFAULT)?;
let stop = Event::new(EventFlags::DEFAULT)?;
Ok(Self {
start,
stop,
active: false,
stats: None,
})
}
fn sync(&mut self) {
if !self.active {
return;
}
if self.stop.synchronize().is_ok() {
// Check timing between start and stop
match self.stop.elapsed_time_f32(&self.start) {
Ok(ms) => {
if let Some(stats_arc) = &self.stats {
let mut stats = stats_arc.lock();
stats.num_launches += 1;
if stats.num_launches == 1 {
stats.sum_ms = ms;
stats.min_ms = ms;
stats.max_ms = ms;
} else {
stats.sum_ms += ms;
stats.min_ms = stats.min_ms.min(ms);
stats.max_ms = stats.max_ms.max(ms);
}
}
}
Err(e) => log::error!("Failed to get elapsed time: {:?}", e),
}
}
self.active = false;
}
}
// --- Profiler Manager ---
struct Profiler {
kernel_stats: Vec<Arc<Mutex<KernelStats>>>,
event_pool: Vec<ProfilerEvent>,
pool_offset: usize,
}
impl Profiler {
fn new() -> Self {
Self {
kernel_stats: Vec::new(),
event_pool: Vec::new(),
pool_offset: 0,
}
}
/// Prepares an event from the pool.
/// Returns a mutable reference to the event, valid as long as the borrow of self.
fn prepare<'a>(&'a mut self, description: &str) -> &'a mut ProfilerEvent {
// Grow pool if empty or needed (simple heuristic)
if self.event_pool.is_empty() {
for _ in 0..128 {
if let Ok(e) = ProfilerEvent::new() {
self.event_pool.push(e);
}
}
}
if self.pool_offset >= self.event_pool.len() {
self.pool_offset = 0;
}
let idx = self.pool_offset;
self.pool_offset += 1;
let pe = &mut self.event_pool[idx];
if pe.active {
pe.sync();
}
pe.active = true;
pe.stats = None;
// Find or create stats
let mut found = None;
for s in &self.kernel_stats {
if s.lock().description == description {
found = Some(s.clone());
break;
}
}
if found.is_none() {
let new_stats = Arc::new(Mutex::new(KernelStats::new(description)));
self.kernel_stats.push(new_stats.clone());
found = Some(new_stats);
}
pe.stats = found;
pe
}
}
pub struct GpuState {
context: Context,
stream: Stream,
profiler: Profiler,
}
impl GpuState {
fn init(device_index: u32) -> Result<Self, Box<dyn Error>> {
cust::init(CudaFlags::empty())?;
let device = Device::get_device(device_index)?;
let name = device.name().unwrap_or_else(|_| "Unknown".into());
let memory = device.total_memory().unwrap_or(0);
let memory_gb = memory as f64 / (1024.0 * 1024.0 * 1024.0);
let major = device
.get_attribute(DeviceAttribute::ComputeCapabilityMajor)
.unwrap_or(0);
let minor = device
.get_attribute(DeviceAttribute::ComputeCapabilityMinor)
.unwrap_or(0);
log::info!(
"Selected GPU: {} ({:.2} GB, SM {}.{})",
name,
memory_gb,
major,
minor
);
let has_unified = device
.get_attribute(DeviceAttribute::UnifiedAddressing)
.unwrap_or(0);
if has_unified == 0 {
panic!("Selected GPU does not support unified addressing.");
}
let context = Context::new(device)?;
CurrentContext::set_resource_limit(ResourceLimit::StackSize, 8192)?;
let stack_size = CurrentContext::get_resource_limit(ResourceLimit::StackSize)?;
log::info!("Reset stack size to {}", stack_size);
CurrentContext::set_resource_limit(ResourceLimit::PrintfFifoSize, 32 * 1024 * 1024)?;
CurrentContext::set_cache_config(CacheConfig::PreferL1)?;
let stream = Stream::new(StreamFlags::DEFAULT, None)?;
Ok(Self {
context,
stream,
profiler: Profiler::new(),
})
}
}
lazy_static! {
static ref GPU_STATE: Mutex<Option<GpuState>> = Mutex::new(None);
}
pub fn gpu_init() {
if !get_options().use_gpu {
return;
}
let device_id = get_options().gpu_device.unwrap_or(0);
log::info!("Initializing GPU Device {}", device_id);
match GpuState::init(device_id) {
Ok(state) => {
#[cfg(feature = "use_nvtx")]
nvtx::name_thread("MAIN_THREAD");
*GPU_STATE.lock() = Some(state);
}
Err(e) => {
panic!("Failed to initialize GPU: {:?}", e);
}
}
}
pub fn gpu_thread_init() {
if let Some(state) = GPU_STATE.lock().as_ref() {
if let Err(e) = CurrentContext::set_current(&state.context) {
log::error!("Failed to set CUDA context for thread: {:?}", e);
}
}
}
pub fn gpu_wait() {
let mut guard = GPU_STATE.lock();
if let Some(state) = guard.as_mut() {
if let Err(e) = state.stream.synchronize() {
log::error!("GPU Wait failed: {:?}", e);
}
}
}
/// Launches a parallel for loop on the GPU.
///
/// # Arguments
/// * `description`: Name for profiling.
/// * `n_items`: Total items (threads).
/// * `function`: Compiled kernel function handle.
/// * `params`: Kernel parameters (must be DeviceCopy).
pub fn gpu_parallel_for<T: DeviceCopy>(
description: &str,
n_items: i32,
function: &Function,
params: &T,
) {
#[cfg(feature = "use_nvtx")]
nvtx::range_push(description);
let mut guard = GPU_STATE.lock();
let state = guard.as_mut().expect("GPU not initialized");
let (_, block_size) = match function.suggested_launch_configuration(0, 0.into()) {
Ok(cfg) => cfg,
Err(e) => panic!(
"Failed to calculate launch config for {}: {:?}",
description, e
),
};
#[cfg(debug_assertions)]
log::debug!("[{}] Block size: {}", description, block_size);
let grid_size = (n_items as u32 + block_size - 1) / block_size;
let stream = &state.stream;
let profiler = &mut state.profiler;
// Save the index we are about to use so we can retrieve the STOP event later
let event_idx = profiler.pool_offset;
{
let pe = profiler.prepare(description);
if let Err(e) = pe.start.record(stream) {
log::error!("Failed to record start event: {:?}", e);
}
}
let params_ptr = params as *const T as *mut c_void;
let n_items_ptr = &n_items as *const i32 as *mut c_void;
let args = [params_ptr, n_items_ptr];
#[kernel]
pub unsafe fn scale_array_kernel(data: *mut Float, n: u32, scale: Float) {
let i = thread::index_1d() as usize;
if i < n as usize {
let ptr = unsafe { data.add(i) };
unsafe {
if let Err(e) =
state
.stream
.launch(function, (grid_size, 1, 1), (block_size, 1, 1), 0, &args)
{
panic!("CUDA Launch failed for {}: {:?}", description, e);
*ptr = *ptr * scale;
}
}
// Retrieve the specific event we just set up.
// Pool_offset was incremented in prepare().
// If event_idx was the one used, the event is at event_idx.
if event_idx < profiler.event_pool.len() {
let pe = &mut profiler.event_pool[event_idx];
if let Err(e) = pe.stop.record(stream) {
log::error!("Failed to record stop event: {:?}", e);
}
}
#[cfg(debug_assertions)]
let _ = state.stream.synchronize();
#[cfg(feature = "use_nvtx")]
nvtx::range_pop();
}
pub fn report_kernel_stats() {
let mut guard = GPU_STATE.lock();
if let Some(state) = guard.as_mut() {
let _ = state.stream.synchronize();
// Process all pending events
for pe in &mut state.profiler.event_pool {
if pe.active {
pe.sync();
}
}
let mut total_ms = 0.0;
for s in &state.profiler.kernel_stats {
total_ms += s.lock().sum_ms;
}
println!("Wavefront Kernel Profile:");
for s in &state.profiler.kernel_stats {
let stats = s.lock();
let percent = if total_ms > 0.0 {
100.0 * stats.sum_ms / total_ms
} else {
0.0
};
println!(
" {:<45} {:5} launches {:9.2} ms / {:5.1}% (avg {:6.3})",
stats.description,
stats.num_launches,
stats.sum_ms,
percent,
if stats.num_launches > 0 {
stats.sum_ms / stats.num_launches as f32
} else {
0.0
}
);
}
println!("\nTotal: {:.2} ms", total_ms);
}
}
pub fn gpu_memset<T: DeviceCopy>(dst: &mut DeviceSlice<T>, value: u8) {
unsafe {
let ptr = dst.as_raw_ptr(); // Returns CUdeviceptr (u64)
let len = dst.len() * std::mem::size_of::<T>();
// We need the `cust::external::cuda` or equivalent sys crate function
log::warn!("gpu_memset requested but raw memset not exposed via safe cust API yet.");
}
}
#[macro_export]
macro_rules! impl_gpu_traits {
($name:ty) => {
unsafe impl cust::memory::DeviceCopy for $name {}
unsafe impl bytemuck::Zeroable for $name {}
unsafe impl bytemuck::Pod for $name {}
};
}
#[macro_export]
macro_rules! impl_math_gpu_traits {
($Struct:ident) => {
#[cfg(feature = "use_gpu")]
unsafe impl<T, const N: usize> cust::memory::DeviceCopy for $Struct<T, N> where
T: cust::memory::DeviceCopy + Copy
{
}
unsafe impl<T, const N: usize> bytemuck::Zeroable for $Struct<T, N> where
T: bytemuck::Zeroable
{
}
unsafe impl<T, const N: usize> bytemuck::Pod for $Struct<T, N> where T: bytemuck::Pod {}
};
}

View file

@ -1,47 +0,0 @@
use image_rs::Pixel;
use crate::camera::Camera;
use crate::core::film::Film;
use crate::core::filter::Filter;
use crate::core::sampler::Sampler;
use crate::core::scene::BasicScene;
use crate::lights::Light;
use crate::lights::LightSampler;
use crate::{
EscapedRayQueue, GetBSSRDFAndProbeRayQueue, HitAreaLightQueue, MaterialEvalQueue,
MediumSampleQueue, MediumScatterQueue, PixelSampleStateStorage, RayQueue, ShadowRayQueue,
SubsurfaceScatterQueue,
};
use std::sync::Arc;
pub struct WavefrontPathIntegrator {
pub film: Film,
pub filter: Filter,
pub sampler: Sampler,
pub camera: Arc<Camera>,
pub light_sampler: LightSampler,
pub infinite_lights: Option<Vec<Arc<Light>>>,
pub max_depth: i32,
pub samples_per_pixel: i32,
pub regularize: bool,
pub scanlines_per_pixel: i32,
pub max_queue_size: i32,
pub pixel_sample_state: PixelSampleStateStorage,
pub ray_queue: [RayQueue; 2],
pub hit_area_light_queue: HitAreaLightQueue,
pub shadow_ray_queue: ShadowRayQueue,
pub escaped_ray_queue: Option<EscapedRayQueue>,
pub basic_material_queue: Option<MaterialEvalQueue>,
pub universal_material_queue: Option<MaterialEvalQueue>,
pub medium_sample_queue: Option<MediumSampleQueue>,
pub medium_scatter_queue: Option<MediumScatterQueue>,
pub bssrf_queue: Option<GetBSSRDFAndProbeRayQueue>,
pub subsurface_queue: Option<SubsurfaceScatterQueue>,
}
#[cfg(feature = "use_gpu")]
impl WavefrontPathIntegrator {
pub fn new(scene: BasicScene) -> Self {
todo!()
}
}

View file

@ -1,535 +0,0 @@
#![allow(clippy::too_many_arguments)]
use super::Float4;
use crate::Float;
use crate::core::geometry::{Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray, Vector3f};
use crate::lights::LightSampleContext;
use crate::soa_struct;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use cust::memory::{CopyDestination, DeviceMemory};
use cust::prelude::*;
#[macro_export]
macro_rules! soa_struct {
(
$(#[$outer:meta])*
pub struct $name:ident {
$(
pub $field:ident : $type:ty
),* $(,)?
}
) => {
#[cfg(feature = "use_gpu")]
$(#[$outer])*
pub struct $name {
capacity: u32,
pub count: cust::memory::DeviceBuffer<u32>,
$(
pub $field: cust::memory::DeviceBuffer<$type>,
)*
}
#[cfg(feature = "use_gpu")]
impl $name {
pub fn new(capacity: usize) -> cust::error::CudaResult<Self> {
use cust::memory::DeviceBuffer;
Ok(Self {
capacity: capacity as u32,
count: DeviceBuffer::zeroed(1)?,
$(
$field: DeviceBuffer::zeroed(capacity)?,
)*
})
}
pub fn len(&self) -> cust::error::CudaResult<u32> {
let mut host_count = [0u32; 1];
self.count.copy_to(&mut host_count)?;
Ok(host_count[0])
}
pub fn reset(&mut self) -> cust::error::CudaResult<()> {
self.count.copy_from(&[0])
}
// Generate the View name
pub fn as_view(&mut self) -> paste::paste! { [<$name View>] } {
paste::paste! {
[<$name View>] {
capacity: self.capacity,
count: self.count.as_device_ptr().as_mut_ptr(),
$(
$field: self.$field.as_device_ptr().as_raw() as *mut $type,
)*
}
}
}
}
paste::paste! {
#[repr(C)]
#[derive(Clone, Copy)]
pub struct [<$name View>] {
pub capacity: u32,
pub count: *mut u32,
$(
pub $field: *mut $type,
)*
}
unsafe impl cust::memory::DeviceCopy for [<$name View>] {}
impl [<$name View>] {
// The raw push that fills every field
#[cfg(feature = "use_gpu")]
pub unsafe fn push(&self, $( $field : $type ),* ) -> Option<u32> {
use core::sync::atomic::{AtomicU32, Ordering};
let index = unsafe {
let counter_ptr = self.count as *mut AtomicU32;
(*counter_ptr).fetch_add(1, Ordering::Relaxed)
};
if index >= self.capacity {
return None;
}
unsafe {
$(
*self.$field.add(index as usize) = $field;
)*
}
Some(index)
}
#[cfg(feature = "use_gpu")]
pub unsafe fn size(&self) -> u32 {
use core::sync::atomic::{AtomicU32, Ordering};
unsafe {
(*(self.count as *const AtomicU32)).load(Ordering::Relaxed)
}
}
$(
#[cfg(feature = "use_gpu")]
pub fn [<$field _ptr>](&self) -> *mut $type {
self.$field
}
)*
}
}
};
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct RaySamplesDirect {
pub u: Point2f,
pub uc: Float,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct RaySamplesIndirect {
pub uc: Float,
pub rr: Float,
pub u: Point2f,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct RaySamplesSubsurface {
pub uc: Float,
pub u: Point2f,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct RaySamples {
pub direct: RaySamplesDirect,
pub indirect: RaySamplesIndirect,
pub have_subsurface: bool,
pub subsurface: RaySamplesSubsurface,
}
soa_struct! {
pub struct RayQueue {
pub ray_o: Point3f,
pub ray_d: Vector3f,
pub depth: i32,
pub lambda: SampledWavelengths,
pub pixel_index: u32,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub ctx_pi: Point3f,
pub ctx_n: Normal3f,
pub ctx_ns: Normal3f,
pub eta_scale: Float,
pub specular_bounce: u32,
pub any_non_specular_bounces: u32,
}
}
soa_struct! {
pub struct PixelSampleStateStorage {
pub p_pixel: Point2i,
pub l: SampledSpectrum,
pub lambda: SampledWavelengths,
pub filter_weight: Float,
pub visible_surface: u32,
pub camera_ray_weight: SampledSpectrum,
pub rs_direct_packed: Float4,
pub rs_indirect_packed: Float4,
pub rs_subsurface_packed: Float4,
}
}
soa_struct! {
pub struct EscapedRayQueue {
pub ray_o: Point3f,
pub ray_d: Vector3f,
pub depth: i32,
pub lambda: SampledWavelengths,
pub pixel_index: u32,
pub beta: SampledSpectrum,
pub specular_bounce: u32,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub ctx_pi: Point3f,
pub ctx_n: Normal3f,
pub ctx_ns: Normal3f,
}
}
soa_struct! {
pub struct HitAreaLightQueue {
pub area_light_id: u32, // Light ID
pub p: Point3f,
pub n: Normal3f,
pub uv: Point2f,
pub wo: Vector3f,
pub lambda: SampledWavelengths,
pub depth: i32,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub ctx_pi: Point3f,
pub ctx_n: Normal3f,
pub ctx_ns: Normal3f,
pub specular_bounce: u32,
pub pixel_index: u32,
}
}
soa_struct! {
pub struct ShadowRayQueue {
pub ray_o: Point3f,
pub ray_d: Vector3f,
pub t_max: Float,
pub lambda: SampledWavelengths,
pub ld: SampledSpectrum,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub pixel_index: u32,
}
}
soa_struct! {
pub struct GetBSSRDFAndProbeRayQueue {
pub material_id: u32,
pub lambda: SampledWavelengths,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub p: Point3f,
pub wo: Vector3f,
pub n: Normal3f,
pub ns: Normal3f,
pub dpdus: Vector3f,
pub uv: Point2f,
pub depth: i32,
pub mi_inside: u32,
pub mi_outside: u32,
pub eta_scale: Float,
pub pixel_index: u32,
}
}
soa_struct! {
pub struct SubsurfaceScatterQueue {
pub p0: Point3f,
pub p1: Point3f,
pub depth: i32,
pub material_id: u32,
pub lambda: SampledWavelengths,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub mi_inside: u32,
pub mi_outside: u32,
pub eta_scale: Float,
pub pixel_index: u32,
}
}
soa_struct! {
pub struct MediumSampleQueue {
pub ray_o: Point3f,
pub ray_d: Vector3f,
pub t_max: Float,
pub lambda: SampledWavelengths,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub pixel_index: u32,
pub ctx_pi: Point3f,
pub ctx_n: Normal3f,
pub ctx_ns: Normal3f,
pub specular_bounce: u32,
pub any_non_specular_bounces: u32,
pub eta_scale: Float,
pub area_light_id: u32,
pub pi: Point3fi,
pub n: Normal3f,
pub dpdu: Vector3f,
pub dpdv: Vector3f,
pub wo: Vector3f,
pub uv: Point2f,
pub material_id: u32,
pub ns: Normal3f,
pub dpdus: Vector3f,
pub dpdvs: Vector3f,
pub dndus: Normal3f,
pub dndvs: Normal3f,
pub face_index: i32,
pub mi_inside: u32,
pub mi_outside: u32,
}
}
soa_struct! {
pub struct MaterialEvalQueue {
pub material_id: u32,
pub pi: Point3fi,
pub n: Normal3f,
pub dpdu: Vector3f,
pub dpdv: Vector3f,
pub time: Float,
pub depth: i32,
pub ns: Normal3f,
pub dpdus: Vector3f,
pub dpdvs: Vector3f,
pub dndus: Normal3f,
pub dndvs: Normal3f,
pub uv: Point2f,
pub face_index: i32,
pub lambda: SampledWavelengths,
pub pixel_index: u32,
pub any_non_specular_bounces: u32,
pub wo: Vector3f,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub eta_scale: Float,
pub mi_inside: u32,
pub mi_outside: u32,
}
}
soa_struct! {
pub struct MediumScatterQueue {
pub p: Point3f,
pub depth: usize,
pub lambda: SampledWavelengths,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub wo: Vector3f,
pub time: Float,
pub eta_scale: Float,
pub pixel_index: usize,
// ID
pub phase_function: u32,
pub medium: u32,
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct RayWorkItem {
pub ray: Ray,
pub depth: i32,
pub lambda: SampledWavelengths,
pub pixel_index: u32,
pub beta: SampledSpectrum,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub prev_intr_ctx: LightSampleContext,
pub eta_scale: Float,
pub specular_bounce: bool,
pub any_non_specular_bounces: bool,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EscapedRayWorkItem {
pub ray_o: Point3f,
pub ray_d: Vector3f,
pub depth: i32,
pub lambda: SampledWavelengths,
pub pixel_index: u32,
pub beta: SampledSpectrum,
pub specular_bounce: bool,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub prev_intr_ctx: LightSampleContext,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ShadowRayWorkItem {
pub ray: Ray,
pub t_max: Float,
pub lambda: SampledWavelengths,
pub ld: SampledSpectrum,
pub r_u: SampledSpectrum,
pub r_l: SampledSpectrum,
pub pixel_index: u32,
}
impl RayQueueView {
#[cfg(feature = "use_gpu")]
pub unsafe fn push_work_item(&self, item: RayWorkItem) -> Option<u32> {
unsafe {
self.push(
item.ray.o,
item.ray.d,
item.depth,
item.lambda,
item.pixel_index,
item.beta,
item.r_u,
item.r_l,
item.prev_intr_ctx.pi.into(),
item.prev_intr_ctx.n,
item.prev_intr_ctx.ns,
item.eta_scale,
if item.specular_bounce { 1 } else { 0 },
if item.any_non_specular_bounces { 1 } else { 0 },
)
}
}
}
impl EscapedRayQueueView {
#[cfg(feature = "use_gpu")]
pub unsafe fn push_work_item(&self, r: &RayWorkItem) -> Option<u32> {
unsafe {
self.push(
r.ray.o,
r.ray.d,
r.depth,
r.lambda,
r.pixel_index,
r.beta,
if r.specular_bounce { 1 } else { 0 },
r.r_u,
r.r_l,
r.prev_intr_ctx.pi.into(),
r.prev_intr_ctx.n,
r.prev_intr_ctx.ns,
)
}
}
}
impl PixelSampleStateStorageView {
#[cfg(feature = "use_gpu")]
pub unsafe fn get_samples(&self, index: u32) -> RaySamples {
let i = index as usize;
let (dir, ind, ss) = unsafe {
(
*self.rs_direct_packed.add(i),
*self.rs_indirect_packed.add(i),
*self.rs_subsurface_packed.add(i),
)
};
let direct_u = Point2f::new(dir.v[0], dir.v[1]);
let direct_uc = dir.v[2];
let flags = dir.v[3] as i32;
let have_subsurface = (flags & 1) != 0;
let indirect_uc = ind.v[0];
let indirect_rr = ind.v[1];
let indirect_u = Point2f::new(ind.v[2], ind.v[3]);
let subsurface_uc = ss.v[0];
let subsurface_u = Point2f::new(ss.v[1], ss.v[2]);
RaySamples {
direct: RaySamplesDirect {
u: direct_u,
uc: direct_uc,
},
indirect: RaySamplesIndirect {
uc: indirect_uc,
rr: indirect_rr,
u: indirect_u,
},
have_subsurface,
subsurface: RaySamplesSubsurface {
uc: subsurface_uc,
u: subsurface_u,
},
}
}
#[cfg(feature = "use_gpu")]
pub unsafe fn set_samples(&self, index: u32, rs: RaySamples) {
if index >= self.capacity {
return;
}
let i = index as usize;
let flags = if rs.have_subsurface { 1.0 } else { 0.0 };
let dir = Float4 {
v: [rs.direct.u.0[0], rs.direct.u.0[1], rs.direct.uc, flags],
};
let ind = Float4 {
v: [
rs.indirect.uc,
rs.indirect.rr,
rs.indirect.u.0[0],
rs.indirect.u.0[1],
],
};
unsafe {
*self.rs_direct_packed.add(i) = dir;
*self.rs_indirect_packed.add(i) = ind;
}
if rs.have_subsurface {
let ss = Float4 {
v: [
rs.subsurface.uc,
rs.subsurface.u.0[0],
rs.subsurface.u.0[1],
0.0,
],
};
unsafe {
*self.rs_subsurface_packed.add(i) = ss;
}
}
}
}

5
shared/build.rs Normal file
View file

@ -0,0 +1,5 @@
fn main() {
// This allows "spirv" to be used in #[cfg(target_arch = "...")]
// without triggering a warning.
println!("cargo:rustc-check-cfg=cfg(target_arch, values(\"spirv\"))");
}

View file

@ -19,7 +19,6 @@ use crate::spectra::{
N_SPECTRUM_SAMPLES, RGBColorSpace, RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths,
StandardColorSpaces,
};
use crate::utils::DevicePtr;
use crate::utils::hash::hash_buffer;
use crate::utils::math::{
clamp, fast_exp, i0, lerp, log_i0, radians, safe_acos, safe_asin, safe_sqrt, sample_discrete,
@ -91,8 +90,8 @@ where
thickness: Float,
g: Float,
albedo: SampledSpectrum,
max_depth: usize,
n_samples: usize,
max_depth: u32,
n_samples: u32,
}
impl<T, B, const TWO_SIDED: bool> LayeredBxDF<T, B, TWO_SIDED>
@ -106,8 +105,8 @@ where
thickness: Float,
albedo: SampledSpectrum,
g: Float,
max_depth: usize,
n_samples: usize,
max_depth: u32,
n_samples: u32,
) -> Self {
Self {
top,

View file

@ -8,7 +8,7 @@ use crate::core::geometry::{
use crate::core::scattering::reflect;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::square;
use crate::utils::ptr::{DevicePtr, Slice};
use crate::utils::ptr::Ptr;
use crate::utils::sampling::{PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere};
use crate::{Float, INV_PI, PI, PI_OVER_2};
use core::any::Any;
@ -16,7 +16,7 @@ use core::any::Any;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MeasuredBxDFData {
pub wavelengths: Slice<Float>,
pub wavelengths: Ptr<Float>,
pub spectra: PiecewiseLinear2D<3>,
pub ndf: PiecewiseLinear2D<0>,
pub vndf: PiecewiseLinear2D<2>,
@ -28,7 +28,7 @@ pub struct MeasuredBxDFData {
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MeasuredBxDF {
pub brdf: DevicePtr<MeasuredBxDFData>,
pub brdf: Ptr<MeasuredBxDFData>,
pub lambda: SampledWavelengths,
}
@ -38,7 +38,7 @@ unsafe impl Sync for MeasuredBxDF {}
impl MeasuredBxDF {
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {
Self {
brdf: DevicePtr::from(brdf),
brdf: Ptr::from(brdf),
lambda: *lambda,
}
}

View file

@ -6,4 +6,4 @@ mod spherical;
pub use orthographic::OrthographicCamera;
pub use perspective::PerspectiveCamera;
pub use realistic::RealisticCamera;
pub use spherical::SphericalCamera;
pub use spherical::{SphericalCamera, Mapping};

View file

@ -11,6 +11,7 @@ use crate::core::pbrt::Float;
use crate::core::sampler::CameraSample;
use crate::core::scattering::refract;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::math::{lerp, quadratic, square};
#[repr(C)]
@ -37,8 +38,8 @@ pub struct RealisticCamera {
base: CameraBase,
focus_distance: Float,
set_aperture_diameter: Float,
aperture_image: *const DeviceImage,
element_interfaces: *const LensElementInterface,
aperture_image: Ptr<DeviceImage>,
element_interfaces: Ptr<LensElementInterface>,
n_elements: usize,
physical_extent: Bounds2f,
exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
@ -51,13 +52,13 @@ impl RealisticCamera {
lens_params: &[Float],
focus_distance: Float,
set_aperture_diameter: Float,
aperture_image: Option<DeviceImage>,
aperture_image: Ptr<DeviceImage>,
) -> Self {
let film_ptr = base.film;
if film_ptr.is_null() {
panic!("Camera must have a film");
}
let film = unsafe { &*film_ptr };
let film = &*film_ptr;
let aspect = film.full_resolution().x() as Float / film.full_resolution().y() as Float;
let diagonal = film.diagonal();
@ -90,7 +91,6 @@ impl RealisticCamera {
element_interface.push(el_int);
}
let n_samples = 64;
let half_diag = film.diagonal() / 2.0;
let mut exit_pupil_bounds = [Bounds2f::default(); EXIT_PUPIL_SAMPLES];
@ -107,7 +107,7 @@ impl RealisticCamera {
Self {
base,
focus_distance,
element_interfaces,
element_interfaces: Ptr::from(element_interfaces),
n_elements,
physical_extent,
set_aperture_diameter,
@ -123,14 +123,16 @@ impl RealisticCamera {
}
pub fn compute_thick_lens_approximation(&self) -> ([Float; 2], [Float; 2]) {
use crate::utils::Ptr;
let x = 0.001 * self.get_film().diagonal();
let r_scene = Ray::new(
Point3f::new(0., x, self.lens_front_z() + 1.),
Vector3f::new(0., 0., -1.),
None,
None,
&Ptr::null(),
);
let Some(r_film) = self.trace_lenses_from_film(r_scene) else {
let Some((_, r_film)) = self.trace_lenses_from_film(&r_scene) else {
panic!(
"Unable to trace ray from scene to film for thick lens approx. Is aperture very small?"
)
@ -140,14 +142,14 @@ impl RealisticCamera {
Point3f::new(x, 0., self.lens_rear_z() - 1.),
Vector3f::new(0., 0., 1.),
None,
None,
&Ptr::null(),
);
let Some(r_scene) = self.trace_lenses_from_film(r_film) else {
let Some((_, r_scene)) = self.trace_lenses_from_film(&r_film) else {
panic!(
"Unable to trace ray from scene to film for thick lens approx. Is aperture very small?"
)
};
let (pz1, f_1) = Self::compute_cardinal_points(r_film, r_scene);
let (pz1, fz1) = Self::compute_cardinal_points(r_film, r_scene);
([pz0, pz1], [fz0, fz1])
}
@ -155,19 +157,23 @@ impl RealisticCamera {
let (pz, fz) = self.compute_thick_lens_approximation();
let f = fz[0] - pz[0];
let z = -focus_distance;
let c = (pz[1] - z - pz[0]) * (pz[1] - z - 4 * f - pz[0]);
if c <= 0 {
let c = (pz[1] - z - pz[0]) * (pz[1] - z - 4. * f - pz[0]);
if c <= 0. {
panic!(
"Coefficient must be positive. It looks focusDistance {} is too short for a given lenses configuration",
focusDistance
focus_distance
);
}
let delta = (pz[1] - z + pz[0] - c.sqrt()) / 2.;
self.element_interface.last().thickness + delta
let last_interface = unsafe { self.element_interfaces.add(self.n_elements) };
last_interface.thickness + delta
}
pub fn bound_exit_pupil(&self, film_x_0: Float, film_x_1: Float) -> Bounds2f {
Self::compute_exit_pupil_bounds(&self.element_interface, film_x_0, film_x_1)
let interface_array = unsafe {
core::slice::from_raw_parts(self.element_interfaces.as_raw(), self.n_elements as usize)
};
Self::compute_exit_pupil_bounds(interface_array, film_x_0, film_x_1)
}
fn compute_exit_pupil_bounds(
@ -203,7 +209,10 @@ impl RealisticCamera {
// Expand pupil bounds if ray makes it through the lens system
if !pupil_bounds.contains(Point2f::new(p_rear.x(), p_rear.y()))
&& trace_lenses_from_film(Ray::new(p_film, p_rear - p_film, None, None), None)
&& trace_lenses_from_film(
Ray::new(p_film, p_rear - p_film, None, &Ptr::null()),
None,
)
{
pupil_bounds = pupil_bounds.union_point(Point2f::new(p_rear.x(), p_rear.y()));
}
@ -223,7 +232,7 @@ impl RealisticCamera {
pub fn sample_exit_pupil(&self, p_film: Point2f, u_lens: Point2f) -> Option<ExitPupilSample> {
// Find exit pupil bound for sample distance from film center
let film = self.film();
let film = self.get_film();
let r_film = (square(p_film.x()) + square(p_film.y())).sqrt();
let mut r_index = (r_film / (film.diagonal() / 2.)) as usize * self.exit_pupil_bounds.len();
r_index = (self.exit_pupil_bounds.len() - 1).min(r_index);
@ -265,11 +274,11 @@ impl RealisticCamera {
Point3f::new(r_camera.o.x(), r_camera.o.y(), -r_camera.o.z()),
Vector3f::new(r_camera.d.x(), r_camera.d.y(), -r_camera.d.z()),
Some(r_camera.time),
None,
&Ptr::null(),
);
for i in (0..self.element_interface.len() - 1).rev() {
let element: &LensElementInterface = &self.element_interface[i];
for i in (0..self.n_elements - 1).rev() {
let element: &LensElementInterface = unsafe { &self.element_interfaces.add(i) };
// Update ray from film accounting for interaction with _element_
element_z -= element.thickness;
@ -308,8 +317,9 @@ impl RealisticCamera {
// Update ray path for element interface interaction
if !is_stop {
let eta_i = element.eta;
let eta_t = if i > 0 && self.element_interface[i - 1].eta != 0. {
self.element_interface[i - 1].eta
let interface_i = unsafe { self.element_interfaces.add(i - 1) };
let eta_t = if i > 0 && interface_i.eta != 0. {
interface_i.eta
} else {
1.
};
@ -331,7 +341,7 @@ impl RealisticCamera {
Point3f::new(r_lens.o.x(), r_lens.o.y(), -r_lens.o.z()),
Vector3f::new(r_lens.d.x(), r_lens.d.y(), -r_lens.d.z()),
Some(r_lens.time),
None,
&Ptr::null(),
);
Some((weight, r_out))
@ -368,19 +378,22 @@ impl RealisticCamera {
}
pub fn lens_rear_z(&self) -> Float {
self.element_interface.last().unwrap().thickness
let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) };
last_interface.thickness
}
pub fn lens_front_z(&self) -> Float {
let mut z_sum = 0.;
for element in &self.element_interface {
for i in 0..self.n_elements {
let element = unsafe { self.element_interfaces.add(i) };
z_sum += element.thickness;
}
z_sum
}
pub fn rear_element_radius(&self) -> Float {
self.element_interface.last().unwrap().aperture_radius
let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) };
last_interface.aperture_radius
}
}
@ -406,7 +419,7 @@ impl CameraTrait for RealisticCamera {
let eps = self.sample_exit_pupil(Point2f::new(p_film.x(), p_film.y()), sample.p_lens)?;
let p_pupil = Point3f::new(0., 0., 0.);
let r_film = Ray::new(p_film, p_pupil - p_film, None, None);
let r_film = Ray::new(p_film, p_pupil - p_film, None, &Ptr::null());
let (weight, mut ray) = self.trace_lenses_from_film(&r_film)?;
if weight == 0. {
return None;

View file

@ -53,7 +53,7 @@ impl CameraTrait for SphericalCamera {
Point3f::new(0., 0., 0.),
dir,
Some(self.sample_time(sample.time)),
self.base().medium.clone(),
&self.base().medium,
);
Some(CameraRay {
ray: self.render_from_camera(&ray, &mut None),

View file

@ -2,17 +2,17 @@ use crate::Float;
use crate::core::bxdf::{BSDFSample, BxDF, BxDFFlags, BxDFTrait, FArgs, TransportMode};
use crate::core::geometry::{Frame, Normal3f, Point2f, Vector3f, VectorLike};
use crate::spectra::SampledSpectrum;
use crate::utils::DevicePtr;
use crate::utils::Ptr;
#[repr(C)]
#[derive(Copy, Debug, Default)]
#[derive(Copy, Clone, Debug, Default)]
pub struct BSDF {
bxdf: DevicePtr<BxDF>,
bxdf: Ptr<BxDF>,
shading_frame: Frame,
}
impl BSDF {
pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: DevicePtr<BxDF>) -> Self {
pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: Ptr<BxDF>) -> Self {
Self {
bxdf,
shading_frame: Frame::new(dpdus.normalize(), Vector3f::from(ns)),
@ -66,7 +66,7 @@ impl BSDF {
u2: Point2f,
f_args: FArgs,
) -> Option<BSDFSample> {
let bxdf = self.bxdf.as_ref()?;
let bxdf = unsafe { self.bxdf.as_ref() };
let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits());
let wo = self.render_to_local(wo_render);
@ -119,7 +119,8 @@ impl BSDF {
pub fn regularize(&mut self) {
if !self.bxdf.is_null() {
unsafe { self.bxdf.as_mut().regularize() }
let mut bxdf = unsafe { *self.bxdf.as_raw() };
bxdf.regularize();
}
}
}

View file

@ -1,4 +1,5 @@
use crate::core::bxdf::{BSDF, NormalizedFresnelBxDF};
use crate::bxdfs::NormalizedFresnelBxDF;
use crate::core::bsdf::BSDF;
use crate::core::geometry::{Frame, Normal3f, Point2f, Point3f, Point3fi, Vector3f};
use crate::core::interaction::{InteractionBase, ShadingGeom, SurfaceInteraction};
use crate::core::shape::Shape;
@ -6,7 +7,6 @@ use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
use crate::utils::Ptr;
use crate::utils::math::{catmull_rom_weights, square};
use crate::utils::sampling::sample_catmull_rom_2d;
use crate::utils::{Ptr, ptr::Slice};
use crate::{Float, PI};
use enum_dispatch::enum_dispatch;
use std::sync::Arc;
@ -105,30 +105,35 @@ pub struct BSSRDFTable {
impl BSSRDFTable {
pub fn get_rho(&self) -> &[Float] {
unsafe { core::slice::from_raw_parts(self.rho_samples.0, self.n_rho_samples as usize) }
unsafe {
core::slice::from_raw_parts(self.rho_samples.as_ref(), self.n_rho_samples as usize)
}
}
pub fn get_radius(&self) -> &[Float] {
unsafe {
core::slice::from_raw_parts(self.radius_samples.0, self.n_radius_samples as usize)
core::slice::from_raw_parts(
self.radius_samples.as_ref(),
self.n_radius_samples as usize,
)
}
}
pub fn get_profile(&self) -> &[Float] {
let n_profile = (self.n_rho_samples * self.n_radius_samples) as usize;
unsafe { core::slice::from_raw_parts(self.profile.0, n_profile) }
unsafe { core::slice::from_raw_parts(self.profile.as_ref(), n_profile) }
}
pub fn get_cdf(&self) -> &[Float] {
let n_profile = (self.n_rho_samples * self.n_radius_samples) as usize;
unsafe { core::slice::from_raw_parts(self.profile_cdf.0, n_profile) }
unsafe { core::slice::from_raw_parts(self.profile_cdf.as_ref(), n_profile) }
}
pub fn eval_profile(&self, rho_index: u32, radius_index: u32) -> Float {
debug_assert!(rho_index < self.n_rho_samples);
debug_assert!(radius_index < self.n_radius_samples);
let idx = (rho_index * self.n_radius_samples + radius_index) as usize;
unsafe { *self.profile.0.add(idx) }
unsafe { *self.profile.add(idx) }
}
}

View file

@ -10,7 +10,7 @@ use crate::core::pbrt::Float;
use crate::core::sampler::CameraSample;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::lerp;
use crate::utils::ptr::DevicePtr;
use crate::utils::ptr::Ptr;
use crate::utils::transform::{AnimatedTransform, Transform};
use enum_dispatch::enum_dispatch;
@ -113,8 +113,8 @@ pub struct CameraBase {
pub min_pos_differential_y: Vector3f,
pub min_dir_differential_x: Vector3f,
pub min_dir_differential_y: Vector3f,
pub film: DevicePtr<Film>,
pub medium: DevicePtr<Medium>,
pub film: Ptr<Film>,
pub medium: Ptr<Medium>,
}
#[enum_dispatch(CameraTrait)]
@ -163,9 +163,6 @@ pub trait CameraTrait {
sample: CameraSample,
lambda: &SampledWavelengths,
) -> Option<CameraRay> {
match self {
Camera::Orthographic(c) => c.generate_ray_differential(sample, lambda),
_ => {
let mut central_cam_ray = self.generate_ray(sample, lambda)?;
let mut rd = RayDifferential::default();
let mut rx_found = false;
@ -176,10 +173,10 @@ pub trait CameraTrait {
s_shift.p_film[0] += eps;
if let Some(rx_cam_ray) = self.generate_ray(s_shift, lambda) {
rd.rx_origin = central_cam_ray.ray.o
+ (rx_cam_ray.ray.o - central_cam_ray.ray.o) / eps;
rd.rx_direction = central_cam_ray.ray.d
+ (rx_cam_ray.ray.d - central_cam_ray.ray.d) / eps;
rd.rx_origin =
central_cam_ray.ray.o + (rx_cam_ray.ray.o - central_cam_ray.ray.o) / eps;
rd.rx_direction =
central_cam_ray.ray.d + (rx_cam_ray.ray.d - central_cam_ray.ray.d) / eps;
rx_found = true;
break;
}
@ -190,10 +187,10 @@ pub trait CameraTrait {
s_shift.p_film[1] += eps;
if let Some(ry_cam_ray) = self.generate_ray(s_shift, lambda) {
rd.ry_origin = central_cam_ray.ray.o
+ (ry_cam_ray.ray.o - central_cam_ray.ray.o) / eps;
rd.ry_direction = central_cam_ray.ray.d
+ (ry_cam_ray.ray.d - central_cam_ray.ray.d) / eps;
rd.ry_origin =
central_cam_ray.ray.o + (ry_cam_ray.ray.o - central_cam_ray.ray.o) / eps;
rd.ry_direction =
central_cam_ray.ray.d + (ry_cam_ray.ray.d - central_cam_ray.ray.d) / eps;
ry_found = true;
break;
}
@ -205,8 +202,6 @@ pub trait CameraTrait {
Some(central_cam_ray)
}
}
}
fn approximate_dp_dxy(
&self,
@ -232,13 +227,13 @@ pub trait CameraTrait {
Point3f::new(0., 0., 0.) + self.base().min_pos_differential_x,
Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_x,
None,
&DevicePtr::default(),
&Ptr::default(),
);
let y_ray = Ray::new(
Point3f::new(0., 0., 0.) + self.base().min_pos_differential_y,
Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_y,
None,
&DevicePtr::default(),
&Ptr::default(),
);
let n_down = Vector3f::from(n_down_z);
let tx = -(n_down.dot(y_ray.o.into())) / n_down.dot(x_ray.d);

View file

@ -27,8 +27,8 @@ impl From<(Float, Float, Float)> for XYZ {
}
impl From<[Float; 3]> for XYZ {
fn from(triplet: (Float, Float, Float)) -> Self {
XYZ::new(triplet.0, triplet.1, triplet.2)
fn from(triplet: [Float; 3]) -> Self {
XYZ::new(triplet[0], triplet[1], triplet[2])
}
}
@ -336,6 +336,30 @@ impl Index<u32> for RGB {
}
}
impl Index<i32> for RGB {
type Output = Float;
fn index(&self, index: i32) -> &Self::Output {
debug_assert!(index < 3);
match index {
0 => &self.r,
1 => &self.g,
_ => &self.b,
}
}
}
impl Index<usize> for RGB {
type Output = Float;
fn index(&self, index: usize) -> &Self::Output {
debug_assert!(index < 3);
match index {
0 => &self.r,
1 => &self.g,
_ => &self.b,
}
}
}
impl IndexMut<u32> for RGB {
fn index_mut(&mut self, index: u32) -> &mut Self::Output {
debug_assert!(index < 3);
@ -347,6 +371,28 @@ impl IndexMut<u32> for RGB {
}
}
impl IndexMut<i32> for RGB {
fn index_mut(&mut self, index: i32) -> &mut Self::Output {
debug_assert!(index < 3);
match index {
0 => &mut self.r,
1 => &mut self.g,
_ => &mut self.b,
}
}
}
impl IndexMut<usize> for RGB {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
debug_assert!(index < 3);
match index {
0 => &mut self.r,
1 => &mut self.g,
_ => &mut self.b,
}
}
}
impl Neg for RGB {
type Output = Self;
fn neg(self) -> Self {
@ -667,7 +713,7 @@ impl ColorEncodingTrait for SRGBEncoding {
fn to_linear(&self, vin: &[u8], vout: &mut [Float]) {
for (i, &v) in vin.iter().enumerate() {
vout[i] = SRGB_TO_LINEAR_LUT[v as u32];
vout[i] = SRGB_TO_LINEAR_LUT[v as usize];
}
}
@ -992,6 +1038,18 @@ impl Add for Coeffs {
}
}
impl Sub for Coeffs {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self {
Self {
c0: self.c0 - rhs.c0,
c1: self.c1 - rhs.c1,
c2: self.c2 - rhs.c2,
}
}
}
impl Mul<Float> for Coeffs {
type Output = Self;
#[inline(always)]
@ -1025,7 +1083,7 @@ impl RGBToSpectrumTable {
let m = rgb.max_component_value();
let min_val = rgb.min_component_value();
if m - min_val < 1e-4 {
let x = clamp(rgb[0], 1e-4, 0.9999);
let x: Float = clamp(rgb[0], 1e-4, 0.9999);
let c2 = (0.5 - x) / (x * (1.0 - x)).sqrt();
return RGBSigmoidPolynomial::new(0.0, 0.0, c2);
}

View file

@ -13,16 +13,15 @@ use crate::spectra::{
ConstantSpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, N_SPECTRUM_SAMPLES,
PiecewiseLinearSpectrum, RGBColorSpace, SampledSpectrum, SampledWavelengths, colorspace,
};
use crate::utils::AtomicFloat;
use crate::utils::containers::Array2D;
use crate::utils::math::linear_least_squares;
use crate::utils::math::{SquareMatrix, wrap_equal_area_square};
use crate::utils::ptr::DevicePtr;
use crate::utils::sampling::VarianceEstimator;
use crate::utils::transform::AnimatedTransform;
use crate::utils::{AtomicFloat, Ptr};
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
pub struct RGBFilm {
pub base: FilmBase,
pub max_component_value: Float,
@ -33,7 +32,7 @@ pub struct RGBFilm {
}
#[repr(C)]
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct RGBPixel {
rgb_sum: [AtomicFloat; 3],
weight_sum: AtomicFloat,
@ -108,7 +107,7 @@ impl RGBFilm {
_vi: Option<&VisibleSurface>,
weight: Float,
) {
let sensor = unsafe { self.get_sensor() };
let sensor = self.get_sensor();
let mut rgb = sensor.to_sensor_rgb(l, lambda);
let m = rgb.into_iter().copied().fold(f32::NEG_INFINITY, f32::max);
if m > self.max_component_value {
@ -117,13 +116,13 @@ impl RGBFilm {
let pixel = &self.pixels[p_film];
for c in 0..3 {
pixel.rgb_sum[c].add((weight * rgb[c]) as f64);
pixel.rgb_sum[c].add((weight * rgb[c as u32]) as f32);
}
pixel.weight_sum.add(weight as f64);
pixel.weight_sum.add(weight as f32);
}
pub fn add_splat(&mut self, p: Point2f, l: SampledSpectrum, lambda: &SampledWavelengths) {
let sensor = unsafe { self.get_sensor() };
let sensor = self.get_sensor();
let mut rgb = sensor.to_sensor_rgb(l, lambda);
let m = rgb.into_iter().copied().fold(f32::NEG_INFINITY, f32::max);
if m > self.max_component_value {
@ -148,32 +147,32 @@ impl RGBFilm {
if wt != 0. {
let pixel = &self.pixels[*pi];
for i in 0..3 {
pixel.rgb_splat[i].add((wt * rgb[i]) as f64);
pixel.rgb_splat[i].add((wt * rgb[i as u32]) as f32);
}
}
}
}
pub fn get_pixel_rgb(&self, p: Point2i, splat_scale: Option<Float>) -> RGB {
let pixel = unsafe { &self.pixels.get(p) };
let pixel = &self.pixels.get(p);
let mut rgb = RGB::new(
pixel.rgb_sum[0].load() as Float,
pixel.rgb_sum[1].load() as Float,
pixel.rgb_sum[2].load() as Float,
pixel.rgb_sum[0].get() as Float,
pixel.rgb_sum[1].get() as Float,
pixel.rgb_sum[2].get() as Float,
);
let weight_sum = pixel.weight_sum.load();
let weight_sum = pixel.weight_sum.get();
if weight_sum != 0. {
rgb /= weight_sum as Float
}
if let Some(splat) = splat_scale {
for c in 0..3 {
let splat_val = pixel.rgb_splat[c].load();
let splat_val = pixel.rgb_splat[c].get();
rgb[c] += splat * splat_val as Float / self.filter_integral;
}
} else {
for c in 0..3 {
let splat_val = pixel.rgb_splat[c].load();
let splat_val = pixel.rgb_splat[c].get();
rgb[c] += splat_val as Float / self.filter_integral;
}
}
@ -210,7 +209,7 @@ struct GBufferPixel {
}
#[repr(C)]
#[derive(Debug, Copy)]
#[derive(Debug)]
#[cfg_attr(target_os = "cuda", derive(Copy, Clone))]
pub struct GBufferFilm {
pub base: FilmBase,
@ -224,40 +223,6 @@ pub struct GBufferFilm {
output_rgbf_from_sensor_rgb: SquareMatrix<Float, 3>,
}
#[cfg(not(target_os = "cuda"))]
impl GBufferFilm {
pub fn new(
base: &FilmBase,
output_from_render: &AnimatedTransform,
apply_inverse: bool,
colorspace: &RGBColorSpace,
max_component_value: Float,
write_fp16: bool,
) -> Self {
assert!(!base.pixel_bounds.is_empty());
let sensor_ptr = base.sensor;
if sensor_ptr.is_null() {
panic!("Film must have a sensor");
}
let sensor = unsafe { &*sensor_ptr };
let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor.xyz_from_sensor_rgb;
let filter_integral = base.filter.integral();
let pixels = Array2D::new(base.pixel_bounds);
Self {
base: base.clone(),
output_from_render: output_from_render.clone(),
apply_inverse,
pixels,
colorspace: colorspace.clone(),
max_component_value,
write_fp16,
filter_integral,
output_rgbf_from_sensor_rgb,
}
}
}
impl GBufferFilm {
pub fn base(&self) -> &FilmBase {
&self.base
@ -279,8 +244,19 @@ impl GBufferFilm {
unsafe { &*self.base.sensor }
}
pub fn add_sample(
&mut self,
_p_film: Point2i,
_l: SampledSpectrum,
_lambda: &SampledWavelengths,
_visible_surface: Option<&VisibleSurface>,
_weight: Float,
) {
todo!()
}
pub fn add_splat(&mut self, p: Point2f, l: SampledSpectrum, lambda: &SampledWavelengths) {
let sensor = unsafe { self.get_sensor() };
let sensor = self.get_sensor();
let mut rgb = sensor.to_sensor_rgb(l, lambda);
let m = rgb.into_iter().copied().fold(f32::NEG_INFINITY, f32::max);
if m > self.max_component_value {
@ -305,7 +281,7 @@ impl GBufferFilm {
if wt != 0. {
let pixel = &self.pixels[*pi];
for i in 0..3 {
pixel.rgb_splat[i].add((wt * rgb[i]) as f64);
pixel.rgb_splat[i].add((wt * rgb[i]) as f32);
}
}
}
@ -318,25 +294,25 @@ impl GBufferFilm {
}
pub fn get_pixel_rgb(&self, p: Point2i, splat_scale: Option<Float>) -> RGB {
let pixel = unsafe { &self.pixels.get(p) };
let pixel = &self.pixels.get(p);
let mut rgb = RGB::new(
pixel.rgb_sum[0].load() as Float,
pixel.rgb_sum[1].load() as Float,
pixel.rgb_sum[2].load() as Float,
pixel.rgb_sum[0].get() as Float,
pixel.rgb_sum[1].get() as Float,
pixel.rgb_sum[2].get() as Float,
);
let weight_sum = pixel.weight_sum.load();
let weight_sum = pixel.weight_sum.get();
if weight_sum != 0. {
rgb /= weight_sum as Float
}
if let Some(splat) = splat_scale {
for c in 0..3 {
let splat_val = pixel.rgb_splat[c].load();
let splat_val = pixel.rgb_splat[c].get();
rgb[c] += splat * splat_val as Float / self.filter_integral;
}
} else {
for c in 0..3 {
let splat_val = pixel.rgb_splat[c].load();
let splat_val = pixel.rgb_splat[c].get();
rgb[c] += splat_val as Float / self.filter_integral;
}
}
@ -359,7 +335,7 @@ pub struct SpectralPixel {
}
#[repr(C)]
#[derive(Debug, Default)]
#[derive(Debug)]
#[cfg_attr(target_os = "cuda", derive(Copy, Clone))]
pub struct SpectralFilm {
pub base: FilmBase,
@ -389,6 +365,21 @@ impl SpectralFilm {
fn uses_visible_surface(&self) -> bool {
true
}
pub fn add_sample(
&mut self,
_p_film: Point2i,
_l: SampledSpectrum,
_lambda: &SampledWavelengths,
_visible_surface: Option<&VisibleSurface>,
_weight: Float,
) {
todo!()
}
pub fn add_splat(&mut self, _p: Point2f, _v: SampledSpectrum, _lambda: &SampledWavelengths) {
todo!()
}
}
#[repr(C)]
@ -402,102 +393,6 @@ pub struct PixelSensor {
}
impl PixelSensor {
const N_SWATCH_REFLECTANCES: usize = 24;
#[cfg(not(target_os = "cuda"))]
pub fn new(
r: Spectrum,
g: Spectrum,
b: Spectrum,
output_colorspace: RGBColorSpace,
sensor_illum: &Spectrum,
imaging_ratio: Float,
spectra: *const StandardSpectra,
swatches: &[Spectrum; 24],
) -> Self {
// As seen in usages of this constructos, sensor_illum can be null
// Going with the colorspace's own illuminant, but this might not be the right choice
// TODO: Test this
let illum: &Spectrum = match sensor_illum {
Some(arc_illum) => &**arc_illum,
None => &output_colorspace.illuminant,
};
let r_bar = DenselySampledSpectrum::from_spectrum(&r);
let g_bar = DenselySampledSpectrum::from_spectrum(&g);
let b_bar = DenselySampledSpectrum::from_spectrum(&b);
let mut rgb_camera = [[0.; 3]; Self::N_SWATCH_REFLECTANCES];
let swatches = Self::get_swatches();
for i in 0..Self::N_SWATCH_REFLECTANCES {
let rgb = Self::project_reflectance::<RGB>(
&swatches[i],
illum,
&Spectrum::DenselySampled(r_bar.clone()),
&Spectrum::DenselySampled(g_bar.clone()),
&Spectrum::DenselySampled(b_bar.clone()),
);
for c in 0..3 {
rgb_camera[i][c] = rgb[c];
}
}
let mut xyz_output = [[0.; 3]; Self::N_SWATCH_REFLECTANCES];
let sensor_white_g = illum.inner_product(&Spectrum::DenselySampled(g_bar.clone()));
let sensor_white_y = illum.inner_product(spectra.y);
for i in 0..Self::N_SWATCH_REFLECTANCES {
let s = swatches[i].clone();
let xyz = Self::project_reflectance::<XYZ>(
&s,
&output_colorspace.illuminant,
spectra.x,
spectra.y,
spectra.z,
) * (sensor_white_y / sensor_white_g);
for c in 0..3 {
xyz_output[i][c] = xyz[c];
}
}
let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output)?;
Ok(Self {
xyz_from_sensor_rgb,
r_bar,
g_bar,
b_bar,
imaging_ratio,
})
}
pub fn new_with_white_balance(
output_colorspace: &RGBColorSpace,
sensor_illum: DevicePtr<Spectrum>,
imaging_ratio: Float,
spectra: *const StandardSpectra,
) -> Self {
let r_bar = DenselySampledSpectrum::from_spectrum(spectra.x);
let g_bar = DenselySampledSpectrum::from_spectrum(spectra.y);
let b_bar = DenselySampledSpectrum::from_spectrum(spectra.z);
let xyz_from_sensor_rgb: SquareMatrix<Float, 3>;
if let Some(illum) = sensor_illum {
let source_white = illum.to_xyz(spectra).xy();
let target_white = output_colorspace.w;
xyz_from_sensor_rgb = white_balance(source_white, target_white);
} else {
xyz_from_sensor_rgb = SquareMatrix::<Float, 3>::default();
}
Self {
xyz_from_sensor_rgb,
r_bar,
g_bar,
b_bar,
imaging_ratio,
}
}
pub fn project_reflectance<T>(
refl: &Spectrum,
illum: &Spectrum,
@ -529,7 +424,7 @@ impl PixelSensor {
result[2] *= inv_g;
}
T::from((result[0], result[1], result[2]))
T::from([result[0], result[1], result[2]])
}
}
@ -609,7 +504,7 @@ impl Film {
}
pub fn add_sample(
&self,
&mut self,
p_film: Point2i,
l: SampledSpectrum,
lambda: &SampledWavelengths,

View file

@ -20,33 +20,6 @@ pub struct FilterSampler {
}
impl FilterSampler {
#[cfg(not(target_os = "cuda"))]
pub fn new<F>(radius: Vector2f, func: F) -> Self
where
F: Fn(Point2f) -> Float,
{
let domain = Bounds2f::from_points(
Point2f::new(-radius.x(), -radius.y()),
Point2f::new(radius.x(), radius.y()),
);
let nx = (32.0 * radius.x()) as usize;
let ny = (32.0 * radius.y()) as usize;
let mut f = Array2D::new_with_dims(nx, ny);
for y in 0..f.y_size() {
for x in 0..f.x_size() {
let p = domain.lerp(Point2f::new(
(x as Float + 0.5) / f.x_size() as Float,
(y as Float + 0.5) / f.y_size() as Float,
));
f[(x as i32, y as i32)] = func(p);
}
}
let distrib = DevicePiecewiseConstant2D::new_with_bounds(&f, domain);
Self { domain, f, distrib }
}
pub fn sample(&self, u: Point2f) -> FilterSample {
let (p, pdf, pi) = self.distrib.sample(u);
@ -54,7 +27,8 @@ impl FilterSampler {
return FilterSample { p, weight: 0.0 };
}
let weight = *self.f.get_linear(pi.x() as u32 + self.f.x_size()) / pdf;
let idx = pi.x() as u32 + self.f.x_size();
let weight = *self.f.get_linear(idx as usize) / pdf;
FilterSample { p, weight }
}
}

View file

@ -2,7 +2,7 @@ use super::{Normal3f, Point3f, Point3fi, Vector3f, VectorLike};
use crate::core::medium::Medium;
use crate::core::pbrt::Float;
use crate::utils::math::{next_float_down, next_float_up};
use crate::utils::ptr::DevicePtr;
use crate::utils::ptr::Ptr;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
@ -10,7 +10,7 @@ pub struct Ray {
pub o: Point3f,
pub d: Vector3f,
pub time: Float,
pub medium: DevicePtr<Medium>,
pub medium: Ptr<Medium>,
// We do this instead of creating a trait for Rayable or some gnarly thing like that
pub has_differentials: bool,
pub differential: RayDifferential,
@ -21,7 +21,7 @@ impl Default for Ray {
Self {
o: Point3f::new(0.0, 0.0, 0.0),
d: Vector3f::new(0.0, 0.0, 0.0),
medium: DevicePtr::null(),
medium: Ptr::null(),
time: 0.0,
has_differentials: false,
differential: RayDifferential::default(),
@ -35,7 +35,7 @@ impl Ray {
o,
d,
time: time.unwrap_or_else(|| Self::default().time),
medium: DevicePtr::from(medium),
medium: Ptr::from(medium),
..Self::default()
}
}
@ -71,7 +71,7 @@ impl Ray {
o: origin,
d,
time,
medium: DevicePtr::null(),
medium: Ptr::null(),
has_differentials: false,
differential: RayDifferential::default(),
}
@ -99,7 +99,7 @@ impl Ray {
o: pf,
d,
time,
medium: DevicePtr::null(),
medium: Ptr::null(),
has_differentials: false,
differential: RayDifferential::default(),
}

View file

@ -35,6 +35,16 @@ pub enum PixelFormat {
F32,
}
impl std::fmt::Display for PixelFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PixelFormat::U8 => write!(f, "U256"),
PixelFormat::F16 => write!(f, "Half"),
PixelFormat::F32 => write!(f, "Float"),
}
}
}
impl PixelFormat {
pub fn is_8bit(&self) -> bool {
matches!(self, PixelFormat::U8)

View file

@ -1,6 +1,8 @@
use crate::Float;
use crate::bxdfs::DiffuseBxDF;
use crate::core::bsdf::BSDF;
use crate::core::bssrdf::BSSRDF;
use crate::core::bxdf::{BSDF, BxDF, BxDFFlags, DiffuseBxDF};
use crate::core::bxdf::{BxDF, BxDFFlags};
use crate::core::camera::{Camera, CameraTrait};
use crate::core::geometry::{
Normal3f, Point2f, Point3f, Point3fi, Ray, RayDifferential, Vector3f, VectorLike,
@ -250,8 +252,8 @@ impl SurfaceInteraction {
}
pub fn compute_differentials(&mut self, r: &Ray, camera: &Camera, samples_per_pixel: i32) {
let computed = if !r.differential.is_null() {
let diff = unsafe { &*r.differential };
let computed = if !r.has_differentials {
let diff = r.differential;
let dot_rx = self.common.n.dot(diff.rx_direction.into());
let dot_ry = self.common.n.dot(diff.ry_direction.into());
@ -338,8 +340,8 @@ impl SurfaceInteraction {
let new_ray = Ray::spawn(&self.pi(), &self.n(), ray.time, ray.d);
ray.o = new_ray.o;
// Skipping other variables, since they should not change when passing through surface
if !ray.differential.is_null() {
let diff = unsafe { &mut *ray.differential };
if !ray.has_differentials {
let mut diff = ray.differential;
diff.rx_origin += diff.rx_direction * t;
diff.ry_origin += diff.ry_direction * t;
}
@ -357,12 +359,12 @@ impl SurfaceInteraction {
let material = {
let root_mat = self.material;
let mut active_mat: &Material = *root_mat;
let mut active_mat: &Material = &*root_mat;
let tex_eval = UniversalTextureEvaluator;
while let Material::Mix(mix) = active_mat {
// We need a context to evaluate the 'amount' texture
let ctx = MaterialEvalContext::from(&*self);
active_mat = mix.choose_material(&tex_eval, &ctx);
active_mat = mix.choose_material(&tex_eval, &ctx)?;
}
active_mat.clone()
};
@ -370,8 +372,8 @@ impl SurfaceInteraction {
let ctx = MaterialEvalContext::from(&*self);
let tex_eval = UniversalTextureEvaluator;
let displacement = material.get_displacement();
let normal_map = material.get_normal_map();
if displacement.is_some() || normal_map.is_some() {
let normal_map = Ptr::from(material.get_normal_map().unwrap());
if !displacement.is_null() || !normal_map.is_null() {
// This calls the function defined above
self.compute_bump_geom(&tex_eval, displacement, normal_map);
}
@ -380,7 +382,7 @@ impl SurfaceInteraction {
if get_options().force_diffuse {
let r = bsdf.rho_wo(self.common.wo, &[sampler.get1d()], &[sampler.get2d()]);
let diff_bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
bsdf = BSDF::new(self.shading.n, self.shading.dpdu, Some(diff_bxdf));
bsdf = BSDF::new(self.shading.n, self.shading.dpdu, Ptr::from(&diff_bxdf));
}
Some(bsdf)
}
@ -393,13 +395,12 @@ impl SurfaceInteraction {
_camera: &Camera,
) -> Option<BSSRDF> {
let material = {
let root_mat = self.material.as_deref()?;
let mut active_mat: &Material = root_mat;
let mut active_mat = unsafe { self.material.as_ref() };
let tex_eval = UniversalTextureEvaluator;
while let Material::Mix(mix) = active_mat {
// We need a context to evaluate the 'amount' texture
let ctx = MaterialEvalContext::from(self);
active_mat = mix.choose_material(&tex_eval, &ctx);
active_mat = mix.choose_material(&tex_eval, &ctx)?;
}
active_mat.clone()
};
@ -418,8 +419,9 @@ impl SurfaceInteraction {
let ctx = NormalBumpEvalContext::from(&*self);
let (dpdu, dpdv) = if !displacement.is_null() {
bump_map(tex_eval, &displacement, &ctx)
} else if let Some(map) = normal_image {
normal_map(map.as_ref(), &ctx)
} else if !normal_image.is_null() {
let map = unsafe { normal_image.as_ref() };
normal_map(map, &ctx)
} else {
(self.shading.dpdu, self.shading.dpdv)
};
@ -441,7 +443,8 @@ impl SurfaceInteraction {
) -> Ray {
let mut rd = self.spawn_ray(wi);
if let Some(diff_i) = &ray_i.differential {
if ray_i.has_differentials {
let diff_i = ray_i.differential;
let mut n = self.shading.n;
let mut dndx = self.shading.dndu * self.dudx + self.shading.dndv * self.dvdx;

View file

@ -17,7 +17,6 @@ use crate::spectra::{
};
use crate::utils::Transform;
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
use crate::utils::ptr::{DevicePtr, Ptr};
use crate::utils::sampling::DevicePiecewiseConstant2D;
use crate::{Float, PI};
use bitflags::bitflags;

View file

@ -19,7 +19,7 @@ use crate::core::texture::{
};
use crate::materials::*;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::utils::hash::hash_float;
use crate::utils::math::clamp;
@ -125,7 +125,7 @@ pub fn bump_map<T: TextureEvaluator>(
displacement: &GPUFloatTexture,
ctx: &NormalBumpEvalContext,
) -> (Vector3f, Vector3f) {
debug_assert!(tex_eval.can_evaluate(&[DevicePtr::from(displacement)], &[]));
debug_assert!(tex_eval.can_evaluate(&[Ptr::from(displacement)], &[]));
let mut du = 0.5 * (ctx.dudx.abs() + ctx.dudy.abs());
if du == 0.0 {
du = 0.0005;
@ -174,7 +174,7 @@ pub trait MaterialTrait {
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool;
fn get_normal_map(&self) -> Option<&DeviceImage>;
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture>;
fn get_displacement(&self) -> Ptr<GPUFloatTexture>;
fn has_subsurface_scattering(&self) -> bool;
}

View file

@ -12,7 +12,7 @@ use crate::spectra::{
};
use crate::utils::containers::SampledGrid;
use crate::utils::math::{clamp, square};
use crate::utils::ptr::DevicePtr;
use crate::utils::ptr::Ptr;
use crate::utils::rng::Rng;
use crate::utils::transform::Transform;
@ -94,22 +94,26 @@ impl PhaseFunctionTrait for HGPhaseFunction {
pub struct MajorantGrid {
pub bounds: Bounds3f,
pub res: Point3i,
pub voxels: *const Float,
pub voxels: *mut Float,
pub n_voxels: u32,
}
unsafe impl Send for MajorantGrid {}
unsafe impl Sync for MajorantGrid {}
impl MajorantGrid {
#[cfg(not(target_os = "cuda"))]
pub fn new(bounds: Bounds3f, res: Point3i) -> Self {
Self {
bounds,
res,
voxels: Vec::with_capacity((res.x() * res.y() * res.z()) as usize),
}
}
// #[cfg(not(target_os = "cuda"))]
// pub fn new(bounds: Bounds3f, res: Point3i) -> Self {
// let n_voxels = (res.x() * res.y() * res.z()) as usize;
// let voxels = Vec::with_capacity(n_voxels);
// Self {
// bounds,
// res,
// voxels: voxels.as_ptr(),
// n_voxels: n_voxels as u32,
// }
// }
//
#[inline(always)]
fn is_valid(&self) -> bool {
!self.voxels.is_null()
@ -123,7 +127,7 @@ impl MajorantGrid {
let idx = z * self.res.x() * self.res.y() + y * self.res.x() + x;
if idx >= 0 && (idx as usize) < self.voxels.len() {
if idx >= 0 && (idx as u32) < self.n_voxels {
unsafe { *self.voxels.add(idx as usize) }
} else {
0.0
@ -131,7 +135,7 @@ impl MajorantGrid {
}
#[inline(always)]
pub fn set(&self, x: i32, y: i32, z: i32, v: Float) {
pub fn set(&mut self, x: i32, y: i32, z: i32, v: Float) {
if !self.is_valid() {
return;
}
@ -264,7 +268,7 @@ impl DDAMajorantIterator {
let p_grid_start = grid.bounds.offset(&ray.at(t_min));
let grid_intersect = Vector3f::from(p_grid_start);
let res = [grid.res.x, grid.res.y, grid.res.z];
let res = [grid.res.x(), grid.res.y(), grid.res.z()];
for axis in 0..3 {
iter.voxel[axis] = clamp(
@ -449,36 +453,10 @@ pub enum Medium {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct HomogeneousMedium {
sigma_a_spec: DenselySampledSpectrum,
sigma_s_spec: DenselySampledSpectrum,
le_spec: DenselySampledSpectrum,
phase: HGPhaseFunction,
}
impl HomogeneousMedium {
pub fn new(
sigma_a: Spectrum,
sigma_s: Spectrum,
sigma_scale: Float,
le: Spectrum,
le_scale: Float,
g: Float,
) -> Self {
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
sigma_a_spec.scale(sigma_scale);
sigma_s_spec.scale(sigma_scale);
le_spec.scale(le_scale);
Self {
sigma_a_spec,
sigma_s_spec,
le_spec,
phase: HGPhaseFunction::new(g),
}
}
pub sigma_a_spec: DenselySampledSpectrum,
pub sigma_s_spec: DenselySampledSpectrum,
pub le_spec: DenselySampledSpectrum,
pub phase: HGPhaseFunction,
}
impl MediumTrait for HomogeneousMedium {
@ -517,70 +495,17 @@ impl MediumTrait for HomogeneousMedium {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GridMedium {
bounds: Bounds3f,
render_from_medium: Transform,
sigma_a_spec: DenselySampledSpectrum,
sigma_s_spec: DenselySampledSpectrum,
density_grid: SampledGrid<Float>,
phase: HGPhaseFunction,
temperature_grid: SampledGrid<Float>,
le_spec: DenselySampledSpectrum,
le_scale: SampledGrid<Float>,
is_emissive: bool,
majorant_grid: MajorantGrid,
}
impl GridMedium {
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_os = "cuda"))]
pub fn new(
bounds: &Bounds3f,
render_from_medium: &Transform,
sigma_a: &Spectrum,
sigma_s: &Spectrum,
sigma_scale: Float,
g: Float,
density_grid: SampledGrid<Float>,
temperature_grid: SampledGrid<Float>,
le: &Spectrum,
le_scale: SampledGrid<Float>,
) -> Self {
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(sigma_a);
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(sigma_s);
let le_spec = DenselySampledSpectrum::from_spectrum(le);
sigma_a_spec.scale(sigma_scale);
sigma_s_spec.scale(sigma_scale);
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
let is_emissive = if temperature_grid.is_some() {
true
} else {
le_spec.max_value() > 0.
};
for z in 0..majorant_grid.res.z() {
for y in 0..majorant_grid.res.y() {
for x in 0..majorant_grid.res.x() {
let bounds = majorant_grid.voxel_bounds(x, y, z);
majorant_grid.set(x, y, z, density_grid.max_value(bounds));
}
}
}
Self {
bounds: *bounds,
render_from_medium: *render_from_medium,
sigma_a_spec,
sigma_s_spec,
density_grid,
phase: HGPhaseFunction::new(g),
temperature_grid,
le_spec,
le_scale,
is_emissive,
majorant_grid,
}
}
pub bounds: Bounds3f,
pub render_from_medium: Transform,
pub sigma_a_spec: DenselySampledSpectrum,
pub sigma_s_spec: DenselySampledSpectrum,
pub density_grid: SampledGrid<Float>,
pub phase: HGPhaseFunction,
pub temperature_grid: SampledGrid<Float>,
pub le_spec: DenselySampledSpectrum,
pub le_scale: SampledGrid<Float>,
pub is_emissive: bool,
pub majorant_grid: MajorantGrid,
}
impl MediumTrait for GridMedium {
@ -603,12 +528,11 @@ impl MediumTrait for GridMedium {
};
let le = if scale > 0.0 {
let raw_emission = match &self.temperature_grid {
Some(grid) => {
let temp = grid.lookup(p);
let raw_emission = if self.temperature_grid.is_valid() {
let temp = self.temperature_grid.lookup(p);
BlackbodySpectrum::new(temp).sample(lambda)
}
None => self.le_spec.sample(lambda),
} else {
self.le_spec.sample(lambda)
};
raw_emission * scale
@ -664,59 +588,15 @@ impl MediumTrait for GridMedium {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RGBGridMedium {
bounds: Bounds3f,
render_from_medium: Transform,
phase: HGPhaseFunction,
le_scale: Float,
sigma_scale: Float,
sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
le_grid: SampledGrid<RGBIlluminantSpectrum>,
majorant_grid: MajorantGrid,
}
impl RGBGridMedium {
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_os = "cuda"))]
pub fn new(
bounds: &Bounds3f,
render_from_medium: &Transform,
g: Float,
sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_scale: Float,
le_grid: SampledGrid<RGBIlluminantSpectrum>,
le_scale: Float,
) -> Self {
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
for z in 0..majorant_grid.res.x() {
for y in 0..majorant_grid.res.y() {
for x in 0..majorant_grid.res.x() {
let bounds = majorant_grid.voxel_bounds(x, y, z);
let convert = |s: &RGBUnboundedSpectrum| s.max_value();
let max_sigma_t = sigma_a_grid
.as_ref()
.map_or(1.0, |g| g.max_value_convert(bounds, convert))
+ sigma_s_grid
.as_ref()
.map_or(1.0, |g| g.max_value_convert(bounds, convert));
majorant_grid.set(x, y, z, sigma_scale * max_sigma_t);
}
}
}
Self {
bounds: *bounds,
render_from_medium: *render_from_medium,
le_grid,
le_scale,
phase: HGPhaseFunction::new(g),
sigma_a_grid,
sigma_s_grid,
sigma_scale,
majorant_grid,
}
}
pub bounds: Bounds3f,
pub render_from_medium: Transform,
pub phase: HGPhaseFunction,
pub le_scale: Float,
pub sigma_scale: Float,
pub sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
pub sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
pub le_grid: SampledGrid<RGBIlluminantSpectrum>,
pub majorant_grid: MajorantGrid,
}
impl MediumTrait for RGBGridMedium {
@ -789,7 +669,8 @@ impl MediumTrait for RGBGridMedium {
}
}
#[derive(Debug, Clone)]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CloudMedium;
impl MediumTrait for CloudMedium {
fn is_emissive(&self) -> bool {
@ -807,7 +688,9 @@ impl MediumTrait for CloudMedium {
todo!()
}
}
#[derive(Debug, Clone)]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct NanoVDBMedium;
impl MediumTrait for NanoVDBMedium {
fn is_emissive(&self) -> bool {
@ -829,8 +712,8 @@ impl MediumTrait for NanoVDBMedium {
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MediumInterface {
pub inside: DevicePtr<Medium>,
pub outside: DevicePtr<Medium>,
pub inside: Ptr<Medium>,
pub outside: Ptr<Medium>,
}
unsafe impl Send for MediumInterface {}
@ -839,8 +722,8 @@ unsafe impl Sync for MediumInterface {}
impl Default for MediumInterface {
fn default() -> Self {
Self {
inside: DevicePtr::null(),
outside: DevicePtr::null(),
inside: Ptr::null(),
outside: Ptr::null(),
}
}
}
@ -848,8 +731,8 @@ impl Default for MediumInterface {
impl From<Medium> for MediumInterface {
fn from(medium: Medium) -> Self {
Self {
inside: DevicePtr::from(&medium),
outside: DevicePtr::from(&medium),
inside: Ptr::from(&medium),
outside: Ptr::from(&medium),
}
}
}
@ -863,8 +746,8 @@ impl From<&Medium> for MediumInterface {
impl MediumInterface {
pub fn new(inside: &Medium, outside: &Medium) -> Self {
Self {
inside: DevicePtr::from(inside),
outside: DevicePtr::from(outside),
inside: Ptr::from(inside),
outside: Ptr::from(outside),
}
}
@ -873,6 +756,6 @@ impl MediumInterface {
}
pub fn is_medium_transition(&self) -> bool {
self.inside.0 != self.outside.0
self.inside != self.outside
}
}

View file

@ -1,4 +1,3 @@
use crate::core::aggregates::LinearBVHNode;
use crate::core::geometry::{Bounds3f, Ray};
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
use crate::core::light::Light;
@ -24,11 +23,11 @@ pub trait PrimitiveTrait {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GeometricPrimitive {
shape: *const Shape,
material: *const Material,
area_light: *const Light,
shape: Ptr<Shape>,
material: Ptr<Material>,
area_light: Ptr<Light>,
medium_interface: MediumInterface,
alpha: *const GPUFloatTexture,
alpha: Ptr<GPUFloatTexture>,
}
unsafe impl Send for GeometricPrimitive {}
@ -41,7 +40,8 @@ impl PrimitiveTrait for GeometricPrimitive {
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let mut si = self.shape.intersect(r, t_max)?;
if let Some(ref alpha) = self.alpha {
if !self.alpha.is_null() {
let alpha = unsafe { &self.alpha.as_ref() };
let ctx = TextureEvalContext::from(&si.intr);
let a = alpha.evaluate(&ctx);
if a < 1.0 {
@ -66,18 +66,21 @@ impl PrimitiveTrait for GeometricPrimitive {
}
}
if r.medium.is_null() {
return None;
}
si.set_intersection_properties(
self.material.clone(),
self.area_light.clone(),
Some(self.medium_interface.clone()),
Some(r.medium.clone().expect("Medium not set")),
self.material,
self.area_light,
self.medium_interface.clone(),
r.medium,
);
Some(si)
}
fn intersect_p(&self, r: &Ray, t_max: Option<Float>) -> bool {
if self.alpha.is_some() {
if !self.alpha.is_null() {
self.intersect(r, t_max).is_some()
} else {
self.shape.intersect_p(r, t_max)
@ -160,32 +163,38 @@ impl PrimitiveTrait for AnimatedPrimitive {
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct LinearBVHNode {
bounds: Bounds3f,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct BVHAggregatePrimitive {
max_prims_in_node: u32,
primitives: *const Ptr<Primitive>,
nodes: *const LinearBVHNode,
nodes: Ptr<LinearBVHNode>,
}
impl PrimitiveTrait for BVHAggregatePrimitive {
fn bounds(&self) -> Bounds3f {
if !self.nodes.is_empty() {
self.nodes[0].bounds
if !self.nodes.is_null() {
self.nodes.bounds
} else {
Bounds3f::default()
}
}
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
if self.nodes.is_empty() {
if !self.nodes.is_null() {
return None;
}
self.intersect(r, t_max)
}
fn intersect_p(&self, r: &Ray, t_max: Option<Float>) -> bool {
if self.nodes.is_empty() {
if !self.nodes.is_null() {
return false;
}
self.intersect_p(r, t_max)

View file

@ -6,10 +6,9 @@ use crate::utils::Ptr;
use crate::utils::containers::Array2D;
use crate::utils::math::{
BinaryPermuteScrambler, DigitPermutation, FastOwenScrambler, NoRandomizer, OwenScrambler,
PRIME_TABLE_SIZE, Scrambler, clamp, compute_radical_inverse_permutations, encode_morton_2,
inverse_radical_inverse, lerp, log2_int, owen_scrambled_radical_inverse, permutation_element,
radical_inverse, round_up_pow2, scrambled_radical_inverse, sobol_interval_to_index,
sobol_sample,
PRIME_TABLE_SIZE, Scrambler, clamp, encode_morton_2, inverse_radical_inverse, lerp, log2_int,
owen_scrambled_radical_inverse, permutation_element, radical_inverse, round_up_pow2,
scrambled_radical_inverse, sobol_interval_to_index, sobol_sample,
};
use crate::utils::rng::Rng;
use crate::utils::sobol::N_SOBOL_DIMENSIONS;
@ -107,62 +106,59 @@ pub struct HaltonSampler {
}
impl HaltonSampler {
pub fn new(
samples_per_pixel: u32,
full_res: Point2i,
randomize: RandomizeStrategy,
seed: u64,
) -> Self {
let digit_permutations = compute_radical_inverse_permutations(seed);
let mut base_scales = [0u64; 2];
let mut base_exponents = [0u64; 2];
let bases = [2, 3];
let res_coords = [full_res.x(), full_res.y()];
for i in 0..2 {
let base = bases[i] as u64;
let mut scale = 1u64;
let mut exp = 0u64;
let limit = std::cmp::min(res_coords[i], MAX_HALTON_RESOLUTION) as u64;
while scale < limit {
scale *= base;
exp += 1;
}
base_scales[i] = scale;
base_exponents[i] = exp;
}
let mut mult_inverse = [0u64; 2];
mult_inverse[0] =
Self::multiplicative_inverse(base_scales[0] as i64, base_scales[0] as i64);
mult_inverse[1] =
Self::multiplicative_inverse(base_scales[1] as i64, base_scales[1] as i64);
Self {
samples_per_pixel,
randomize,
digit_permutations,
base_scales,
base_exponents,
mult_inverse,
halton_index: 0,
dim: 0,
}
}
// pub fn new(
// samples_per_pixel: u32,
// full_res: Point2i,
// randomize: RandomizeStrategy,
// seed: u64,
// ) -> Self {
// let digit_permutations = compute_radical_inverse_permutations(seed);
// let mut base_scales = [0u64; 2];
// let mut base_exponents = [0u64; 2];
// let bases = [2, 3];
// let res_coords = [full_res.x(), full_res.y()];
//
// for i in 0..2 {
// let base = bases[i] as u64;
// let mut scale = 1u64;
// let mut exp = 0u64;
//
// let limit = std::cmp::min(res_coords[i], MAX_HALTON_RESOLUTION) as u64;
//
// while scale < limit {
// scale *= base;
// exp += 1;
// }
//
// base_scales[i] = scale;
// base_exponents[i] = exp;
// }
//
// let mut mult_inverse = [0u64; 2];
//
// mult_inverse[0] =
// Self::multiplicative_inverse(base_scales[0] as i64, base_scales[0] as i64);
// mult_inverse[1] =
// Self::multiplicative_inverse(base_scales[1] as i64, base_scales[1] as i64);
//
// Self {
// samples_per_pixel,
// randomize,
// digit_permutations,
// base_scales,
// base_exponents,
// mult_inverse,
// halton_index: 0,
// dim: 0,
// }
// }
fn sample_dimension(&self, dimension: u32) -> Float {
if self.randomize == RandomizeStrategy::None {
radical_inverse(dimension, self.halton_index)
} else if self.randomize == RandomizeStrategy::PermuteDigits {
scrambled_radical_inverse(
dimension,
self.halton_index,
&self.digit_permutations[dimension as usize],
)
let digit_perm = unsafe { &*self.digit_permutations.add(dimension as usize) };
scrambled_radical_inverse(dimension, self.halton_index, digit_perm)
} else {
owen_scrambled_radical_inverse(
dimension,
@ -418,13 +414,13 @@ impl SamplerTrait for PaddedSobolSampler {
self.dim as u64,
self.seed,
];
let hash = hash_buffer(&hash_input, 0) as u32;
let hash = hash_buffer(&hash_input, 0);
let index = permutation_element(
self.sample_index as u32,
self.samples_per_pixel as u32,
hash,
hash as u32,
);
self.sample_dimension(0, index, hash >> 32)
self.sample_dimension(0, index, (hash >> 32) as u32)
}
fn get2d(&mut self) -> Point2f {
let hash_input = [
@ -433,16 +429,16 @@ impl SamplerTrait for PaddedSobolSampler {
self.dim as u64,
self.seed,
];
let hash = hash_buffer(&hash_input, 0) as u32;
let hash = hash_buffer(&hash_input, 0);
let index = permutation_element(
self.sample_index as u32,
self.samples_per_pixel as u32,
hash,
hash as u32,
);
self.dim += 2;
Point2f::new(
self.sample_dimension(0, index, hash),
self.sample_dimension(1, index, hash >> 32),
self.sample_dimension(0, index, hash as u32),
self.sample_dimension(1, index, (hash >> 32) as u32),
)
}
@ -630,7 +626,7 @@ impl ZSobolSampler {
let mix_input = higher_digits ^ (0x55555555 * self.dim as u64);
let p = (mix_bits(mix_input) >> 24) % 24;
digit = PERMUTATIONS[p as u32][digit as u32] as u64;
digit = PERMUTATIONS[p as usize][digit as usize] as u64;
sample_index |= digit << digit_shift;
}

View file

@ -1,6 +1,6 @@
use crate::core::geometry::{
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
Vector3fi, VectorLike,
Vector3fi, VectorLike, ray,
};
use crate::core::interaction::{
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
@ -10,7 +10,7 @@ use crate::core::material::Material;
use crate::core::medium::{Medium, MediumInterface};
use crate::shapes::*;
use crate::utils::math::{next_float_down, next_float_up};
use crate::utils::{DevicePtr, Transform};
use crate::utils::{Ptr, Transform};
use crate::{Float, PI};
use enum_dispatch::enum_dispatch;
@ -37,13 +37,13 @@ impl ShapeIntersection {
pub fn set_intersection_properties(
&mut self,
mtl: &Material,
area: &Light,
mtl: Ptr<Material>,
area: Ptr<Light>,
prim_medium_interface: MediumInterface,
ray_medium: &Medium,
ray_medium: Ptr<Medium>,
) {
self.intr
.set_intersection_properties(mtl, area, ray_medium, prim_medium_interface);
.set_intersection_properties(&mtl, &area, &ray_medium, prim_medium_interface);
}
}
@ -118,12 +118,7 @@ impl ShapeSampleContext {
}
pub fn spawn_ray(&self, w: Vector3f) -> Ray {
Ray::new(
self.offset_ray_origin(w),
w,
Some(self.time),
&DevicePtr::null(),
)
Ray::new(self.offset_ray_origin(w), w, Some(self.time), &Ptr::null())
}
}

View file

@ -1,8 +1,9 @@
use crate::Float;
use crate::core::color::{RGB, XYZ};
use crate::spectra::*;
use enum_dispatch::enum_dispatch;
pub use crate::spectra::*;
#[enum_dispatch]
pub trait SpectrumTrait: Copy {
fn evaluate(&self, lambda: Float) -> Float;

View file

@ -8,13 +8,15 @@ use crate::spectra::{
RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
SampledWavelengths,
};
use crate::textures::*;
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::utils::Transform;
use crate::utils::math::square;
use crate::{Float, INV_2_PI, INV_PI, PI};
use enum_dispatch::enum_dispatch;
pub use crate::textures::*;
#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct TexCoord2D {
@ -428,8 +430,8 @@ pub trait TextureEvaluator: Send + Sync {
fn can_evaluate(
&self,
_ftex: &[DevicePtr<GPUFloatTexture>],
_stex: &[DevicePtr<GPUSpectrumTexture>],
_ftex: &[Ptr<GPUFloatTexture>],
_stex: &[Ptr<GPUSpectrumTexture>],
) -> bool;
}
@ -453,8 +455,8 @@ impl TextureEvaluator for UniversalTextureEvaluator {
fn can_evaluate(
&self,
_float_textures: &[DevicePtr<GPUFloatTexture>],
_spectrum_textures: &[DevicePtr<GPUSpectrumTexture>],
_float_textures: &[Ptr<GPUFloatTexture>],
_spectrum_textures: &[Ptr<GPUSpectrumTexture>],
) -> bool {
true
}

View file

@ -13,27 +13,6 @@ pub struct GaussianFilter {
pub sampler: FilterSampler,
}
impl GaussianFilter {
pub fn new(radius: Vector2f, sigma: Float) -> Self {
let exp_x = gaussian(radius.x(), 0., sigma);
let exp_y = gaussian(radius.y(), 0., sigma);
let sampler = FilterSampler::new(radius, move |p: Point2f| {
let gx = (gaussian(p.x(), 0., sigma) - exp_x).max(0.0);
let gy = (gaussian(p.y(), 0., sigma) - exp_y).max(0.0);
gx * gy
});
Self {
radius,
sigma,
exp_x: gaussian(radius.x(), 0., sigma),
exp_y: gaussian(radius.y(), 0., sigma),
sampler,
}
}
}
impl FilterTrait for GaussianFilter {
fn radius(&self) -> Vector2f {
self.radius

View file

@ -12,20 +12,6 @@ pub struct LanczosSincFilter {
pub sampler: FilterSampler,
}
impl LanczosSincFilter {
pub fn new(radius: Vector2f, tau: Float) -> Self {
let sampler = FilterSampler::new(radius, move |p: Point2f| {
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
});
Self {
radius,
tau,
sampler,
}
}
}
impl FilterTrait for LanczosSincFilter {
fn radius(&self) -> Vector2f {
self.radius

View file

@ -12,22 +12,7 @@ pub struct MitchellFilter {
}
impl MitchellFilter {
pub fn new(radius: Vector2f, b: Float, c: Float) -> Self {
let sampler = FilterSampler::new(radius, move |p: Point2f| {
let nx = 2.0 * p.x() / radius.x();
let ny = 2.0 * p.y() / radius.y();
Self::mitchell_1d_eval(b, c, nx) * Self::mitchell_1d_eval(b, c, ny)
});
Self {
radius,
b,
c,
sampler,
}
}
fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
let x = x.abs();
if x <= 1.0 {
((12.0 - 9.0 * b - 6.0 * c) * x.powi(3)

View file

@ -125,10 +125,8 @@ impl LightTrait for DiffuseAreaLight {
rgb[c] = self.image.bilerp_channel(uv, c as i32);
}
let spec = RGBIlluminantSpectrum::new(
self.image_color_space.as_ref().unwrap(),
rgb.clamp_zero(),
);
let cs_ref = unsafe { self.colorspace.as_ref() };
let spec = RGBIlluminantSpectrum::new(cs_ref, rgb.clamp_zero());
self.scale * spec.sample(lambda)
} else {
@ -150,11 +148,9 @@ impl LightTrait for DiffuseAreaLight {
for c in 0..3 {
rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32);
}
l += RGBIlluminantSpectrum::new(
self.image_color_space.as_ref().unwrap(),
rgb.clamp_zero(),
)
.sample(&lambda);
let cs_ref = unsafe { self.colorspace.as_ref() };
l += RGBIlluminantSpectrum::new(cs_ref, rgb.clamp_zero()).sample(&lambda);
}
}
l *= self.scale / (self.image.resolution().x() * self.image.resolution().y()) as Float;

View file

@ -214,8 +214,8 @@ impl LightTrait for ImageInfiniteLight {
#[cfg(not(target_os = "cuda"))]
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
let mut sum_l = SampledSpectrum::new(0.);
let width = self.image.resolution.x();
let height = self.image.resolution.y();
let width = self.image.resolution().x();
let height = self.image.resolution().y();
for v in 0..height {
for u in 0..width {
let mut rgb = RGB::default();

View file

@ -3,7 +3,7 @@ use crate::core::color::RGB;
use crate::core::geometry::{
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta,
};
use crate::core::image::DeviceImage;
use crate::core::image::{DeviceImage, ImageAccess};
use crate::core::light::{
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
};
@ -91,11 +91,12 @@ impl LightTrait for ProjectionLight {
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
let mut sum = SampledSpectrum::new(0.);
for y in 0..self.image.resolution.y() {
for x in 0..self.image.resolution.x() {
let res = self.image.resolution();
for y in 0..res.y() {
for x in 0..res.x() {
let ps = self.screen_bounds.lerp(Point2f::new(
(x as Float + 0.5) / self.image.resolution.x() as Float,
(y as Float + 0.5) / self.image.resolution.y() as Float,
(x as Float + 0.5) / res.x() as Float,
(y as Float + 0.5) / res.y() as Float,
));
let w_raw = Vector3f::from(self.light_from_screen.apply_to_point(Point3f::new(
ps.x(),
@ -113,7 +114,7 @@ impl LightTrait for ProjectionLight {
sum += s.sample(&lambda) * dwda;
}
}
self.scale * self.a * sum / (self.image.resolution.x() * self.image.resolution.y()) as Float
self.scale * self.a * sum / (res.x() * res.y()) as Float
}
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {

View file

@ -6,7 +6,7 @@ use crate::core::light::{LightBounds, LightSampleContext};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::{clamp, lerp, sample_discrete};
use crate::utils::math::{safe_sqrt, square};
use crate::utils::ptr::{DevicePtr, Slice};
use crate::utils::ptr::Ptr;
use crate::utils::sampling::AliasTable;
use crate::{Float, ONE_MINUS_EPSILON, PI};
use enum_dispatch::enum_dispatch;
@ -154,14 +154,14 @@ impl CompactLightBounds {
#[derive(Debug, Clone)]
pub struct SampledLight {
pub light: DevicePtr<Light>,
pub light: Ptr<Light>,
pub p: Float,
}
impl SampledLight {
pub fn new(light: Light, p: Float) -> Self {
Self {
light: DevicePtr::from(&light),
light: Ptr::from(&light),
p,
}
}
@ -214,7 +214,7 @@ impl LightSamplerTrait for UniformLightSampler {
let light_index = (u as u32 * self.lights_len).min(self.lights_len - 1) as usize;
Some(SampledLight {
light: DevicePtr::from(&self.light(light_index)),
light: Ptr::from(&self.light(light_index)),
p: 1. / self.lights_len as Float,
})
}
@ -236,7 +236,7 @@ pub struct Alias {
#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct PowerLightSampler {
pub lights: Slice<Light>,
pub lights: Ptr<Light>,
pub lights_len: u32,
pub alias_table: AliasTable,
}
@ -260,28 +260,24 @@ impl LightSamplerTrait for PowerLightSampler {
let (light_index, pmf, _) = self.alias_table.sample(u);
let light_ref = &self.lights[light_index as usize];
let light_ref = unsafe { self.lights.add(light_index as usize) };
Some(SampledLight {
light: DevicePtr::from(light_ref),
light: light_ref,
p: pmf,
})
}
fn pmf(&self, light: &Light) -> Float {
if self.lights_len == 0 {
return 0.0;
}
let array_start = self.lights.as_raw();
let target = light as *const Light as *mut Light;
let light_ptr = light as *const Light;
let start = self.lights.as_ptr();
unsafe {
let index = target.offset_from(array_start);
let end = unsafe { start.add(self.lights.len as usize) };
if light_ptr >= start && light_ptr < end {
let index = unsafe { light_ptr.offset_from(start) };
if index >= 0 && index < self.lights_len as isize {
return self.alias_table.pmf(index as u32);
}
}
0.
}
}

View file

@ -25,38 +25,37 @@ pub struct CoatedDiffuseMaterial {
pub thickness: Ptr<GPUFloatTexture>,
pub g: Ptr<GPUFloatTexture>,
pub eta: Ptr<Spectrum>,
pub max_depth: u32,
pub n_samples: u32,
pub remap_roughness: bool,
pub max_depth: usize,
pub n_samples: usize,
}
impl CoatedDiffuseMaterial {
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_os = "cuda"))]
pub fn new(
reflectance: &GPUSpectrumTexture,
u_roughness: &GPUFloatTexture,
v_roughness: &GPUFloatTexture,
thickness: &GPUFloatTexture,
albedo: &GPUSpectrumTexture,
g: &GPUFloatTexture,
eta: &Spectrum,
displacement: &GPUFloatTexture,
normal_map: &DeviceImage,
reflectance: Ptr<GPUSpectrumTexture>,
u_roughness: Ptr<GPUFloatTexture>,
v_roughness: Ptr<GPUFloatTexture>,
thickness: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>,
g: Ptr<GPUFloatTexture>,
eta: Ptr<Spectrum>,
displacement: Ptr<GPUFloatTexture>,
normal_map: Ptr<DeviceImage>,
remap_roughness: bool,
max_depth: usize,
n_samples: usize,
max_depth: u32,
n_samples: u32,
) -> Self {
Self {
displacement: Ptr::from(displacement),
normal_map: Ptr::from(normal_map),
reflectance: Ptr::from(reflectance),
albedo: Ptr::from(albedo),
u_roughness: Ptr::from(u_roughness),
v_roughness: Ptr::from(v_roughness),
thickness: Ptr::from(thickness),
g: Ptr::from(g),
eta: Ptr::from(eta),
displacement,
normal_map,
reflectance,
albedo,
u_roughness,
v_roughness,
thickness,
g,
eta,
remap_roughness,
max_depth,
n_samples,
@ -163,46 +162,45 @@ pub struct CoatedConductorMaterial {
conductor_eta: Ptr<GPUSpectrumTexture>,
k: Ptr<GPUSpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>,
remap_roughness: bool,
max_depth: u32,
n_samples: u32,
remap_roughness: bool,
}
impl CoatedConductorMaterial {
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_os = "cuda"))]
pub fn new(
normal_map: &DeviceImage,
displacement: &GPUFloatTexture,
interface_uroughness: &GPUFloatTexture,
interface_vroughness: &GPUFloatTexture,
thickness: &GPUFloatTexture,
interface_eta: &Spectrum,
g: &GPUFloatTexture,
albedo: &GPUSpectrumTexture,
conductor_uroughness: &GPUFloatTexture,
conductor_vroughness: &GPUFloatTexture,
conductor_eta: &GPUSpectrumTexture,
k: &GPUSpectrumTexture,
reflectance: &GPUSpectrumTexture,
remap_roughness: bool,
normal_map: Ptr<DeviceImage>,
displacement: Ptr<GPUFloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>,
thickness: Ptr<GPUFloatTexture>,
interface_eta: Ptr<Spectrum>,
g: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>,
conductor_uroughness: Ptr<GPUFloatTexture>,
conductor_vroughness: Ptr<GPUFloatTexture>,
conductor_eta: Ptr<GPUSpectrumTexture>,
k: Ptr<GPUSpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>,
max_depth: u32,
n_samples: u32,
remap_roughness: bool,
) -> Self {
Self {
displacement: Ptr::from(displacement),
normal_map: Ptr::from(normal_map),
interface_uroughness: Ptr::from(interface_uroughness),
interface_vroughness: Ptr::from(interface_vroughness),
thickness: Ptr::from(thickness),
interface_eta: Ptr::from(interface_eta),
g: Ptr::from(g),
albedo: Ptr::from(albedo),
conductor_uroughness: Ptr::from(conductor_uroughness),
conductor_vroughness: Ptr::from(conductor_vroughness),
conductor_eta: Ptr::from(conductor_eta),
k: Ptr::from(k),
reflectance: Ptr::from(reflectance),
displacement,
normal_map,
interface_uroughness,
interface_vroughness,
thickness,
interface_eta,
g,
albedo,
conductor_uroughness,
conductor_vroughness,
conductor_eta,
k,
reflectance,
remap_roughness,
max_depth,
n_samples,
@ -238,12 +236,9 @@ impl MaterialTrait for CoatedConductorMaterial {
}
let (mut ce, mut ck) = if !self.conductor_eta.is_null() {
let k_tex = self
.k
.as_ref()
.expect("CoatedConductor: 'k' must be provided if 'conductor_eta' is present");
let k_tex = self.k;
let ce = tex_eval.evaluate_spectrum(&self.conductor_eta, ctx, lambda);
let ck = tex_eval.evaluate_spectrum(k_tex, ctx, lambda);
let ck = tex_eval.evaluate_spectrum(unsafe { k_tex.as_ref() }, ctx, lambda);
(ce, ck)
} else {
let r = SampledSpectrum::clamp(
@ -282,8 +277,8 @@ impl MaterialTrait for CoatedConductorMaterial {
thick,
a,
gg,
self.max_depth as usize,
self.n_samples as usize,
self.max_depth,
self.n_samples,
));
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
}

View file

@ -13,33 +13,33 @@ use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::textures::GPUSpectrumMixTexture;
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::utils::math::clamp;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct HairMaterial {
pub sigma_a: DevicePtr<GPUSpectrumTexture>,
pub color: DevicePtr<GPUSpectrumTexture>,
pub eumelanin: DevicePtr<GPUFloatTexture>,
pub pheomelanin: DevicePtr<GPUFloatTexture>,
pub eta: DevicePtr<GPUFloatTexture>,
pub beta_m: DevicePtr<GPUFloatTexture>,
pub beta_n: DevicePtr<GPUFloatTexture>,
pub alpha: DevicePtr<GPUFloatTexture>,
pub sigma_a: Ptr<GPUSpectrumTexture>,
pub color: Ptr<GPUSpectrumTexture>,
pub eumelanin: Ptr<GPUFloatTexture>,
pub pheomelanin: Ptr<GPUFloatTexture>,
pub eta: Ptr<GPUFloatTexture>,
pub beta_m: Ptr<GPUFloatTexture>,
pub beta_n: Ptr<GPUFloatTexture>,
pub alpha: Ptr<GPUFloatTexture>,
}
impl HairMaterial {
#[cfg(not(target_os = "cuda"))]
pub fn new(
sigma_a: DevicePtr<GPUSpectrumTexture>,
color: DevicePtr<GPUSpectrumTexture>,
eumelanin: DevicePtr<GPUFloatTexture>,
pheomelanin: DevicePtr<GPUFloatTexture>,
eta: DevicePtr<GPUFloatTexture>,
beta_m: DevicePtr<GPUFloatTexture>,
beta_n: DevicePtr<GPUFloatTexture>,
alpha: DevicePtr<GPUFloatTexture>,
sigma_a: Ptr<GPUSpectrumTexture>,
color: Ptr<GPUSpectrumTexture>,
eumelanin: Ptr<GPUFloatTexture>,
pheomelanin: Ptr<GPUFloatTexture>,
eta: Ptr<GPUFloatTexture>,
beta_m: Ptr<GPUFloatTexture>,
beta_n: Ptr<GPUFloatTexture>,
alpha: Ptr<GPUFloatTexture>,
) -> Self {
Self {
sigma_a,
@ -80,8 +80,8 @@ impl MaterialTrait for HairMaterial {
todo!()
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
DevicePtr::null()
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
Ptr::null()
}
fn has_subsurface_scattering(&self) -> bool {
@ -92,9 +92,9 @@ impl MaterialTrait for HairMaterial {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct MeasuredMaterial {
pub displacement: DevicePtr<GPUFloatTexture>,
pub normal_map: DevicePtr<DeviceImage>,
pub brdf: DevicePtr<MeasuredBxDFData>,
pub displacement: Ptr<GPUFloatTexture>,
pub normal_map: Ptr<DeviceImage>,
pub brdf: Ptr<MeasuredBxDFData>,
}
impl MaterialTrait for MeasuredMaterial {
@ -125,7 +125,7 @@ impl MaterialTrait for MeasuredMaterial {
Some(&*self.normal_map)
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
self.displacement
}
@ -137,16 +137,16 @@ impl MaterialTrait for MeasuredMaterial {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct SubsurfaceMaterial {
pub normal_map: DevicePtr<DeviceImage>,
pub displacement: DevicePtr<GPUFloatTexture>,
pub sigma_a: DevicePtr<GPUSpectrumTexture>,
pub sigma_s: DevicePtr<GPUSpectrumMixTexture>,
pub reflectance: DevicePtr<GPUSpectrumMixTexture>,
pub mfp: DevicePtr<GPUSpectrumMixTexture>,
pub normal_map: Ptr<DeviceImage>,
pub displacement: Ptr<GPUFloatTexture>,
pub sigma_a: Ptr<GPUSpectrumTexture>,
pub sigma_s: Ptr<GPUSpectrumMixTexture>,
pub reflectance: Ptr<GPUSpectrumMixTexture>,
pub mfp: Ptr<GPUSpectrumMixTexture>,
pub eta: Float,
pub scale: Float,
pub u_roughness: DevicePtr<GPUFloatTexture>,
pub v_roughness: DevicePtr<GPUFloatTexture>,
pub u_roughness: Ptr<GPUFloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>,
pub remap_roughness: bool,
pub table: BSSRDFTable,
}
@ -177,7 +177,7 @@ impl MaterialTrait for SubsurfaceMaterial {
todo!()
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
todo!()
}

View file

@ -10,17 +10,17 @@ use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::utils::math::clamp;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DielectricMaterial {
normal_map: DevicePtr<DeviceImage>,
displacement: DevicePtr<GPUFloatTexture>,
u_roughness: DevicePtr<GPUFloatTexture>,
v_roughness: DevicePtr<GPUFloatTexture>,
eta: DevicePtr<Spectrum>,
normal_map: Ptr<DeviceImage>,
displacement: Ptr<GPUFloatTexture>,
u_roughness: Ptr<GPUFloatTexture>,
v_roughness: Ptr<GPUFloatTexture>,
eta: Ptr<Spectrum>,
remap_roughness: bool,
}
@ -51,7 +51,7 @@ impl MaterialTrait for DielectricMaterial {
let distrib = TrowbridgeReitzDistribution::new(u_rough, v_rough);
let bxdf = BxDF::Dielectric(DielectricBxDF::new(sampled_eta, distrib));
BSDF::new(ctx.ns, ctx.dpdus, DevicePtr::from(&bxdf))
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
}
fn get_bssrdf<T>(
@ -71,7 +71,7 @@ impl MaterialTrait for DielectricMaterial {
Some(&*self.normal_map)
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
self.displacement
}
@ -83,9 +83,9 @@ impl MaterialTrait for DielectricMaterial {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ThinDielectricMaterial {
pub displacement: DevicePtr<GPUFloatTexture>,
pub normal_map: DevicePtr<DeviceImage>,
pub eta: DevicePtr<Spectrum>,
pub displacement: Ptr<GPUFloatTexture>,
pub normal_map: Ptr<DeviceImage>,
pub eta: Ptr<Spectrum>,
}
impl MaterialTrait for ThinDielectricMaterial {
fn get_bsdf<T: TextureEvaluator>(
@ -113,7 +113,7 @@ impl MaterialTrait for ThinDielectricMaterial {
Some(&*self.normal_map)
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
self.displacement
}

View file

@ -11,15 +11,15 @@ use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::utils::math::clamp;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DiffuseMaterial {
pub normal_map: DevicePtr<DeviceImage>,
pub displacement: DevicePtr<GPUFloatTexture>,
pub reflectance: DevicePtr<GPUSpectrumTexture>,
pub normal_map: Ptr<DeviceImage>,
pub displacement: Ptr<GPUFloatTexture>,
pub reflectance: Ptr<GPUSpectrumTexture>,
}
impl MaterialTrait for DiffuseMaterial {
@ -31,7 +31,7 @@ impl MaterialTrait for DiffuseMaterial {
) -> BSDF {
let r = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda);
let bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
BSDF::new(ctx.ns, ctx.dpdus, DevicePtr::from(&bxdf))
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
}
fn get_bssrdf<T>(
@ -51,7 +51,7 @@ impl MaterialTrait for DiffuseMaterial {
Some(&*self.normal_map)
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
self.displacement
}
@ -63,10 +63,10 @@ impl MaterialTrait for DiffuseMaterial {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DiffuseTransmissionMaterial {
pub image: DevicePtr<DeviceImage>,
pub displacement: DevicePtr<GPUFloatTexture>,
pub reflectance: DevicePtr<GPUFloatTexture>,
pub transmittance: DevicePtr<GPUFloatTexture>,
pub image: Ptr<DeviceImage>,
pub displacement: Ptr<GPUFloatTexture>,
pub reflectance: Ptr<GPUFloatTexture>,
pub transmittance: Ptr<GPUFloatTexture>,
pub scale: Float,
}
@ -96,7 +96,7 @@ impl MaterialTrait for DiffuseTransmissionMaterial {
Some(&*self.image)
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
self.displacement
}

View file

@ -10,14 +10,14 @@ use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::hash::hash_float;
use crate::utils::math::clamp;
use crate::utils::{DevicePtr, Ptr};
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct MixMaterial {
pub amount: DevicePtr<GPUFloatTexture>,
pub amount: Ptr<GPUFloatTexture>,
pub materials: [Ptr<Material>; 2],
}
@ -73,7 +73,7 @@ impl MaterialTrait for MixMaterial {
None
}
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
panic!(
"MixMaterial::get_displacement() shouldn't be called. \
Displacement is not supported on Mix materials directly."

View file

@ -61,11 +61,7 @@ impl BilinearPatchShape {
#[inline(always)]
fn get_vertex_indices(&self) -> [usize; 4] {
unsafe {
let base_ptr = self
.mesh
.vertex_indices
.0
.add((self.blp_index as usize) * 4);
let base_ptr = self.mesh.vertex_indices.add((self.blp_index as usize) * 4);
[
*base_ptr.add(0) as usize,
*base_ptr.add(1) as usize,
@ -80,10 +76,10 @@ impl BilinearPatchShape {
let [v0, v1, v2, v3] = self.get_vertex_indices();
unsafe {
[
*self.mesh.p.0.add(v0),
*self.mesh.p.0.add(v1),
*self.mesh.p.0.add(v2),
*self.mesh.p.0.add(v3),
*self.mesh.p.add(v0),
*self.mesh.p.add(v1),
*self.mesh.p.add(v2),
*self.mesh.p.add(v3),
]
}
}
@ -96,10 +92,10 @@ impl BilinearPatchShape {
let [v0, v1, v2, v3] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.uv.0.add(v0),
*self.mesh.uv.0.add(v1),
*self.mesh.uv.0.add(v2),
*self.mesh.uv.0.add(v3),
*self.mesh.uv.add(v0),
*self.mesh.uv.add(v1),
*self.mesh.uv.add(v2),
*self.mesh.uv.add(v3),
])
}
}
@ -112,10 +108,10 @@ impl BilinearPatchShape {
let [v0, v1, v2, v3] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.n.0.add(v0),
*self.mesh.n.0.add(v1),
*self.mesh.n.0.add(v2),
*self.mesh.n.0.add(v3),
*self.mesh.n.add(v0),
*self.mesh.n.add(v1),
*self.mesh.n.add(v2),
*self.mesh.n.add(v3),
])
}
}

View file

@ -45,11 +45,7 @@ impl TriangleShape {
#[inline(always)]
fn get_vertex_indices(&self) -> [usize; 3] {
unsafe {
let base_ptr = self
.mesh
.vertex_indices
.0
.add((self.tri_index as usize) * 3);
let base_ptr = self.mesh.vertex_indices.add((self.tri_index as usize) * 3);
[
*base_ptr.add(0) as usize,
*base_ptr.add(1) as usize,
@ -63,9 +59,9 @@ impl TriangleShape {
let [v0, v1, v2] = self.get_vertex_indices();
unsafe {
[
*self.mesh.p.0.add(v0),
*self.mesh.p.0.add(v1),
*self.mesh.p.0.add(v2),
*self.mesh.p.add(v0),
*self.mesh.p.add(v1),
*self.mesh.p.add(v2),
]
}
}
@ -78,9 +74,9 @@ impl TriangleShape {
let [v0, v1, v2] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.uv.0.add(v0),
*self.mesh.uv.0.add(v1),
*self.mesh.uv.0.add(v2),
*self.mesh.uv.add(v0),
*self.mesh.uv.add(v1),
*self.mesh.uv.add(v2),
])
}
}
@ -93,9 +89,9 @@ impl TriangleShape {
let [v0, v1, v2] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.s.0.add(v0),
*self.mesh.s.0.add(v1),
*self.mesh.s.0.add(v2),
*self.mesh.s.add(v0),
*self.mesh.s.add(v1),
*self.mesh.s.add(v2),
])
}
}
@ -108,9 +104,9 @@ impl TriangleShape {
let [v0, v1, v2] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.n.0.add(v0),
*self.mesh.n.0.add(v1),
*self.mesh.n.0.add(v2),
*self.mesh.n.add(v0),
*self.mesh.n.add(v1),
*self.mesh.n.add(v2),
])
}
}
@ -213,7 +209,7 @@ impl TriangleShape {
);
isect.face_index = if !self.mesh.face_indices.is_null() {
unsafe { *self.mesh.face_indices.0.add(self.tri_index as usize) }
unsafe { *self.mesh.face_indices.add(self.tri_index as usize) }
} else {
0
};

View file

@ -3,17 +3,17 @@ use crate::core::geometry::Point2f;
use crate::core::pbrt::Float;
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
use crate::utils::math::SquareMatrix3f;
use crate::utils::ptr::DevicePtr;
use crate::utils::ptr::Ptr;
use std::cmp::{Eq, PartialEq};
#[repr(C)]
#[derive(Copy, Debug, Clone)]
pub struct StandardColorSpaces {
pub srgb: DevicePtr<RGBColorSpace>,
pub dci_p3: DevicePtr<RGBColorSpace>,
pub rec2020: DevicePtr<RGBColorSpace>,
pub aces2065_1: DevicePtr<RGBColorSpace>,
pub srgb: Ptr<RGBColorSpace>,
pub dci_p3: Ptr<RGBColorSpace>,
pub rec2020: Ptr<RGBColorSpace>,
pub aces2065_1: Ptr<RGBColorSpace>,
}
#[repr(C)]
@ -24,7 +24,7 @@ pub struct RGBColorSpace {
pub b: Point2f,
pub w: Point2f,
pub illuminant: DenselySampledSpectrum,
pub rgb_to_spectrum_table: DevicePtr<RGBToSpectrumTable>,
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
pub xyz_from_rgb: SquareMatrix3f,
pub rgb_from_xyz: SquareMatrix3f,
}
@ -42,7 +42,7 @@ impl RGBColorSpace {
}
pub fn to_rgb_coeffs(&self, rgb: RGB) -> RGBSigmoidPolynomial {
self.rgb_to_spectrum_table.to_polynomial(rgb)
self.rgb_to_spectrum_table.evaluate(rgb)
}
pub fn convert_colorspace(&self, other: &RGBColorSpace) -> SquareMatrix3f {

View file

@ -4,7 +4,7 @@ use super::{
};
use crate::core::color::{RGB, RGBSigmoidPolynomial, XYZ};
use crate::core::spectrum::SpectrumTrait;
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::Float;
@ -69,7 +69,7 @@ impl SpectrumTrait for UnboundedRGBSpectrum {
pub struct RGBIlluminantSpectrum {
pub scale: Float,
pub rsp: RGBSigmoidPolynomial,
pub illuminant: DevicePtr<DenselySampledSpectrum>,
pub illuminant: Ptr<DenselySampledSpectrum>,
}
impl RGBIlluminantSpectrum {
@ -85,7 +85,7 @@ impl RGBIlluminantSpectrum {
Self {
scale,
rsp,
illuminant: DevicePtr::from(&illuminant),
illuminant: Ptr::from(&illuminant),
}
}
}

View file

@ -4,7 +4,7 @@ use crate::Float;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
use crate::utils::find_interval;
use crate::utils::ptr::DevicePtr;
use crate::utils::ptr::Ptr;
use core::slice;
use std::hash::{Hash, Hasher};
use std::sync::LazyLock;
@ -36,7 +36,7 @@ impl SpectrumTrait for ConstantSpectrum {
pub struct DenselySampledSpectrum {
pub lambda_min: i32,
pub lambda_max: i32,
pub values: DevicePtr<Float>,
pub values: Ptr<Float>,
}
unsafe impl Send for DenselySampledSpectrum {}
@ -54,7 +54,7 @@ impl DenselySampledSpectrum {
#[inline(always)]
fn get(&self, idx: u32) -> Float {
unsafe { *self.values.0.add(idx as usize) }
unsafe { *self.values.add(idx as usize) }
}
}
@ -62,7 +62,7 @@ impl PartialEq for DenselySampledSpectrum {
fn eq(&self, other: &Self) -> bool {
self.lambda_min == other.lambda_min
&& self.lambda_max == other.lambda_max
&& self.values.0 == other.values.0
&& self.values == other.values
}
}
@ -91,7 +91,7 @@ impl SpectrumTrait for DenselySampledSpectrum {
s[i] = 0.0;
} else {
unsafe {
s[i] = *self.values.0.add(offset as usize);
s[i] = *self.values.add(offset as usize);
}
}
}
@ -104,7 +104,7 @@ impl SpectrumTrait for DenselySampledSpectrum {
if offset < 0 || offset >= n {
0.0
} else {
unsafe { *self.values.0.add(offset as usize) }
unsafe { *self.values.add(offset as usize) }
}
}
@ -118,7 +118,7 @@ impl SpectrumTrait for DenselySampledSpectrum {
for i in 0..n {
unsafe {
let val = *self.values.0.add(i);
let val = *self.values.add(i);
if val > max_val {
max_val = val;
}
@ -131,20 +131,20 @@ impl SpectrumTrait for DenselySampledSpectrum {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct PiecewiseLinearSpectrum {
pub lambdas: DevicePtr<Float>,
pub values: DevicePtr<Float>,
pub lambdas: Ptr<Float>,
pub values: Ptr<Float>,
pub count: u32,
}
impl PiecewiseLinearSpectrum {
#[inline(always)]
fn lambda(&self, i: u32) -> Float {
unsafe { *self.lambdas.0.add(i as usize) }
unsafe { *self.lambdas.add(i as usize) }
}
#[inline(always)]
fn value(&self, i: u32) -> Float {
unsafe { *self.values.0.add(i as usize) }
unsafe { *self.values.add(i as usize) }
}
}
@ -186,7 +186,7 @@ impl SpectrumTrait for PiecewiseLinearSpectrum {
for i in 0..n {
unsafe {
let val = *self.values.0.add(i as usize);
let val = *self.values.add(i as usize);
if val > max_val {
max_val = val;
}

View file

@ -3,7 +3,7 @@ use crate::core::spectrum::Spectrum;
use crate::core::spectrum::SpectrumTrait;
use crate::core::texture::{TextureEvalContext, TextureMapping2D};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::{DevicePtr, Transform};
use crate::utils::{Ptr, Transform};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
@ -48,19 +48,19 @@ impl FloatBilerpTexture {
#[derive(Clone, Copy, Debug)]
pub struct SpectrumBilerpTexture {
pub mapping: TextureMapping2D,
pub v00: DevicePtr<Spectrum>,
pub v01: DevicePtr<Spectrum>,
pub v10: DevicePtr<Spectrum>,
pub v11: DevicePtr<Spectrum>,
pub v00: Ptr<Spectrum>,
pub v01: Ptr<Spectrum>,
pub v10: Ptr<Spectrum>,
pub v11: Ptr<Spectrum>,
}
impl SpectrumBilerpTexture {
pub fn new(
mapping: TextureMapping2D,
v00: DevicePtr<Spectrum>,
v01: DevicePtr<Spectrum>,
v10: DevicePtr<Spectrum>,
v11: DevicePtr<Spectrum>,
v00: Ptr<Spectrum>,
v01: Ptr<Spectrum>,
v10: Ptr<Spectrum>,
v11: Ptr<Spectrum>,
) -> Self {
Self {
mapping,

View file

@ -4,12 +4,12 @@ use crate::core::texture::{
TextureMapping3DTrait,
};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::{DevicePtr, Ptr, math::square};
use crate::utils::{Ptr, math::square};
fn checkerboard(
ctx: &TextureEvalContext,
map2d: DevicePtr<TextureMapping2D>,
map3d: DevicePtr<TextureMapping3D>,
map2d: Ptr<TextureMapping2D>,
map3d: Ptr<TextureMapping3D>,
) -> Float {
let d = |x: Float| -> Float {
let y = x / 2. - (x / 2.).floor() - 0.5;
@ -43,8 +43,8 @@ fn checkerboard(
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct FloatCheckerboardTexture {
pub map2d: DevicePtr<TextureMapping2D>,
pub map3d: DevicePtr<TextureMapping3D>,
pub map2d: Ptr<TextureMapping2D>,
pub map3d: Ptr<TextureMapping3D>,
pub tex: [Ptr<GPUFloatTexture>; 2],
}
@ -74,8 +74,8 @@ impl FloatCheckerboardTexture {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct SpectrumCheckerboardTexture {
pub map2d: DevicePtr<TextureMapping2D>,
pub map3d: DevicePtr<TextureMapping3D>,
pub map2d: Ptr<TextureMapping2D>,
pub map3d: Ptr<TextureMapping3D>,
pub tex: [Ptr<GPUSpectrumTexture>; 2],
}

View file

@ -4,7 +4,7 @@ use crate::core::texture::{
GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext, TextureMapping2D,
};
use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths};
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use crate::utils::math::square;
use crate::utils::noise::noise_2d;
@ -28,8 +28,8 @@ fn inside_polka_dot(st: Point2f) -> bool {
#[derive(Debug, Clone, Copy)]
pub struct FloatDotsTexture {
pub mapping: TextureMapping2D,
pub outside_dot: DevicePtr<GPUFloatTexture>,
pub inside_dot: DevicePtr<GPUFloatTexture>,
pub outside_dot: Ptr<GPUFloatTexture>,
pub inside_dot: Ptr<GPUFloatTexture>,
}
impl FloatDotsTexture {
@ -53,8 +53,8 @@ impl FloatDotsTexture {
#[derive(Clone, Copy, Debug)]
pub struct SpectrumDotsTexture {
pub mapping: TextureMapping2D,
pub outside_dot: DevicePtr<GPUSpectrumTexture>,
pub inside_dot: DevicePtr<GPUSpectrumTexture>,
pub outside_dot: Ptr<GPUSpectrumTexture>,
pub inside_dot: Ptr<GPUSpectrumTexture>,
}
impl SpectrumDotsTexture {

View file

@ -23,6 +23,7 @@ pub struct GPUSpectrumImageTexture {
}
impl GPUSpectrumImageTexture {
#[allow(unused)]
pub fn evaluate(
&self,
ctx: &TextureEvalContext,
@ -44,12 +45,14 @@ impl GPUSpectrumImageTexture {
let d_p_dy = [c.dsdy, c.dtdy];
let tex_color = if self.is_single_channel {
let val: Float =
unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
let val = 0.;
// let val: Float =
// unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
RGB::new(val, val, val)
} else {
let val: [Float; 4] =
unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
let val = [0., 0., 0., 0.];
// let val: [Float; 4] =
// unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
RGB::new(val[0], val[1], val[2])
};
@ -86,6 +89,7 @@ impl GPUFloatImageTexture {
return 0.;
}
#[cfg(feature = "cuda")]
#[allow(unused)]
{
use cuda_std::intrinsics;
let c = self.mapping.map(ctx);
@ -93,10 +97,12 @@ impl GPUFloatImageTexture {
let v = 1.0 - c.st.y();
let d_p_dx = [c.dsdx, c.dtdx];
let d_p_dy = [c.dsdy, c.dtdy];
let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
// let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
let val: Float = 0.;
let result = if self.invert {
(1.0 - val).max(0.0) // Invert the pixel intensity
// Invert the pixel intensity
(1.0 - val).max(0.0)
} else {
val
};

View file

@ -6,7 +6,7 @@ use crate::core::texture::{TextureEvalContext, TextureMapping3D};
use crate::spectra::{RGBAlbedoSpectrum, RGBColorSpace, SampledSpectrum, SampledWavelengths};
use crate::utils::math::clamp;
use crate::utils::noise::fbm;
use crate::utils::ptr::DevicePtr;
use crate::utils::ptr::Ptr;
use crate::utils::splines::evaluate_cubic_bezier;
#[repr(C)]
@ -18,7 +18,7 @@ pub struct MarbleTexture {
pub scale: Float,
pub variation: Float,
// TODO: DO not forget to pass StandardColorSpace here!!
pub colorspace: DevicePtr<RGBColorSpace>,
pub colorspace: Ptr<RGBColorSpace>,
}
unsafe impl Send for MarbleTexture {}

View file

@ -6,7 +6,7 @@ use crate::spectra::{
RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
SampledWavelengths, StandardColorSpaces,
};
use crate::utils::ptr::{DevicePtr, Slice};
use crate::utils::Ptr;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
@ -23,7 +23,7 @@ impl GPUFloatPtexTexture {
#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct GPUSpectrumPtexTexture {
pub face_values: Slice<RGB>,
pub face_values: *const RGB,
pub n_faces: u32,
pub spectrum_type: SpectrumType,
pub colorspaces: StandardColorSpaces,
@ -36,16 +36,16 @@ impl GPUSpectrumPtexTexture {
lambda: &SampledWavelengths,
) -> SampledSpectrum {
let index = ctx.face_index.clamp(0, self.n_faces.saturating_sub(1));
let rgb = self.face_values[index as usize];
let rgb = unsafe { &*self.face_values.add(index as usize) };
let s_rgb = self.colorspaces.srgb;
match self.spectrum_type {
SpectrumType::Unbounded => RGBUnboundedSpectrum::new(&s_rgb, rgb).sample(lambda),
SpectrumType::Unbounded => RGBUnboundedSpectrum::new(&s_rgb, *rgb).sample(lambda),
SpectrumType::Albedo => {
let clamped_rgb = rgb.clamp(0.0, 1.0);
RGBAlbedoSpectrum::new(&s_rgb, clamped_rgb).sample(lambda)
}
SpectrumType::Illuminant => RGBIlluminantSpectrum::new(&s_rgb, rgb).sample(lambda),
SpectrumType::Illuminant => RGBIlluminantSpectrum::new(&s_rgb, *rgb).sample(lambda),
}
}
}

View file

@ -1,4 +1,8 @@
use crate::core::geometry::{
Bounds2i, Bounds3f, Bounds3i, Point2i, Point3f, Point3i, Vector2i, Vector3f, Vector3i,
};
use crate::core::pbrt::Float;
use crate::utils::Ptr;
use crate::utils::math::lerp;
use std::collections::HashSet;
use std::collections::hash_map::RandomState;
@ -6,10 +10,6 @@ use std::hash::{BuildHasher, Hash, Hasher};
use std::ops::{Add, Index, IndexMut, Mul, Sub};
use std::sync::{Arc, Mutex, RwLock};
use crate::core::geometry::{
Bounds2i, Bounds3f, Bounds3i, Point2i, Point3f, Point3i, Vector2i, Vector3f, Vector3i,
};
pub trait Interpolatable:
Copy + Default + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self>
{
@ -120,7 +120,7 @@ impl<T> IndexMut<(i32, i32)> for Array2D<T> {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SampledGrid<T> {
pub values: *const T,
pub values: Ptr<T>,
pub values_len: u32,
pub nx: i32,
pub ny: i32,
@ -135,7 +135,7 @@ impl<T> SampledGrid<T> {
pub fn new(slice: &[T], nx: i32, ny: i32, nz: i32) -> Self {
assert_eq!(slice.len(), (nx * ny * nz) as usize);
Self {
values: slice.as_ptr(),
values: Ptr::from(slice),
values_len: (nx * ny * nz) as u32,
nx,
ny,
@ -145,7 +145,7 @@ impl<T> SampledGrid<T> {
pub fn empty() -> Self {
Self {
values: core::ptr::null(),
values: Ptr::null(),
values_len: 0,
nx: 0,
ny: 0,

View file

@ -1,12 +1,14 @@
use std::fmt;
use std::sync::Arc;
#[derive(Error, Debug)]
#[derive(Debug)]
pub enum LlsError {
SingularMatrix,
}
#[derive(Error, Debug, Clone, PartialEq, Eq)]
impl std::error::Error for LlsError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InversionError {
SingularMatrix,
EmptyMatrix,
@ -30,3 +32,5 @@ impl fmt::Display for InversionError {
}
}
}
impl std::error::Error for InversionError {}

View file

@ -5,7 +5,7 @@ use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI
use crate::utils::hash::{hash_buffer, mix_bits};
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
use crate::utils::DevicePtr;
use crate::utils::Ptr;
use half::f16;
use num_traits::{Float as NumFloat, Num, One, Signed, Zero};
use std::error::Error;
@ -647,7 +647,7 @@ pub fn round_up_pow2(mut n: i32) -> i32 {
pub const PRIME_TABLE_SIZE: usize = 1000;
const PRIMES: [i32; PRIME_TABLE_SIZE] = [
pub const PRIMES: [i32; PRIME_TABLE_SIZE] = [
2, 3, 5, 7, 11, // Subsequent prime numbers
13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
@ -753,14 +753,15 @@ pub fn inverse_radical_inverse(mut inverse: u64, base: u64, n_digits: u64) -> u6
pub struct DigitPermutation {
pub base: u32,
pub n_digits: u32,
pub permutations: DevicePtr<u16>,
pub permutations: Ptr<u16>,
}
impl DigitPermutation {
#[inline(always)]
pub fn permute(&self, digit_index: i32, digit_value: i32) -> i32 {
let idx = (digit_index * self.base as i32 + digit_value) as usize;
self.permutations[idx] as i32
let permutation = unsafe { *self.permutations.add(idx.into()) };
permutation as i32
}
}

View file

@ -1,5 +1,3 @@
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
pub mod containers;
pub mod error;
pub mod hash;
@ -15,9 +13,12 @@ pub mod sobol;
pub mod splines;
pub mod transform;
pub use ptr::{DevicePtr, Ptr};
pub use ptr::Ptr;
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
use crate::Float;
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
#[inline]
pub fn find_interval<F>(sz: u32, pred: F) -> u32
where
@ -57,6 +58,8 @@ where
i
}
#[repr(C)]
#[derive(Debug, Default)]
pub struct AtomicFloat {
bits: AtomicU32,
}
@ -68,11 +71,11 @@ impl AtomicFloat {
}
}
pub fn get(&self) -> f32 {
f32::from_bits(self.bits.load(Ordering::Relaxed))
pub fn get(&self) -> Float {
Float::from_bits(self.bits.load(Ordering::Relaxed))
}
pub fn set(&self, val: f32) {
pub fn set(&self, val: Float) {
self.bits.store(val.to_bits(), Ordering::Relaxed);
}

View file

@ -14,7 +14,14 @@ impl<T: ?Sized> Clone for Ptr<T> {
}
impl<T: ?Sized> Copy for Ptr<T> {}
// Ptr is just a pointer - Send/Sync depends on T
impl<T: ?Sized> PartialEq for Ptr<T> {
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl<T: ?Sized> Eq for Ptr<T> {}
unsafe impl<T: ?Sized + Send> Send for Ptr<T> {}
unsafe impl<T: ?Sized + Sync> Sync for Ptr<T> {}

View file

@ -722,6 +722,27 @@ impl DevicePiecewiseConstant1D {
self.n
}
fn find_interval(&self, u: Float) -> usize {
let mut size = self.n as usize;
let mut first = 0;
while size > 0 {
let half = size >> 1;
let middle = first + half;
let cdf_val = unsafe { *self.cdf.add(middle) };
if cdf_val <= u {
first = middle + 1;
size -= half + 1;
} else {
size = half;
}
}
(first - 1).clamp(0, self.n as usize - 1)
}
pub fn sample(&self, u: Float) -> (Float, Float, usize) {
// Find offset via binary search on CDF
let offset = self.find_interval(u);
@ -766,12 +787,12 @@ impl DevicePiecewiseConstant2D {
// }
pub fn integral(&self) -> f32 {
self.p_marginal.integral()
self.marginal.integral()
}
pub fn sample(&self, u: Point2f) -> (Point2f, f32, Point2i) {
let (d1, pdf1, off_y) = self.p_marginal.sample(u.y());
let (d0, pdf0, off_x) = self.p_conditional_v[off_y].sample(u.x());
let (d1, pdf1, off_y) = self.marginal.sample(u.y());
let (d0, pdf0, off_x) = (unsafe { &*self.conditional.add(off_y) }).sample(u.x());
let pdf = pdf0 * pdf1;
let offset = Point2i::new(off_x as i32, off_y as i32);
(Point2f::new(d0, d1), pdf, offset)
@ -779,13 +800,13 @@ impl DevicePiecewiseConstant2D {
pub fn pdf(&self, p: Point2f) -> Float {
// Find which row
let delta_v = 1.0 / self.n_v as Float;
// let delta_v = 1.0 / self.n_v as Float;
let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1);
let conditional = unsafe { &*self.conditional.add(v_offset) };
// Find which column
let delta_u = 1.0 / self.n_u as Float;
// let delta_u = 1.0 / self.n_u as Float;
let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1);
let func_val = unsafe { *conditional.func.add(u_offset) };
@ -1279,7 +1300,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
#[inline(always)]
fn get_param_value(&self, dim: usize, idx: usize) -> Float {
// Safety: Bounds checking against param_size ensures this is valid
unsafe { *self.param_values[dim].0.add(idx) }
unsafe { *self.param_values[dim].add(idx) }
}
fn get_slice_info(&self, params: [Float; N]) -> (u32, [(Float, Float); N]) {
@ -1341,7 +1362,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
current_mask >>= 1;
}
let idx = (i0 + offset) as usize;
let val = unsafe { *data.0.add(idx) };
let val = unsafe { *data.add(idx) };
result += weight * val;
}
result

View file

@ -1,11 +1,10 @@
use crate::Float;
use crate::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
use crate::core::primitive::PrimitiveTrait;
use crate::core::shape::ShapeIntersection;
use crate::utils::math::encode_morton_3;
use crate::utils::math::next_float_down;
use crate::utils::{find_interval, partition_slice};
use rayon::prelude::*;
use shared::Float;
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
use shared::core::shape::ShapeIntersection;
use shared::utils::math::encode_morton_3;
use shared::utils::{find_interval, partition_slice};
use std::cmp::Ordering;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};

View file

@ -88,6 +88,7 @@ pub trait CameraFactory {
medium: Medium,
film: Arc<Film>,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, String>;
}

View file

@ -1,4 +1,10 @@
use shared::film::{Film, FilmBase, GBufferFilm, PixelSensor, RGBFilm, SpectralFilm};
use shared::Float;
use shared::core::spectrum::Spectrum;
use shared::film::{Film, FilmBase, GBufferFilm, PixelSensor, PixelSensor, RGBFilm, SpectralFilm};
use shared::spectra::{PiecewiseLinearSpectrum, RGBColorSpace};
use crate::spectra::DenselySampledSpectrumBuffer;
use crate::utils::{FileLoc, ParameterDictionary};
const N_SWATCH_REFLECTANCES: usize = 24;
const SWATCH_REFLECTANCES: Lazy<[Spectrum; N_SWATCH_REFLECTANCES]> = Lazy::new(|| {
@ -10,7 +16,7 @@ const SWATCH_REFLECTANCES: Lazy<[Spectrum; N_SWATCH_REFLECTANCES]> = Lazy::new(|
});
pub trait PixelSensorHost {
pub fn get_swatches() -> &[Spectrum; N_SWATCH_REFLECTANCES] {
pub fn get_swatches() -> &'static [Spectrum; N_SWATCH_REFLECTANCES] {
&*SWATCH_REFLECTANCES
}
@ -29,9 +35,9 @@ pub trait PixelSensorHost {
let imaging_ratio = exposure_time * iso / 100.;
let d_illum = if white_balance_temp == 0. {
generate_cie_d(6500.)
DenselySampledSpectrumBuffer::generate_cie_d(6500.)
} else {
generate_cie_d(white_balance_temp)
DenselySampledSpectrumBuffer::generate_cie_d(white_balance_temp)
};
let sensor_illum: Option<Arc<Spectrum>> = if white_balance_temp != 0. {
@ -73,8 +79,104 @@ pub trait PixelSensorHost {
.map_err(|e| e.to_string());
}
}
fn new(
r: Spectrum,
g: Spectrum,
b: Spectrum,
output_colorspace: RGBColorSpace,
sensor_illum: &Spectrum,
imaging_ratio: Float,
spectra: *const StandardSpectra,
swatches: &[Spectrum; 24],
) -> Self {
// As seen in usages of this constructos, sensor_illum can be null
// Going with the colorspace's own illuminant, but this might not be the right choice
// TODO: Test this
let illum: &Spectrum = match sensor_illum {
Some(arc_illum) => &**arc_illum,
None => &output_colorspace.illuminant,
};
let r_bar = DenselySampledSpectrum::from_spectrum(&r);
let g_bar = DenselySampledSpectrum::from_spectrum(&g);
let b_bar = DenselySampledSpectrum::from_spectrum(&b);
let mut rgb_camera = [[0.; 3]; Self::N_SWATCH_REFLECTANCES];
let swatches = Self::get_swatches();
for i in 0..Self::N_SWATCH_REFLECTANCES {
let rgb = Self::project_reflectance::<RGB>(
&swatches[i],
illum,
&Spectrum::Dense(r_bar.clone()),
&Spectrum::Dense(g_bar.clone()),
&Spectrum::Dense(b_bar.clone()),
);
for c in 0..3 {
rgb_camera[i][c] = rgb[c];
}
}
let mut xyz_output = [[0.; 3]; Self::N_SWATCH_REFLECTANCES];
let sensor_white_g = illum.inner_product(&Spectrum::Dense(g_bar.clone()));
let sensor_white_y = illum.inner_product(spectra.y);
for i in 0..Self::N_SWATCH_REFLECTANCES {
let s = swatches[i].clone();
let xyz = Self::project_reflectance::<XYZ>(
&s,
&Spectrum::Dense(output_colorspace.illuminant),
spectra.x,
spectra.y,
spectra.z,
) * (sensor_white_y / sensor_white_g);
for c in 0..3 {
xyz_output[i][c] = xyz[c];
}
}
let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output)?;
Ok(Self {
xyz_from_sensor_rgb,
r_bar,
g_bar,
b_bar,
imaging_ratio,
})
}
fn new_with_white_balance(
output_colorspace: &RGBColorSpace,
sensor_illum: &Spectrum,
imaging_ratio: Float,
spectra: *const StandardSpectra,
) -> Self {
let r_bar = DenselySampledSpectrumBuffer::from_spectrum(spectra.x);
let g_bar = DenselySampledSpectrumBuffer::from_spectrum(spectra.y);
let b_bar = DenselySampledSpectrumBuffer::from_spectrum(spectra.z);
let xyz_from_sensor_rgb: SquareMatrix<Float, 3>;
if let Some(illum) = sensor_illum {
let source_white = illum.to_xyz(spectra).xy();
let target_white = output_colorspace.w;
xyz_from_sensor_rgb = white_balance(source_white, target_white);
} else {
xyz_from_sensor_rgb = SquareMatrix::<Float, 3>::default();
}
Self {
xyz_from_sensor_rgb,
r_bar,
g_bar,
b_bar,
imaging_ratio,
}
}
}
impl PixelSensorHost for PixelSensor {}
struct SpectralFilmStorage {
pixels: Array2D<SpectralPixel>,
bucket_sums: Vec<f64>,
@ -164,6 +266,39 @@ impl SpectralFilmHost {
}
}
impl GBufferFilmHost {
pub fn new(
base: &FilmBase,
output_from_render: &AnimatedTransform,
apply_inverse: bool,
colorspace: &RGBColorSpace,
max_component_value: Float,
write_fp16: bool,
) -> Self {
assert!(!base.pixel_bounds.is_empty());
let sensor_ptr = base.sensor;
if sensor_ptr.is_null() {
panic!("Film must have a sensor");
}
let sensor = unsafe { &*sensor_ptr };
let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor.xyz_from_sensor_rgb;
let filter_integral = base.filter.integral();
let pixels = Array2D::new(base.pixel_bounds);
Self {
base: base.clone(),
output_from_render: output_from_render.clone(),
apply_inverse,
pixels,
colorspace: colorspace.clone(),
max_component_value,
write_fp16,
filter_integral,
output_rgbf_from_sensor_rgb,
}
}
}
pub trait FilmBaseHost {
fn create(
params: &ParameterDictionary,

View file

@ -1,3 +1,5 @@
use shared::core::filter::FilterSampler;
use shared::core::geometry::Point2f;
use shared::filter::Filter;
use shared::filters::*;
@ -46,3 +48,37 @@ impl FilterFactory for Filter {
}
}
}
pub trait CreateFilterSampler {
fn new<F>(radius: Vector2f, func: F) -> Self
where
F: Fn(Point2f) -> Float;
}
impl CreateFilterSampler for FilterSampler {
fn new<F>(radius: Vector2f, func: F) -> Self
where
F: Fn(Point2f) -> Float,
{
let domain = Bounds2f::from_points(
Point2f::new(-radius.x(), -radius.y()),
Point2f::new(radius.x(), radius.y()),
);
let nx = (32.0 * radius.x()) as usize;
let ny = (32.0 * radius.y()) as usize;
let mut f = Array2D::new_with_dims(nx, ny);
for y in 0..f.y_size() {
for x in 0..f.x_size() {
let p = domain.lerp(Point2f::new(
(x as Float + 0.5) / f.x_size() as Float,
(y as Float + 0.5) / f.y_size() as Float,
));
f[(x as i32, y as i32)] = func(p);
}
}
let distrib = DevicePiecewiseConstant2D::new_with_bounds(&f, domain);
Self { domain, f, distrib }
}
}

View file

@ -1,12 +1,11 @@
use super::{Image, ImageAndMetadata};
use crate::core::image::PixelStorage;
use crate::utils::error::ImageError;
use anyhow::{Context, Result, bail};
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
use image_rs::ImageReader;
use image_rs::{DynamicImage, ImageBuffer, Rgb, Rgba};
use image_rs::{DynamicImage, ImageReader};
use shared::Float;
use shared::core::color::{ColorEncoding, LINEAR, SRGB};
use shared::core::image::DeviceImage;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;
@ -182,12 +181,12 @@ impl ImageIO for Image {
fn to_u8_buffer(&self) -> Vec<u8> {
match &self.pixels {
PixelData::U8(data) => data.clone(),
PixelData::F16(data) => data
PixelStorage::U8(data) => data.clone(),
PixelStorage::F16(data) => data
.iter()
.map(|v| (v.to_f32().clamp(0.0, 1.0) * 255.0 + 0.5) as u8)
.collect(),
PixelData::F32(data) => data
PixelStorage::F32(data) => data
.iter()
.map(|v| (v.clamp(0.0, 1.0) * 255.0 + 0.5) as u8)
.collect(),
@ -205,41 +204,27 @@ fn read_generic(path: &Path, encoding: Option<ColorEncoding>) -> Result<ImageAnd
let res = Point2i::new(w, h);
// Check if it was loaded as high precision or standard
let rgb_names = || vec!["R".to_string(), "G".to_string(), "B".to_string()];
let rgba_names = || {
vec![
"R".to_string(),
"G".to_string(),
"B".to_string(),
"A".to_string(),
]
};
let image = match dyn_img {
DynamicImage::ImageRgb32F(buf) => Image {
format: PixelFormat::F32,
resolution: res,
channel_names: vec!["R".into(), "G".into(), "B".into()],
encoding: LINEAR,
pixels: PixelData::F32(buf.into_raw()),
},
DynamicImage::ImageRgba32F(buf) => Image {
format: PixelFormat::F32,
resolution: res,
channel_names: vec!["R".into(), "G".into(), "B".into(), "A".into()],
encoding: LINEAR,
pixels: PixelData::F32(buf.into_raw()),
},
DynamicImage::ImageRgb32F(buf) => Image::from_f32(buf.into_raw(), res, rgb_names()),
DynamicImage::ImageRgba32F(buf) => Image::from_f32(buf.into_raw(), res, rgba_names()),
_ => {
// Default to RGB8 for everything else
let enc = encoding.unwrap_or(ColorEncoding::sRGB);
if dyn_img.color().has_alpha() {
let buf = dyn_img.to_rgba8();
Image {
format: PixelFormat::U8,
resolution: res,
channel_names: vec!["R".into(), "G".into(), "B".into(), "A".into()],
encoding: encoding.unwrap_or(SRGB),
pixels: PixelData::U8(buf.into_raw()),
}
Image::from_u8(buf.into_raw(), res, rgba_names(), enc)
} else {
let buf = dyn_img.to_rgb8();
Image {
format: PixelFormat::U8,
resolution: res,
channel_names: vec!["R".into(), "G".into(), "B".into()],
encoding: encoding.unwrap_or(SRGB),
pixels: PixelData::U8(buf.into_raw()),
}
Image::from_u8(buf.into_raw(), res, rgb_names(), enc)
}
}
};
@ -275,7 +260,7 @@ fn read_exr(path: &Path) -> Result<ImageAndMetadata> {
resolution: Point2i::new(w, h),
channel_names: vec!["R".into(), "G".into(), "B".into(), "A".into()],
encoding: LINEAR,
pixels: PixelData::F32(image.layer_data.channel_data.pixels),
pixels: PixelStorage::F32(image.layer_data.channel_data.pixels),
};
let metadata = ImageMetadata::default();
@ -360,7 +345,7 @@ fn read_pfm(path: &Path) -> Result<ImageAndMetadata> {
resolution: Point2i::new(w, h),
channel_names: names,
encoding: LINEAR,
pixels: PixelData::F32(pixels),
pixels: PixelStorage::F32(pixels),
};
let metadata = ImageMetadata::default();

View file

@ -1,11 +1,11 @@
use crate::core::geometry::{Bounds2i, Point2i};
use crate::core::pbrt::Float;
use crate::spectra::colorspace::RGBColorSpace;
use crate::utils::math::SquareMatrix;
use smallvec::SmallVec;
use shared::Float;
use shared::core::geometry::{Bounds2i, Point2i};
use shared::spectra::RGBColorSpace;
use shared::utils::math::SquareMatrix;
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
// use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone, Default)]
pub struct ImageChannelDesc {
pub offset: Vec<usize>,

View file

@ -1,6 +1,7 @@
use half::f16;
use shared::core::geometry::Point2i;
use shared::core::image::{DeviceImage, ImageAccess, ImageBase, PixelFormat, WrapMode};
use shared::utils::math::f16_to_f32;
use smallvec::smallvec;
use std::ops::Deref;
pub mod io;
@ -11,16 +12,6 @@ pub mod pixel;
pub use io::ImageIO;
pub use metadata::*;
impl std::fmt::Display for PixelFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PixelFormat::U8 => write!(f, "U256"),
PixelFormat::F16 => write!(f, "Half"),
PixelFormat::F32 => write!(f, "Float"),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct ImageChannelValues(pub SmallVec<[Float; 4]>);

View file

@ -1,5 +1,5 @@
// use rayon::prelude::*;
use super::Image;
use crate::core::image::PixelStorage;
use rayon::prelude::*;
use shared::Float;
use shared::core::geometry::{Bounds2i, Point2i};
@ -19,9 +19,9 @@ impl Image {
let nc = self.n_channels();
match &mut self.pixels {
PixelData::U8(d) => flip_y_kernel(d, res, nc),
PixelData::F16(d) => flip_y_kernel(d, res, nc),
PixelData::F32(d) => flip_y_kernel(d, res, nc),
PixelStorage::U8(d) => flip_y_kernel(d, res, nc),
PixelStorage::F16(d) => flip_y_kernel(d, res, nc),
PixelStorage::F32(d) => flip_y_kernel(d, res, nc),
}
}
@ -39,13 +39,13 @@ impl Image {
);
match (&self.pixels, &mut new_image.pixels) {
(PixelData::U8(src), PixelData::U8(dst)) => {
(PixelStorage::U8(src), PixelStorage::U8(dst)) => {
crop_kernel(src, dst, self.resolution, bounds, self.n_channels())
}
(PixelData::F16(src), PixelData::F16(dst)) => {
(PixelStorage::F16(src), PixelStorage::F16(dst)) => {
crop_kernel(src, dst, self.resolution, bounds, self.n_channels())
}
(PixelData::F32(src), PixelData::F32(dst)) => {
(PixelStorage::F32(src), PixelStorage::F32(dst)) => {
crop_kernel(src, dst, self.resolution, bounds, self.n_channels())
}
_ => panic!("Format mismatch in crop"),
@ -56,9 +56,9 @@ impl Image {
pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap: WrapMode2D) {
match &self.pixels {
PixelData::U8(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
PixelData::F16(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
PixelData::F32(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
PixelStorage::U8(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
PixelStorage::F16(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
PixelStorage::F32(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
}
}
@ -68,13 +68,13 @@ impl Image {
let encoding = self.encoding;
match &mut self.pixels {
PixelData::U8(d) => {
PixelStorage::U8(d) => {
copy_rect_in_kernel(d, resolution, n_channels, encoding, extent, buf)
}
PixelData::F16(d) => {
PixelStorage::F16(d) => {
copy_rect_in_kernel(d, resolution, n_channels, encoding, extent, buf)
}
PixelData::F32(d) => {
PixelStorage::F32(d) => {
copy_rect_in_kernel(d, resolution, n_channels, encoding, extent, buf)
}
}
@ -154,9 +154,9 @@ impl Image {
);
match &mut next.pixels {
PixelData::U8(d) => downsample_kernel(d, new_res, prev, internal_wrap),
PixelData::F16(d) => downsample_kernel(d, new_res, prev, internal_wrap),
PixelData::F32(d) => downsample_kernel(d, new_res, prev, internal_wrap),
PixelStorage::U8(d) => downsample_kernel(d, new_res, prev, internal_wrap),
PixelStorage::F16(d) => downsample_kernel(d, new_res, prev, internal_wrap),
PixelStorage::F32(d) => downsample_kernel(d, new_res, prev, internal_wrap),
}
levels.push(next);
}

View file

@ -1,16 +1,16 @@
use crate::core::spectrum::{SPECTRUM_CACHE, spectrum_to_photometric};
use crate::core::spectrum::SPECTRUM_CACHE;
use crate::core::texture::FloatTexture;
use crate::lights::*;
use crate::utils::containers::InternCache;
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use log::error;
use shared::core::camera::CameraTransform;
use shared::core::light::Light;
use shared::core::medium::Medium;
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::lights::*;
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
use shared::utils::{Ptr, Transform};
use shared::utils::Transform;
pub fn lookup_spectrum(s: &Spectrum) -> DenselySampledSpectrum {
let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
@ -120,7 +120,7 @@ impl LightFactory for Light {
alpha_tex,
colorspace,
)?,
"infinite" => infinite::create(
"infinite" => crate::lights::infinite::create(
arena,
render_from_light,
medium,

View file

@ -1,6 +1,7 @@
use crate::Arena;
use crate::core::image::Image;
use crate::utils::TextureParameterDictionary;
use crate::utils::error::FileLoc;
use crate::utils::parameters::ParameterDictionary;
use shared::core::material::Material;
use shared::materials::*;
use std::collections::HashMap;
@ -11,6 +12,7 @@ pub trait CreateMaterial: Sized {
normal_map: Option<Ptr<Image>>,
named_materials: &HashMap<String, Material>,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, Error>;
}
@ -38,6 +40,7 @@ pub trait MaterialFactory {
normal_map: Ptr<Image>,
named_materials: HashMap<String, Material>,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, Error>;
}
@ -48,7 +51,8 @@ impl MaterialFactory for Material {
normal_map: Option<Arc<Image>>,
named_materials: &HashMap<String, Material>,
loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
make_material_factory!(
name, params, normal_map, named_materials, loc;

View file

@ -7,6 +7,7 @@ pub mod filter;
pub mod image;
pub mod light;
pub mod material;
pub mod medium;
pub mod sampler;
pub mod sampler;
pub mod scene;

View file

@ -1,12 +1,24 @@
use shared::core::sampler::Sampler;
use crate::Arena;
pub trait CreateSampler {
fn create(
params: &ParameterDictionary,
full_res: Point2i,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, Error>;
}
pub trait SamplerFactory {
fn create(
name: &str,
params: &ParameterDictionary,
full_res: Point2i,
loc: &FileLoc,
) -> Result<Self, String>;
arena: &mut Arena,
) -> Result<Self, Error>;
}
impl SamplerFactory for Sampler {
@ -15,30 +27,31 @@ impl SamplerFactory for Sampler {
params: &ParameterDictionary,
full_res: Point2i,
loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
match name {
"zsobol" => {
let sampler = ZSobolSampler::create(params, full_res, loc)?;
let sampler = ZSobolSampler::create(params, full_res, loc, arena)?;
Ok(Sampler::ZSobol(sampler))
}
"paddedsobol" => {
let sampler = PaddedSobolSampler::create(params, full_res, loc)?;
let sampler = PaddedSobolSampler::create(params, full_res, loc, arena)?;
Ok(Sampler::PaddedSobol(sampler))
}
"halton" => {
let sampler = HaltonSampler::create(params, full_res, loc)?;
let sampler = HaltonSampler::create(params, full_res, loc, arena)?;
Ok(Sampler::Halton(sampler))
}
"sobol" => {
let sampler = SobolSampler::create(params, full_res, loc)?;
let sampler = SobolSampler::create(params, full_res, loc, arena)?;
Ok(Sampler::Sobol(sampler))
}
"Independent" => {
let sampler = IndependentSampler::create(params, full_res, loc)?;
let sampler = IndependentSampler::create(params, full_res, loc, arena)?;
Ok(Sampler::Independent(sampler))
}
"stratified" => {
let sampler = StratifiedSampler::create(params, full_res, loc)?;
let sampler = StratifiedSampler::create(params, full_res, loc, arena)?;
Ok(Sampler::Stratified(sampler))
}
_ => Err(format!("Film type '{}' unknown at {}", name, loc)),

View file

@ -12,27 +12,27 @@ use image_rs::Primitive;
use parking_lot::Mutex;
use shared::Float;
use shared::core::camera::{Camera, CameraTransform};
use shared::core::color::LINEAR;
// use shared::core::color::LINEAR;
use shared::core::film::{Film, FilmTrait};
use shared::core::filter::Filter;
use shared::core::geometry::{Point3f, Vector3f};
use shared::core::geometry::Vector3f;
use shared::core::lights::Light;
use shared::core::material::Material;
use shared::core::medium::{Medium, MediumInterface};
use shared::core::options::RenderingCoordinateSystem;
use shared::core::primitive::{Primitive, PrimitiveTrait};
use shared::core::primitive::PrimitiveTrait;
use shared::core::sampler::Sampler;
use shared::core::spectrum::{Spectrum, SpectrumType};
use shared::core::spectrum::SpectrumType;
use shared::core::texture::{FloatTexture, SpectrumTexture};
use shared::images::Image;
use shared::spectra::RGBColorSpace;
use shared::utils::error::FileLoc;
use shared::utils::math::SquareMatrix;
// use shared::utils::math::SquareMatrix;
use shared::utils::transform::{AnimatedTransform, Transform, look_at};
use std::collections::{HashMap, HashSet};
use std::ops::{Index as IndexTrait, IndexMut as IndexMutTrait};
use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering};
// use std::sync::atomic::{AtomicI32, Ordering};
#[derive(Clone, Debug)]
pub enum MaterialRef {
@ -1000,6 +1000,7 @@ struct GraphicsState {
pub current_inside_medium: String,
pub current_outside_medium: String,
pub current_material_name: String,
pub current_material_index: Option<usize>,
pub area_light_name: String,
pub area_light_params: ParsedParameterVector,
pub area_light_loc: FileLoc,
@ -1064,6 +1065,7 @@ impl BasicSceneBuilder {
named_coordinate_systems: HashMap::new(),
active_instance_definition: None,
shapes: Vec::new(),
animated_shapes: Vec::new(),
named_material_names: HashSet::new(),
medium_names: HashSet::new(),
@ -1526,8 +1528,9 @@ impl ParserTarget for BasicSceneBuilder {
}
}
fn material(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
todo!()
fn material(&mut self, _name: &str, _params: &ParsedParameterVector, loc: FileLoc) {
self.verify_world("material", loc);
self.graphics_state.current_material_name = self.scene.add_material(name, material)
}
fn make_named_material(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
todo!()

View file

@ -1,16 +1,15 @@
use crate::core::texture::FloatTexture;
use crate::shapes::TriQuadMesh;
use crate::shapes::{BilinearPatchMeshHost, TriQuadMesh, TriangleMeshHost};
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
use shared::core::options::get_options;
use shared::core::shape::*;
use shared::shapes::*;
use shared::spectra::*;
// use shared::spectra::*;
use shared::utils::Transform;
use shared::utils::mesh::{BilinearPatchMesh, TriangleMesh};
use std::sync::{Arc, Mutex};
pub static ALL_TRIANGLE_MESHES: Mutex<Vec<Arc<TriangleMeshHost>>> = Mutex::new(Vec::new());
pub static ALL_TRIANGLE_MESHES: Mutex<Vec<Arc<BilinearPatchMeshHost>>> = Mutex::new(Vec::new());
pub static ALL_BILINEAR_MESHES: Mutex<Vec<Arc<BilinearPatchMeshHost>>> = Mutex::new(Vec::new());
pub trait CreateShape {
fn create(

View file

@ -2,7 +2,6 @@ use crate::core::light::LightBaseTrait;
use crate::spectra::cie_y;
use crate::utils::containers::InternCache;
use shared::Float;
use shared::core::light::LightBase;
use shared::core::spectrum::Spectrum;
pub static SPECTRUM_CACHE: Lazy<Mutex<HashMap<String, Spectrum>>> =

View file

@ -1,7 +1,7 @@
use crate::textures::*;
use crate::utils::FileLoc;
use crate::utils::mipmap::MIPMapFilterOptions;
use crate::utils::{FileLoc, TextureParameterDictionary};
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
use enum_dispatch::enum_dispatch;
use shared::textures::*;
use shared::utils::Transform;
use std::sync::{Arc, Mutex, OnceLock};
@ -29,19 +29,25 @@ impl FloatTexture {
render_from_texture: &Transform,
params: &TextureParameterDictionary,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, String> {
match name {
"constant" => {
let tex = FloatConstantTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Constant(tex))
}
"scale" => Ok(FloatScaledTexture::create(render_from_texture, params, loc)),
"scale" => Ok(FloatScaledTexture::create(
render_from_texture,
params,
loc,
arena,
)),
"mix" => {
let tex = FloatMixTexture::create(render_from_texture, params, loc);
let tex = FloatMixTexture::create(render_from_texture, params, loc, arena);
Ok(FloatTexture::Mix(tex))
}
"directionmix" => {
let tex = FloatDirectionMixTexture::create(render_from_texture, params, loc);
let tex = FloatDirectionMixTexture::create(render_from_texture, params, loc, arena);
Ok(FloatTexture::DirectionMix(tex))
}
"bilerp" => {
@ -50,7 +56,7 @@ impl FloatTexture {
}
"imagemap" => {
let tex = FloatImageTexture::create(render_from_texture, params, loc);
Ok(loatTexture::Image(tex))
Ok(FloatTexture::Image(tex))
}
"checkerboard" => {
let tex = FloatCheckerboardTexture::create(render_from_texture, params, loc);
@ -143,10 +149,11 @@ pub static TEXTURE_CACHE: OnceLock<Mutex<HashMap<TexInfo, Arc<MIPMap>>>> = OnceL
pub fn get_texture_cache() -> &'static Mutex<HashMap<TexInfo, Arc<MIPMap>>> {
TEXTURE_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
struct TexInfo {
filename: String,
filter_options: MIPMapFilterOptions,
wrap_mode: WrapMode,
encoding: ColorEncoding,
pub struct TexInfo {
pub filename: String,
pub filter_options: MIPMapFilterOptions,
pub wrap_mode: WrapMode,
pub encoding: ColorEncoding,
}

View file

@ -1,9 +1,7 @@
mod pipeline;
use pipeline::*;
use shared::core::bssrdf::{BSSRDFTrait, SubsurfaceInteraction};
use shared::core::bxdf::{BSDF, BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode};
use shared::core::bxdf::{BSDF, BxDFFlags, BxDFTrait, FArgs, TransportMode};
use shared::core::camera::Camera;
use shared::core::film::VisibleSurface;
use shared::core::geometry::{Bounds2i, Point2f, Point2i, Point3fi, Ray, Vector3f, VectorLike};
@ -15,7 +13,7 @@ use shared::core::medium::{MediumTrait, PhaseFunctionTrait};
use shared::core::options::get_options;
use shared::core::pbrt::{Float, SHADOW_EPSILON};
use shared::core::primitive::{Primitive, PrimitiveTrait};
use shared::core::sampler::{CameraSample, Sampler, SamplerTrait};
use shared::core::sampler::{Sampler, SamplerTrait};
use shared::lights::sampler::LightSamplerTrait;
use shared::lights::sampler::{LightSampler, UniformLightSampler};
use shared::shapes::ShapeIntersection;
@ -30,6 +28,8 @@ use shared::utils::sampling::{
use std::sync::Arc;
use crate::Arena;
#[derive(Clone, Debug)]
pub struct IntegratorBase {
pub aggregate: Arc<Primitive>,
@ -70,7 +70,13 @@ pub trait IntegratorTrait {
}
pub trait RayIntegratorTrait {
fn evaluate_pixel_sample(&self, p_pixel: Point2i, sample_ind: usize, sampler: &mut Sampler);
fn evaluate_pixel_sample(
&self,
p_pixel: Point2i,
sample_ind: usize,
sampler: &mut Sampler,
arena: &mut Arena,
);
fn li(
&self,
@ -78,6 +84,7 @@ pub trait RayIntegratorTrait {
lambda: &SampledWavelengths,
sampler: &mut Sampler,
visible_surface: bool,
arena: &mut Arena,
) -> (SampledSpectrum, Option<VisibleSurface>);
}
@ -201,8 +208,21 @@ impl SimplePathIntegrator {
}
impl RayIntegratorTrait for SimplePathIntegrator {
fn evaluate_pixel_sample(&self, p_pixel: Point2i, sample_ind: usize, sampler: &mut Sampler) {
pipeline::evaluate_pixel_sample(self, self.camera.as_ref(), sampler, p_pixel, sample_ind);
fn evaluate_pixel_sample(
&self,
p_pixel: Point2i,
sample_ind: usize,
sampler: &mut Sampler,
arena: &mut Arena,
) {
pipeline::evaluate_pixel_sample(
self,
self.camera.as_ref(),
sampler,
p_pixel,
sample_ind,
arena,
);
}
fn li(
@ -211,6 +231,7 @@ impl RayIntegratorTrait for SimplePathIntegrator {
lambda: &SampledWavelengths,
sampler: &mut Sampler,
_visible_surface: bool,
arena: &mut Arena,
) -> (SampledSpectrum, Option<VisibleSurface>) {
let mut l = SampledSpectrum::new(0.0);
let mut beta = SampledSpectrum::new(1.0);
@ -333,7 +354,7 @@ impl RayIntegratorTrait for PathIntegrator {
p_pixel: Point2i,
sample_ind: usize,
sampler: &mut Sampler,
scratch: &Bump,
arena: &mut Arena,
) {
pipeline::evaluate_pixel_sample(
self,
@ -341,7 +362,7 @@ impl RayIntegratorTrait for PathIntegrator {
sampler,
p_pixel,
sample_ind,
scratch,
arena,
);
}
@ -350,8 +371,8 @@ impl RayIntegratorTrait for PathIntegrator {
mut ray: Ray,
lambda: &SampledWavelengths,
sampler: &mut Sampler,
_scratch: &Bump,
visible_surface: bool,
_arena: &mut Arena,
) -> (SampledSpectrum, Option<VisibleSurface>) {
let mut l = SampledSpectrum::new(0.0);
let mut beta = SampledSpectrum::new(1.0);
@ -508,7 +529,7 @@ impl RayIntegratorTrait for SimpleVolPathIntegrator {
p_pixel: Point2i,
sample_ind: usize,
sampler: &mut Sampler,
scratch: &Bump,
arena: &mut Arena,
) {
pipeline::evaluate_pixel_sample(
self,
@ -516,7 +537,7 @@ impl RayIntegratorTrait for SimpleVolPathIntegrator {
sampler,
p_pixel,
sample_ind,
scratch,
arena,
);
}
@ -525,8 +546,8 @@ impl RayIntegratorTrait for SimpleVolPathIntegrator {
mut ray: Ray,
lambda: &SampledWavelengths,
sampler: &mut Sampler,
_scratch: &Bump,
_visible_surface: bool,
_arena: &mut Arena,
) -> (SampledSpectrum, Option<VisibleSurface>) {
let mut l = SampledSpectrum::new(0.);
let mut beta = 1.;
@ -786,7 +807,7 @@ impl RayIntegratorTrait for VolPathIntegrator {
p_pixel: Point2i,
sample_ind: usize,
sampler: &mut Sampler,
scratch: &Bump,
arena: &mut Arena,
) {
pipeline::evaluate_pixel_sample(
self,
@ -803,8 +824,8 @@ impl RayIntegratorTrait for VolPathIntegrator {
mut ray: Ray,
lambda: &SampledWavelengths,
sampler: &mut Sampler,
scratch: &Bump,
visible_surface: bool,
arena: &mut Arena,
) -> (SampledSpectrum, Option<VisibleSurface>) {
let mut l = SampledSpectrum::new(0.);
let mut beta = SampledSpectrum::new(1.);

View file

@ -1,5 +1,6 @@
use crate::core::image::Image;
use crate::core::{options::PBRTOptions, sampler::get_camera_sample};
use crate::spectra::get_spectra_context;
use indicatif::{ProgressBar, ProgressStyle};
use std::io::Write;
use std::path::Path;
@ -66,6 +67,7 @@ pub fn render<T>(
_base: &IntegratorBase,
camera: &Camera,
sampler_prototype: &Sampler,
arena: &mut Arena,
) where
T: RayIntegratorTrait,
{
@ -76,7 +78,14 @@ pub fn render<T>(
tile_sampler.start_pixel_sample(p_pixel, s_index, None);
evaluate_pixel_sample(integrator, camera, &mut tile_sampler, p_pixel, s_index);
evaluate_pixel_sample(
integrator,
camera,
&mut tile_sampler,
p_pixel,
s_index,
arena,
);
return;
}
@ -142,7 +151,14 @@ pub fn render<T>(
for p_pixel in tile_bounds {
for sample_index in wave_start..wave_end {
sampler.start_pixel_sample(*p_pixel, sample_index, None);
evaluate_pixel_sample(integrator, camera, &mut sampler, *p_pixel, sample_index);
evaluate_pixel_sample(
integrator,
camera,
&mut sampler,
*p_pixel,
sample_index,
arena,
);
}
}
@ -202,6 +218,7 @@ pub fn evaluate_pixel_sample<T: RayIntegratorTrait>(
sampler: &mut Sampler,
pixel: Point2i,
_sample_index: usize,
arena: &mut Arena,
) {
let mut lu = sampler.get1d();
if get_options().disable_wavelength_jitter {
@ -221,10 +238,11 @@ pub fn evaluate_pixel_sample<T: RayIntegratorTrait>(
let initialize_visible_surface = film.uses_visible_surface();
let (mut l, visible_surface) =
integrator.li(camera_ray.ray, &lambda, sampler, initialize_visible_surface);
integrator.li(camera_ray.ray, &lambda, sampler, initialize_visible_surface, arena);
l *= camera_ray.weight;
if l.has_nans() || l.y(&lambda).is_infinite() {
let std_spectra = get_spectra_context();
if l.has_nans() || l.y(&lambda, std_spectra).is_infinite() {
l = SampledSpectrum::new(0.);
}

View file

@ -1,5 +1,7 @@
pub mod core;
pub mod filters;
pub mod globals;
pub mod gpu;
pub mod integrators;
pub mod lights;
pub mod materials;
@ -8,3 +10,5 @@ pub mod shapes;
pub mod spectra;
pub mod textures;
pub mod utils;
pub use utils::arena::Arena;

View file

@ -1,22 +1,19 @@
use crate::core::image::{Image, ImageIO};
use crate::core::light::{CreateLight, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
use log::error;
use shared::Float;
use shared::core::ligh::{Light, LightBase, LightType};
use shared::core::medium::MediumInterface;
use shared::core::shape::Shape;
use shared::core::spectrum::{Spectrum, SpectrumTrait};
use shared::core::texture::SpectrumType;
use shared::core::texture::{FloatTextureTrait, TextureEvalContext};
use shared::lights::DiffuseAreaLight;
use shared::spectra::RGBColorSpace;
use shared::utils::Transform;
use std::sync::Arc;
use crate::core::image::{Image, ImageIO};
use crate::core::light::{CreateLight, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::{
FloatTexture, FloatTextureTrait, TextureEvalContext, TextureEvaluator,
UniversalTextureEvaluator,
};
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
use shared::utils::{Ptr, Transform};
pub trait CreateDiffuseLight {
fn new(
@ -59,7 +56,7 @@ impl CreateDiffuseLight for DiffuseAreaLight {
let base = LightBase::new(light_type, &render_from_light, &medium_interface);
let lemit = LightBase::lookup_spectrum(&le);
let lemit = lookup_spectrum(&le);
if let Some(im) = &image {
let desc = im
.get_channel_desc(&["R", "G", "B"])

View file

@ -2,13 +2,13 @@ use crate::core::light::{CreateLight, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use shared::core::geometry::{Vector3f, VectorLike};
use shared::core::geometry::{Point3f, Vector3f, VectorLike};
use shared::core::light::{Light, LightBase};
use shared::core::medium::{Medium, MediumInterface};
use shared::core::shape::Shape;
use shared::lights::DistantLight;
use shared::spectra::RGBColorSpace;
use shared::utils::{Ptr, Transform};
use shared::utils::Transform;
pub trait CreateDistantLight {
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self;
@ -53,7 +53,7 @@ impl CreateLight for DistantLight {
let mut scale = parameters.get_one_float("scale", 1);
let from = parameters.get_one_point3f("from", Point3f::new(0., 0., 0.));
let to = parameters.get_one_point3f("to", Point3f(0., 0., 1.));
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
let w = (from - to).normalize();
let (v1, v2) = w.coordinate_system();
let m: [Float; 16] = [

View file

@ -1,19 +1,18 @@
use crate::core::image::{Image, ImageIO, ImageMetadata};
use crate::core::image::{Image, ImageIO};
use crate::core::light::{CreateLight, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::lights::distant::CreateDistantLight;
use crate::utils::sampling::PiecewiseConstant2D;
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
use log::error;
use shared::Float;
use shared::core::image::ImageBase;
use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface};
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::lights::GoniometricLight;
use shared::spectra::RGBColorSpace;
use shared::utils::containers::Array2D;
use shared::utils::{Ptr, Transform};
pub trait CreateGoniometricLight {

View file

@ -1,5 +1,6 @@
use log::error;
use shared::Float;
use shared::core::geometry::{Bounds3f, Point2f};
use shared::core::geometry::Point2f;
use shared::core::light::{CreateLight, Light, LightBase, LightType};
use shared::core::medium::MediumInterface;
use shared::core::spectrum::Spectrum;

View file

@ -2,7 +2,6 @@ use crate::core::light::{CreateLight, LightBaseTrait, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use shared::core::geometry::VectorLike;
use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface};
use shared::core::shape::Shape;

View file

@ -1,8 +1,9 @@
use crate::core::image::{Image, ImageIO, ImageMetadata};
use crate::core::image::{Image, ImageIO};
use crate::core::light::CreateLight;
use crate::core::spectrum::spectrum_to_photometric;
use crate::spectra::colorspace::new;
use crate::utils::{Arena, ParameterDictionary, Ptr, Upload, resolve_filename};
use crate::utils::{Arena, ParameterDictionary, Upload, resolve_filename};
use log::error;
use shared::Float;
use shared::core::geometry::{Bounds2f, VectorLike};
use shared::core::image::ImageAccess;

View file

@ -1,18 +1,16 @@
use crate::core::image::{Image, ImageIO, ImageMetadata};
// use crate::core::image::{Image, ImageIO, ImageMetadata};
use crate::core::light::CreateLight;
use crate::core::spectrum::spectrum_to_photometric;
use crate::spectra::colorspace::new;
use crate::utils::{Arena, ParameterDictionary, Ptr, Upload, resolve_filename};
use shared::core::geometry::{Bounds2f, Frame, VectorLike};
use shared::core::image::ImageAccess;
use crate::utils::{Arena, ParameterDictionary};
use shared::core::geometry::{Frame, VectorLike};
use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::MediumInterface;
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::lights::{ProjectionLight, SpotLight};
use shared::lights::SpotLight;
use shared::spectra::RGBColorSpace;
use shared::utils::math::{radians, square};
use shared::utils::{Ptr, Transform};
use shared::utils::Transformk
use shared::utils::math::radians;
use shared::{Float, PI};
pub trait CreateSpotLight {
@ -32,7 +30,6 @@ impl CreateSpotLight for SpotLight {
medium_interface: MediumInterface,
le: Spectrum,
scale: shared::Float,
image: Ptr<Image>,
cos_falloff_start: Float,
total_width: Float,
) -> Self {
@ -98,6 +95,7 @@ impl CreateLight for SpotLight {
coneangle,
coneangle - conedelta,
);
arena.alloc(specific);
Ok(Light::Spot(specific))
}
}

View file

@ -1,11 +1,14 @@
use crate::core::image::Image;
use crate::core::material::CreateMaterial;
use crate::utils::{Arena, ParameterDictionary};
use crate::core::texture::SpectrumTexture;
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload};
use shared::core::material::Material;
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::materials::coated::*;
use shared::spectra::ConstantSpectrum;
use shared::textures::SpectrumConstantTexture;
use std::collections::HashMap;
impl CreateMaterial for CoatedDiffuseMaterial {
fn create(
@ -13,17 +16,17 @@ impl CreateMaterial for CoatedDiffuseMaterial {
normal_map: Option<Arc<Image>>,
named_materials: &HashMap<String, Material>,
loc: &FileLoc,
area: &mut Arena,
) -> Self {
arena: &mut Arena,
) -> Result<Material, Error> {
let reflectance = parameters
.get_spectrum_texture("reflectance", None, SpectrumType::Albedo)
.unwrap_or(SpectrumConstantTexture::new(Spectrum::Constant(
ConstantSpectrum::new(0.5),
.unwrap_or(Arc::new(SpectrumTexture::Constant(
SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))),
)));
let u_roughness = parameters
.get_float_texture_or_null("uroughness")
.unwrap_or_else(|| parameters.get_float_texture("roughness", 0.5));
.or_else(|| parameters.get_float_texture("roughness", 0.5));
let v_roughness = parameters
.get_float_texture_or_null("vroughness")
.unwap_or_else(|| parameters.get_float("roughness", 0.5));
@ -42,24 +45,27 @@ impl CreateMaterial for CoatedDiffuseMaterial {
let albedo = parameters
.get_spectrum_texture("albedo", None, SpectrumType::Albedo)
.unwrap_or_else(|| {
SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.)))
let default_spectrum = Spectrum::Constant(ConstantSpectrum::new(0.));
SpectrumTexture::Constant(SpectrumConstantTexture::new(default_spectrum))
});
let displacement = parameters.get_float_texture("displacement");
let displacement = parameters.get_float_texture("displacement", 0.);
let remap_roughness = parameters.get_one_bool("remaproughness", true);
arena.alloc(Self::new(
reflectance,
u_roughness,
v_roughness,
thickness,
albedo,
g,
let specific = CoatedDiffuseMaterial::new(
reflectance.upload(arena),
u_roughness.upload(arena),
v_roughness.upload(arena),
thickness.upload(arena),
albedo.upload(arena),
g.upload(arena),
eta,
displacement,
normal_map,
displacement.upload(arena),
normal_map.upload(arena),
remap_roughness,
max_depth,
n_samples,
));
);
Ok(Material::CoatedDiffuse(specific));
}
}
@ -73,7 +79,7 @@ impl CreateMaterial for CoatedConductorMaterial {
) -> Result<Self, String> {
let interface_u_roughness = parameters
.get_float_texture_or_null("interface.uroughness")
.unwrap_or_else(|| parameters.get_float_texture("interface.roughness", 0.));
.r_else(|| parameters.get_float_texture("interface.roughness", 0.));
let interface_v_roughness = parameters
.GetFloatTextureOrNull("interface.vroughness")
.unwrap_or_else(|| parameters.get_float_texture("interface.vroughness", 0.));
@ -123,8 +129,8 @@ impl CreateMaterial for CoatedConductorMaterial {
let displacement = parameters.get_float_texture_or_null("displacement");
let remap_roughness = parameters.get_one_bool("remaproughness", true);
let material = Self::new(
displacement,
normal_map,
displacement.upload(arena),
normal_map.upload(arena),
interface_u_roughness,
interface_v_roughness,
thickness,

View file

@ -1,76 +1,52 @@
use crate::core::material::CreateMaterial;
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
use shared::core::bxdf::HairBxDF;
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload};
use shared::bxdfs::HairBxDF;
use shared::core::material::Material;
use shared::core::spectrum::Spectrum;
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
use shared::core::texture::GPUFloatTexture;
use shared::materials::complex::*;
impl CreateMaterial for HairMaterial {
fn create(
parameters: &TextureParameterDictionary,
normal_map: Option<Arc<ImageBuffer>>,
named_materials: &HashMap<String, Material>,
_named_materials: &HashMap<String, Material>,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, String> {
) -> Result<Material, Error> {
let sigma_a = parameters.get_spectrum_texture_or_null("sigma_a", SpectrumType::Unbounded);
let reflectance = parameters
.get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo)
.or_else(|| parameters.get_spectrum_texture_or_null("color", SpectrumType::Albedo));
let eumelanin = parameters.get_float_texture_or_null("eumelanin");
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin");
let sigma_a = match (
sigma_a,
reflectance,
eumelanin.is_some() || pheomelanin.is_some(),
) {
(Some(s), Some(_), _) => {
warn(
loc,
r#"Ignoring "reflectance" parameter since "sigma_a" was provided."#,
);
Some(s)
}
(Some(s), _, true) => {
warn(
loc,
r#"Ignoring "eumelanin"/"pheomelanin" parameter since "sigma_a" was provided."#,
);
Some(s)
}
(Some(s), None, false) => Some(s),
let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
(None, Some(r), true) => {
warn(
loc,
r#"Ignoring "eumelanin"/"pheomelanin" parameter since "reflectance" was provided."#,
);
Some(r)
}
(None, Some(r), false) => Some(r),
(None, None, true) => None, // eumelanin/pheomelanin will be used
(None, None, false) => Some(SpectrumConstantTexture::new(Spectrum::RGBUnbounded(
RGBUnboundedSpectrum::new(HairBxDF::sigma_a_from_concentration(1.3, 0.0)),
))),
// Default distribution if nothing is spceified
let sigma_a = if sigma_a.is_none() && color.is_none() && !has_melanin {
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0);
Some(Arc::new(SpectrumConstantTexture::new(
Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(default_rgb)),
)))
} else {
sigma_a
};
let eta = parameters.get_flot_texture("eta", 1.55);
let beta_m = parameters.get_float_texture("beta_m", 0.3);
let beta_n = parameters.get_float_texture("beta_n", 0.3);
let alpha = parameters.get_float_texture("alpha", 2.);
let material = HairMaterial::new(
sigma_a,
reflectance,
eumelanin,
pheomelanin,
eta,
beta_m,
beta_n,
alpha,
sigma_a.upload(arena),
reflectance.upload(arena),
eumelanin.upload(arena),
pheomelanin.upload(arena),
eta.upload(arena),
beta_m.upload(arena),
beta_n.upload(arena),
alpha.upload(arena),
);
arena.alloc(material);
return material;
}
}

View file

@ -1,16 +1,11 @@
use crate::core::bssrdf::BSSRDF;
use crate::core::bxdf::{
BSDF, BxDF, CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF,
HairBxDF,
};
use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::math::clamp;
use shared::core::bsdf::BSDF;
use shared::core::bssrdf::BSSRDF;
use shared::core::material::{MaterialEvalContext, MaterialTrait};
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use shared::spectra::SampledWavelengths;
use shared::utils::Ptr;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
@ -22,7 +17,7 @@ pub struct ConductorMaterial {
pub u_roughness: Ptr<GPUFloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>,
pub remap_roughness: bool,
pub normal_map: *const Image,
pub normal_map: Ptr<Image>,
}
impl MaterialTrait for ConductorMaterial {

View file

@ -1,15 +1,12 @@
use crate::core::bssrdf::BSSRDF;
use crate::core::bxdf::{
BSDF, BxDF, CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF,
HairBxDF,
};
use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::clamp;
use shared::core::bsdf::BSDF;
use shared::core::bssrdf::BSSRDF;
use shared::core::bxdf::BxDF;
use shared::core::material::{MaterialEvalContext, MaterialTrait};
use shared::core::spectrum::{Spectrum, SpectrumTrait};
use shared::core::texture::{GPUFloatTexture, TextureEvaluator};
use shared::spectra::SampledWavelengths;
use shared::utils::Ptr;
#[repr(C)]

View file

@ -1,16 +1,12 @@
use crate::core::bssrdf::BSSRDF;
use crate::core::bxdf::{
BSDF, BxDF, CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF,
HairBxDF,
};
use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::math::clamp;
use shared::core::bsdf::BSDF;
use shared::core::bssrdf::BSSRDF;
use shared::core::bxdf::BxDF;
use shared::core::material::{MaterialEvalContext, MaterialTrait};
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use shared::spectra::SampledWavelengths;
use shared::utils::Ptr;
#[repr(C)]
#[derive(Clone, Copy, Debug)]

View file

@ -1,17 +1,12 @@
use crate::core::bssrdf::BSSRDF;
use crate::core::bxdf::{
BSDF, BxDF, CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF,
HairBxDF,
};
use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::hash::hash_float;
use crate::utils::math::clamp;
use shared::core::bsdf::BSDF;
use shared::core::bssrdf::BSSRDF;
use shared::core::material::{Material, MaterialEvalContext, MaterialTrait};
use shared::core::texture::{GPUFloatTexture, TextureEvaluator};
use shared::spectra::SampledWavelengths;
use shared::utils::Ptr;
use shared::utils::hash::hash_float;
#[repr(C)]
#[derive(Clone, Copy, Debug)]

View file

@ -1,14 +1,15 @@
use crate::core::sampler::SamplerFactory;
use crate::core::sampler::CreateSampler;
use crate::utils::{FileLoc, ParameterDictionary};
use shared::core::geometry::Point2i;
use shared::core::sampler::{HaltonSampler, RandomizeStrategy};
impl SamplerFactory for HaltonSampler {
impl CreateSampler for HaltonSampler {
fn create(
params: &ParameterDictionary,
full_res: Point2i,
loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
let options = get_options();
let nsamp = options
.quick_render
@ -32,6 +33,8 @@ impl SamplerFactory for HaltonSampler {
}
};
Ok(HaltonSampler::new(nsamp as u32, full_res, s, seed as u64))
let sampler = HaltonSampler::new(nsamp as u32, full_res, s, seed as u64);
arena.alloc(sampler);
Ok(sampler)
}
}

View file

@ -1,14 +1,15 @@
use crate::core::sampler::SamplerFactory;
use crate::core::sampler::CreateSampler;
use crate::utils::{FileLoc, ParameterDictionary};
use shared::core::geometry::Point2i;
use shared::core::sampler::IndependentSampler;
impl SamplerFactory for IndependentSampler {
impl CreateSampler for IndependentSampler {
fn create(
params: &ParameterDictionary,
_full_res: Point2i,
_loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
let options = get_options();
let nsamp = options
.quick_render
@ -16,6 +17,8 @@ impl SamplerFactory for IndependentSampler {
.or(options.pixel_samples)
.unwrap_or_else(|| params.get_one_int("pixelsamples", 16));
let seed = params.get_one_int("seed", options.seed);
Ok(Self::new(nsamp as usize, seed as u64))
let sampler = Self::new(nsamp as usize, seed as u64);
arena.alloc(sampler);
Ok(sampler)
}
}

View file

@ -1,14 +1,15 @@
use crate::core::sampler::SamplerFactory;
use crate::core::sampler::CreateSampler;
use crate::utils::{FileLoc, ParameterDictionary};
use shared::core::geometry::Point2i;
use shared::core::sampler::{PaddedSobolSampler, RandomizeStrategy, SobolSampler, ZSobolSampler};
impl SamplerFactory for SobolSampler {
impl CreateSampler for SobolSampler {
fn create(
params: &ParameterDictionary,
full_res: Point2i,
loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
let options = get_options();
let nsamp = options
.quick_render
@ -26,16 +27,19 @@ impl SamplerFactory for SobolSampler {
}
};
Ok(Self::new(nsamp as usize, full_res, s, Some(seed as u64)))
let sampler = Self::new(nsamp as usize, full_res, s, Some(seed as u64));
arena.alloc(sampler);
Ok(sampler)
}
}
impl SamplerFactory for PaddedSobolSampler {
impl CreateSampler for PaddedSobolSampler {
fn create(
params: &ParameterDictionary,
_full_res: Point2i,
loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
let options = get_options();
let nsamp = options
.quick_render
@ -56,16 +60,19 @@ impl SamplerFactory for PaddedSobolSampler {
}
};
Ok(Self::new(nsamp as u32, s, Some(seed as u64)))
let sampler = Self::new(nsamp as u32, s, Some(seed as u64));
arena.alloc(sampler);
Ok(sampler)
}
}
impl SamplerFactory for ZSobolSampler {
impl CreateSampler for ZSobolSampler {
fn create(
params: &ParameterDictionary,
full_res: Point2i,
loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
let options = get_options();
let nsamp = options
.quick_render
@ -86,11 +93,8 @@ impl SamplerFactory for ZSobolSampler {
}
};
Ok(ZSobolSampler::new(
nsamp as u32,
full_res,
s,
Some(seed as u64),
))
let sampler = ZSobolSampler::new(nsamp as u32, full_res, s, Some(seed as u64));
arena.alloc(sampler);
Ok(sampler)
}
}

View file

@ -1,14 +1,16 @@
use crate::core::sampler::SamplerFactory;
use crate::Arena;
use crate::core::sampler::CreateSampler;
use crate::utils::{FileLoc, ParameterDictionary};
use shared::core::geometry::{FileLoc, ParameterDictionary};
use shared::core::sampler::StratifiedSampler;
impl SamplerFactory for StratifiedSampler {
impl CreateSampler for StratifiedSampler {
fn create(
params: &ParameterDictionary,
_full_res: Point2i,
_loc: &FileLoc,
) -> Result<Self, String> {
arena: &mut Arena,
) -> Result<Self, Error> {
let options = get_options();
let jitter = params.get_one_bool("jitter", true);
let (x_samples, y_samples) = if options.quick_render {
@ -25,11 +27,14 @@ impl SamplerFactory for StratifiedSampler {
)
};
let seed = params.get_one_int("seed", options.seed);
Ok(Self::new(
let sampler = Self::new(
x_samples as u32,
y_samples as u32,
Some(seed as u64),
jitter,
))
);
arena.aloc(sampler);
Ok(sampler)
}
}

View file

@ -1,8 +1,9 @@
use crate::core::shape::{ALL_BILINEAR_MESHES, CreateMesh, CreateShape};
use crate::core::shape::{ALL_BILINEAR_MESHES, CreateMesh};
use crate::core::texture::FloatTexture;
use crate::shapes::mesh::Mesh;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use log::warn;
use shared::shapes::BilinearPatchShape;
use shared::shapes::mesh::Mesh;
use shared::utils::Transform;
use std::collections::HashMap;

View file

@ -1,13 +1,15 @@
use crate::Arena;
use crate::core::shape::CreateShape;
use crate::core::texture::FloatTexture;
use crate::utils::{FileLoc, ParameterDictionary};
use rayon::iter::split;
use shared::core::geometry::Normal3f;
use shared::shapes::{CurveCommon, CurveShape, CurveType, CylinderShape};
use shared::shapes::{CurveCommon, CurveShape, CurveType};
use shared::utils::Transform;
use shared::utils::splines::{
cubic_bspline_to_bezier, elevate_quadratic_bezier_to_cubic, quadratic_bspline_to_bezier,
};
use log::warn;
use std::collections::HashMap;
pub fn create_curve(
@ -58,6 +60,7 @@ impl CreateShape for CurveShape {
parameters: ParameterDictionary,
float_textures: HashMap<String, FloatTexture>,
loc: FileLoc,
arena: &mut Arena,
) -> Result<Vec<Shape>, String> {
let width = parameters.get_one_float("width", 1.0);
let width0 = parameters.get_one_float("width0", width);

View file

@ -1,10 +1,9 @@
use crate::utils::FileLoc;
use anyhow::{Context, Result as AnyResult, bail};
use ply_rs::parser::Parser;
use ply_rs::ply::{DefaultElement, Property};
use shared::utils::Transform;
use shared::utils::mesh::{BilinearPatchMesh, TriangleMesh};
use shared::utils::sampling::DevicePiecewiseConstant2D;
use std::collections::HashMap;
use std::fs::File;
use std::path::Path;
@ -18,7 +17,7 @@ pub struct TriQuadMesh {
pub quad_indices: Vec<u32>,
}
fn get_float(elem: &DefaultElement, key: &str) -> Result<f32> {
fn get_float(elem: &DefaultElement, key: &str) -> AnyResult<f32> {
match elem.get(key) {
Some(Property::Float(v)) => Ok(*v),
Some(Property::Double(v)) => Ok(*v as f32),
@ -27,7 +26,7 @@ fn get_float(elem: &DefaultElement, key: &str) -> Result<f32> {
}
}
fn get_int(elem: &DefaultElement, key: &str) -> Result<i32> {
fn get_int(elem: &DefaultElement, key: &str) -> AnyResult<i32> {
match elem.get(key) {
Some(Property::Int(v)) => Ok(*v),
Some(Property::UInt(v)) => Ok(*v as i32),
@ -48,7 +47,7 @@ fn get_float_any(elem: &DefaultElement, keys: &[&str]) -> Option<f32> {
None
}
fn get_list_uint(elem: &DefaultElement, key: &str) -> Result<Vec<u32>> {
fn get_list_uint(elem: &DefaultElement, key: &str) -> AnyResult<Vec<u32>> {
match elem.get(key) {
Some(Property::List_Int(vec)) => Ok(vec.iter().map(|&x| x as u32).collect()),
Some(Property::List_UInt(vec)) => Ok(vec.clone()),
@ -59,10 +58,14 @@ fn get_list_uint(elem: &DefaultElement, key: &str) -> Result<Vec<u32>> {
}
impl TriQuadMesh {
pub fn read_ply<P: AsRef<Path>>(filename: P) -> Result<Self> {
let filename_display = filename.as_ref().display().to_string();
pub fn read_ply<P: AsRef<Path>>(filename: P) -> AnyResult<Self> {
let path = filename.as_ref();
let filename_display = path.display().to_string();
let mut f = File::open(&filename)
.with_context(|| format!("Couldn't open PLY file \"{}\"", filename_display))?;
// Going to ply-rs
let p = Parser::<DefaultElement>::new();
let ply = p
.read_ply(&mut f)
@ -78,13 +81,13 @@ impl TriQuadMesh {
let has_normal =
first.contains_key("nx") && first.contains_key("ny") && first.contains_key("nz");
for v_elem in vertices {
// Read Position (Required)
// Read Position
let x = get_float(v_elem, "x")?;
let y = get_float(v_elem, "y")?;
let z = get_float(v_elem, "z")?;
mesh.p.push([x, y, z]);
// Read Normal (Optional)
// Read Normal
if has_normal {
let nx = get_float(v_elem, "nx").unwrap_or(0.0);
let ny = get_float(v_elem, "ny").unwrap_or(0.0);
@ -343,6 +346,12 @@ pub struct BilinearPatchMeshHost {
_storage: Box<BilinearMeshStorage>,
}
#[derive(Debug, Clone, Copy)]
pub struct TriangleMeshHost {
pub view: BilinearPatchMesh,
_storage: Box<BilinearMeshStorage>,
}
impl Deref for TriangleMeshHost {
type Target = TriangleMesh;
fn deref(&self) -> &Self::Target {

View file

@ -7,7 +7,7 @@ pub mod mesh;
pub mod sphere;
pub mod triangle;
pub use bilinear::*;
// pub use bilinear::*;
pub use mesh::*;
use std::sync::{Arc, Mutex};

View file

@ -2,6 +2,7 @@ use crate::core::shape::{ALL_TRIANGLE_MESHES, CreateShape};
use crate::core::texture::FloatTexture;
use crate::shapes::mesh::TriangleMeshHost;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use log::warn;
use shared::shapes::TriangleShape;
use shared::utils::Transform;
@ -9,8 +10,10 @@ impl CreateShape for TriangleShape {
fn create(
name: &str,
render_from_object: Transform,
_object_from_render: Transform,
reverse_orientation: bool,
parameters: ParameterDictionary,
_float_texture: HashMap<String, FloatTexture>,
loc: FileLoc,
arena: &mut Arena,
) -> Result<Mesh, String> {

View file

@ -1,20 +1,22 @@
use super::DenselySampledSpectrumBuffer;
use shared::core::color::RGBToSpectrumTable;
use shared::core::geometry::Point2f;
use shared::spectra::{RGBColorSpace, RGBToSpectrumTable};
use shared::utils::SquareMatrix;
use shared::spectra::RGBColorSpace;
use shared::utils::math::SquareMatrix;
pub struct RGBColorSpaceData {
_illuminant: DenselySampledBuffer,
_illuminant: DenselySampledSpectrumBuffer,
pub view: RGBColorSpace,
}
pub fn new(
impl RGBColorSpaceData {
pub fn new(
r: Point2f,
g: Point2f,
b: Point2f,
illuminant: DenselySampledBuffer,
illuminant: DenselySampledSpectrumBuffer,
rgb_to_spectrum_table: *const RGBToSpectrumTable,
) -> Self {
) -> Self {
let w_xyz: XYZ = illuminant.to_xyz();
let w = w_xyz.xy();
let r_xyz = XYZ::from_xyy(r, Some(1.0));
@ -45,6 +47,7 @@ pub fn new(
_illuminant: illuminant_buffer,
view,
}
}
}
pub fn get_named(name: &str) -> Result<Arc<RGBColorSpace>, String> {

View file

@ -1,16 +1,9 @@
use crate::spectra::{DenselySampledSpectrumBuffer, SpectrumData};
use crate::spectra::DenselySampledSpectrumBuffer;
use shared::Float;
use shared::core::cie::{CIE_S_LAMBDA, CIE_S0, CIE_S1, CIE_S2, CIE_X, CIE_Y, CIE_Z, N_CIES};
use shared::spectra::cie::{CIE_S_LAMBDA, CIE_S0, CIE_S1, CIE_S2, N_CIES};
use shared::spectra::{
BlackbodySpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, PiecewiseLinearSpectrum,
Spectrum,
};
use shared::utils::math::square;
use shared::core::spectrum::Spectrum;
use shared::spectra::PiecewiseLinearSpectrum;
use once_cell::sync::Lazy;
fn create_cie_buffer(data: &[Float]) -> Spectrum {
pub fn create_cie_buffer(data: &[Float]) -> Spectrum {
let buffer = PiecewiseLinearSpectrum::from_interleaved(data, false);
let spec = Spectrum::Piecewise(buffer.view);
DenselySampledSpectrumBuffer::from_spectrum(spec)

View file

@ -1,15 +1,15 @@
use crate::core::pbrt::Float;
use shared::Float;
use shared::spectra::{DenselySampledSpectrum, SampledSpectrum};
pub struct DenselySampledSpectrumBuffer {
pub view: DenselySampledSpectrum,
pub device: DenselySampledSpectrum,
_storage: Vec<Float>,
}
impl std::ops::Deref for DenselySampledSpectrumBuffer {
type Target = DenselySampledSpectrum;
fn deref(&self) -> &Self::Target {
&self.view
&self.device
}
}
@ -21,7 +21,7 @@ impl DenselySampledSpectrumBuffer {
values: values.as_ptr(),
};
Self {
view,
device: view,
_storage: values,
}
}

Some files were not shown because too many files have changed in this diff Show more