Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: move priority to objects, materials to separate list #188

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clovers-cli/src/json_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ pub(crate) fn initialize<'scene>(
let scene_file: SceneFile = serde_json::from_str(&contents)?;
info!("Initializing the scene");
let scene: Scene = scenes::initialize(scene_file, opts.width, opts.height);
info!("Count of nodes in the BVH tree: {}", scene.objects.count());
info!("Count of nodes in the BVH tree: {}", scene.hitables.count());
Ok(scene)
}
38 changes: 19 additions & 19 deletions clovers/src/bvhnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct BVHNode<'scene> {
impl<'scene> BVHNode<'scene> {
/// Create a new `BVHNode` tree from a given list of [Object](crate::objects::Object)s
#[must_use]
pub fn from_list(mut objects: Vec<Hitable>, time_0: Float, time_1: Float) -> BVHNode {
pub fn from_list(mut hitables: Vec<Hitable>, time_0: Float, time_1: Float) -> BVHNode {
// Initialize two child nodes
let left: Box<Hitable>;
let right: Box<Hitable>;
Expand All @@ -38,7 +38,7 @@ impl<'scene> BVHNode<'scene> {
// What is the axis with the largest span?
// TODO: horribly inefficient, improve!
let bounding: AABB =
vec_bounding_box(&objects, time_0, time_1).expect("No bounding box for objects");
vec_bounding_box(&hitables, time_0, time_1).expect("No bounding box for objects");
let spans = [
bounding.axis(0).size(),
bounding.axis(1).size(),
Expand All @@ -50,12 +50,12 @@ impl<'scene> BVHNode<'scene> {
let comparator = comparators[axis];

// How many objects do we have?
let object_span = objects.len();
let object_span = hitables.len();

if object_span == 1 {
// If we only have one object, add one and an empty object.
// TODO: can this hack be removed?
left = Box::new(objects[0].clone());
left = Box::new(hitables[0].clone());
right = Box::new(Hitable::Empty(Empty {}));
let bounding_box = left.bounding_box(time_0, time_1).unwrap().clone(); // TODO: remove unwrap
return BVHNode {
Expand All @@ -66,14 +66,14 @@ impl<'scene> BVHNode<'scene> {
} else if object_span == 2 {
// If we are comparing two objects, perform the comparison
// Insert the child nodes in order
match comparator(&objects[0], &objects[1]) {
match comparator(&hitables[0], &hitables[1]) {
Ordering::Less => {
left = Box::new(objects[0].clone());
right = Box::new(objects[1].clone());
left = Box::new(hitables[0].clone());
right = Box::new(hitables[1].clone());
}
Ordering::Greater => {
left = Box::new(objects[1].clone());
right = Box::new(objects[0].clone());
left = Box::new(hitables[1].clone());
right = Box::new(hitables[0].clone());
}
Ordering::Equal => {
// TODO: what should happen here?
Expand All @@ -82,29 +82,29 @@ impl<'scene> BVHNode<'scene> {
}
} else if object_span == 3 {
// Three objects: create one bare object and one BVHNode with two objects
objects.sort_by(comparator);
left = Box::new(objects[0].clone());
hitables.sort_by(comparator);
left = Box::new(hitables[0].clone());
right = Box::new(Hitable::BVHNode(BVHNode {
left: Box::new(objects[1].clone()),
right: Box::new(objects[2].clone()),
left: Box::new(hitables[1].clone()),
right: Box::new(hitables[2].clone()),
bounding_box: AABB::surrounding_box(
// TODO: no unwrap?
objects[1].bounding_box(time_0, time_1).unwrap(),
objects[2].bounding_box(time_0, time_1).unwrap(),
hitables[1].bounding_box(time_0, time_1).unwrap(),
hitables[2].bounding_box(time_0, time_1).unwrap(),
),
}));
} else {
// Otherwise, recurse
objects.sort_by(comparator);
hitables.sort_by(comparator);

// Split the vector; divide and conquer
let mid = object_span / 2;
let objects_right = objects.split_off(mid);
let hitables_right = hitables.split_off(mid);
left = Box::new(Hitable::BVHNode(BVHNode::from_list(
objects, time_0, time_1,
hitables, time_0, time_1,
)));
right = Box::new(Hitable::BVHNode(BVHNode::from_list(
objects_right,
hitables_right,
time_0,
time_1,
)));
Expand Down
7 changes: 5 additions & 2 deletions clovers/src/colorize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ pub fn colorize(

// Send the ray to the scene, and see if it hits anything.
// distance_min is set to an epsilon to avoid "shadow acne" that can happen when set to zero
let Some(hit_record) = scene.objects.hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng) else {
let Some(hit_record) = scene
.hitables
.hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng)
else {
// If the ray hits nothing, early return the background color.
return bg;
};
Expand Down Expand Up @@ -77,7 +80,7 @@ pub fn colorize(
// Use a probability density function to figure out where to scatter a new ray
// TODO: this weighed priority sampling should be adjusted or removed - doesn't feel ideal.
let light_ptr = PDF::HitablePDF(HitablePDF::new(
&scene.priority_objects,
&scene.priority_hitables,
hit_record.position,
));
let mixture_pdf = MixturePDF::new(light_ptr, scatter_record.pdf_ptr);
Expand Down
5 changes: 4 additions & 1 deletion clovers/src/normals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use rand::rngs::SmallRng;
/// Rendering function for getting a normal map in tangent space. Sends a [Ray] to the [Scene], sees what it hits, gets the normal at that point, and returns a color based on the normal mapping colorization. Wikipedia: [Normal mapping](https://en.wikipedia.org/wiki/Normal_mapping).
#[must_use]
pub fn normal_map(ray: &Ray, scene: &Scene, rng: &mut SmallRng) -> LinSrgb {
let Some(hit_record) = scene.objects.hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng) else {
let Some(hit_record) = scene
.hitables
.hit(ray, EPSILON_SHADOW_ACNE, Float::MAX, rng)
else {
// If the ray hits nothing, early return black
return LinSrgb::new(0.0, 0.0, 0.0);
};
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ pub use triangle::*;
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
/// A list of objects. Allows multiple objects to be used e.g. in a Rotate or Translate object as the target.
pub struct ObjectList {
/// Priority
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// The encased [Object] list
pub objects: Vec<Object>,
}
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/boxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use rand::{rngs::SmallRng, Rng};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
pub struct BoxyInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// First corner for the box
pub corner_0: Vec3,
/// Second, opposing corner for the box
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/constant_medium.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ use super::Object;
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
/// `ConstantMediumInit` structure describes the necessary data for constructing a [`ConstantMedium`]. Used with [serde] when importing [`SceneFiles`](crate::scenes::SceneFile).
pub struct ConstantMediumInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// The boundary object for the constant medium. This determines the size and shape of the fog object.
pub boundary: Box<Object>,
#[cfg_attr(feature = "serde-derive", serde(default = "default_density"))]
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/gltf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ use crate::{
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
pub struct GLTFInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// Path of the .gltf file
pub path: String,
}
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/moving_sphere.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use rand::rngs::SmallRng;
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
/// `SphereInit` structure describes the necessary data for constructing a [`Sphere`](super::Sphere). Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
pub struct MovingSphereInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// Center point of the sphere at time_0
pub center_0: Vec3,
/// Center point of the sphere at time_1
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/quad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use rand::Rng;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
pub struct QuadInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// Corner point
pub q: Vec3,
/// Vector describing the u side
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/rotate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use super::Object;
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
/// `RotateInit` structure describes the necessary data for constructing a [`RotateY`]. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
pub struct RotateInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// The encased [Object] to rotate
pub object: Box<Object>,
/// Angle to rotate the object, in degrees
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/sphere.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use rand::{rngs::SmallRng, Rng};
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
/// `SphereInit` structure describes the necessary data for constructing a [Sphere]. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
pub struct SphereInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// Center of the sphere.
pub center: Vec3,
/// Radius of the sphere.
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ impl<'scene> HitableTrait for STL<'scene> {
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
pub struct STLInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// Path of the .stl file
pub path: String,
/// Material to use for the .stl object
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use super::Object;
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
/// `TranslateInit` structure describes the necessary data for constructing a [Translate] object. Used with [serde] when importing [`SceneFile`](crate::scenes::SceneFile)s.
pub struct TranslateInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// The encased [Object] to translate i.e. move
pub object: Box<Object>,
/// The vector describing the movement of the object
Expand Down
3 changes: 3 additions & 0 deletions clovers/src/objects/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use rand::Rng;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
pub struct TriangleInit {
/// Used for multiple importance sampling
#[cfg_attr(feature = "serde-derive", serde(default))]
pub priority: bool,
/// Corner point
pub q: Vec3,
/// Vector describing the u side
Expand Down
56 changes: 34 additions & 22 deletions clovers/src/scenes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ use tracing::info;
/// A representation of the scene that is being rendered.
pub struct Scene<'scene> {
/// Bounding-volume hierarchy of [Hitable] objects in the scene. This could, as currently written, be any [Hitable] - in practice, we place the root of the [BVHNode] tree here.
pub objects: BVHNode<'scene>,
pub hitables: BVHNode<'scene>,
/// The camera object used for rendering the scene.
pub camera: Camera,
/// The background color to use when the rays do not hit anything in the scene.
pub background_color: Srgb, // TODO: make into Texture or something?
/// A [BVHNode] tree of prioritized objects - e.g. glass items or lights - that affect the biased sampling of the scene. Wrapped into a [Hitable] for convenience reasons (see various PDF functions).
pub priority_objects: Hitable<'scene>,
pub priority_hitables: Hitable<'scene>,
}

impl<'scene> Scene<'scene> {
Expand All @@ -33,16 +33,16 @@ impl<'scene> Scene<'scene> {
time_0: Float,
time_1: Float,
camera: Camera,
objects: Vec<Hitable<'scene>>,
priority_objects: Vec<Hitable<'scene>>,
hitables: Vec<Hitable<'scene>>,
priority_hitables: Vec<Hitable<'scene>>,
background_color: Srgb,
) -> Scene<'scene> {
Scene {
objects: BVHNode::from_list(objects, time_0, time_1),
hitables: BVHNode::from_list(hitables, time_0, time_1),
camera,
background_color,
priority_objects: Hitable::BVHNode(BVHNode::from_list(
priority_objects,
priority_hitables: Hitable::BVHNode(BVHNode::from_list(
priority_hitables,
time_0,
time_1,
)),
Expand All @@ -62,7 +62,6 @@ pub struct SceneFile {
objects: Vec<Object>,
#[cfg_attr(feature = "serde-derive", serde(default))]
materials: Vec<SharedMaterial>,
priority_objects: Vec<Object>,
}

/// Initializes a new [Scene] instance by parsing the contents of a [`SceneFile`] structure and then using those details to construct the [Scene].
Expand Down Expand Up @@ -90,26 +89,39 @@ pub fn initialize<'scene>(scene_file: SceneFile, width: u32, height: u32) -> Sce

#[cfg(feature = "traces")]
info!("Creating a flattened list from the objects");
let hitables: Vec<Hitable> = objects_to_hitables(scene_file.objects, materials);
let priority_objects: Vec<Hitable> =
objects_to_hitables(scene_file.priority_objects, materials);
let mut hitables: Vec<Hitable> = Vec::new();
let mut priority_hitables: Vec<Hitable> = Vec::new();

// TODO: this isn't the greatest ergonomics, but it gets the job done for now
for object in scene_file.objects {
if match &object {
Object::Boxy(i) => i.priority,
Object::ConstantMedium(i) => i.priority,
Object::MovingSphere(i) => i.priority,
Object::ObjectList(i) => i.priority,
Object::Quad(i) => i.priority,
Object::RotateY(i) => i.priority,
Object::Sphere(i) => i.priority,
Object::STL(i) => i.priority,
Object::GLTF(i) => i.priority,
Object::Translate(i) => i.priority,
Object::Triangle(i) => i.priority,
} {
let hitable = object_to_hitable(object, materials);
hitables.push(hitable.clone());
priority_hitables.push(hitable);
} else {
let hitable = object_to_hitable(object, materials);
hitables.push(hitable.clone());
}
}

Scene::new(
time_0,
time_1,
camera,
hitables,
priority_objects,
priority_hitables,
background_color,
)
}

#[must_use]
fn objects_to_hitables(objects: Vec<Object>, materials: &[SharedMaterial]) -> Vec<Hitable<'_>> {
let mut hitables = Vec::new();
for obj in objects {
hitables.push(object_to_hitable(obj, materials));
}

hitables
}
Loading