Skip to content

Commit

Permalink
Use image type for 3D rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
mkeeter committed Feb 17, 2025
1 parent 67c6185 commit cc749be
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Fix inconsistency between JIT and VM evaluator when performing interval
evaluation of `not([NAN, NAN])`.
- Propagate `NAN` values through `and` and `or` operations on intervals.
- Remove `Grad::to_rgb` in favor of handling it at the image level

# 0.3.4
- Add `GenericVmFunction::simplify_with` to simultaneously simplify a function
Expand Down
7 changes: 4 additions & 3 deletions demos/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,14 @@ fn run3d<F: fidget::eval::Function + fidget::render::RenderHints>(
};
let shape = shape.apply_transform(mat.into());

let mut depth = vec![];
let mut color = vec![];
let mut depth = Default::default();
let mut norm = Default::default();
for _ in 0..settings.n {
(depth, color) = cfg.run(shape.clone()).unwrap();
(depth, norm) = cfg.run(shape.clone()).unwrap();
}

let out = if mode_color {
let color = norm.to_color();
depth
.into_iter()
.zip(color)
Expand Down
3 changes: 2 additions & 1 deletion demos/viewer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ fn render<F: fidget::eval::Function + fidget::render::RenderHints>(
view: *view,
..Default::default()
};
let (depth, color) = config.run(shape).unwrap();
let (depth, norm) = config.run(shape).unwrap();
match mode {
Mode3D::Color => {
let color = norm.to_color();
for (p, (&d, &c)) in
pixels.iter_mut().zip(depth.iter().zip(&color))
{
Expand Down
15 changes: 0 additions & 15 deletions fidget/src/core/types/grad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,6 @@ impl Grad {
}
}

/// Returns a normalized RGB color, or `None` if the gradient is 0
pub fn to_rgb(&self) -> Option<[u8; 3]> {
let s = (self.dx.powi(2) + self.dy.powi(2) + self.dz.powi(2)).sqrt();
if s != 0.0 {
let scale = u8::MAX as f32 / s;
Some([
(self.dx.abs() * scale) as u8,
(self.dy.abs() * scale) as u8,
(self.dz.abs() * scale) as u8,
])
} else {
None
}
}

/// Absolute value
pub fn abs(self) -> Self {
if self.v < 0.0 {
Expand Down
8 changes: 4 additions & 4 deletions fidget/src/render/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
eval::Function,
render::{
Image, ImageSize, RenderConfig, RenderMode, TileSizes, View2, View3,
VoxelSize,
DepthImage, Image, ImageSize, NormalImage, RenderConfig, RenderMode,
TileSizes, View2, View3, VoxelSize,
},
shape::{Shape, ShapeVars},
};
Expand Down Expand Up @@ -216,7 +216,7 @@ impl VoxelRenderConfig<'_> {
pub fn run<F: Function>(
&self,
shape: Shape<F>,
) -> Option<(Vec<u32>, Vec<[u8; 3]>)> {
) -> Option<(DepthImage, NormalImage)> {
self.run_with_vars::<F>(shape, &ShapeVars::new())
}

Expand All @@ -225,7 +225,7 @@ impl VoxelRenderConfig<'_> {
&self,
shape: Shape<F>,
vars: &ShapeVars<f32>,
) -> Option<(Vec<u32>, Vec<[u8; 3]>)> {
) -> Option<(DepthImage, NormalImage)> {
crate::render::render3d::<F>(shape, vars, self)
}

Expand Down
53 changes: 53 additions & 0 deletions fidget/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,16 @@ impl<P> Image<P> {
pub fn iter(&self) -> impl Iterator<Item = &P> + '_ {
self.data.iter()
}

/// Returns the number of pixels in the image
pub fn len(&self) -> usize {
self.data.len()
}

/// Checks whether the image is empty
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}

impl<'a, P: 'a> IntoIterator for &'a Image<P> {
Expand All @@ -458,6 +468,14 @@ impl<'a, P: 'a> IntoIterator for &'a Image<P> {
}
}

impl<P> IntoIterator for Image<P> {
type Item = P;
type IntoIter = std::vec::IntoIter<P>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}

impl<P> std::ops::Index<usize> for Image<P> {
type Output = P;
fn index(&self, index: usize) -> &Self::Output {
Expand Down Expand Up @@ -510,3 +528,38 @@ impl<P> std::ops::IndexMut<(usize, usize)> for Image<P> {
&mut self.data[index]
}
}

/// Single-channel depth image
pub type DepthImage = Image<u32>;

/// Three-channel normal image
pub type NormalImage = Image<[f32; 3]>;

impl NormalImage {
/// Converts from floating-point normals to RGB colors
pub fn to_color(&self) -> ColorImage {
let mut data = Vec::with_capacity(self.width * self.height);
for [dx, dy, dz] in self.data.iter() {
let s = (dx.powi(2) + dy.powi(2) + dz.powi(2)).sqrt();
let rgb = if s != 0.0 {
let scale = u8::MAX as f32 / s;
[
(dx.abs() * scale) as u8,
(dy.abs() * scale) as u8,
(dz.abs() * scale) as u8,
]
} else {
[0; 3]
};
data.push(rgb);
}
ColorImage {
data,
width: self.width,
height: self.height,
}
}
}

/// Three-channel color image
pub type ColorImage = Image<[u8; 3]>;
2 changes: 1 addition & 1 deletion fidget/src/render/render2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ impl<'a, F: Function, M: RenderMode> RenderWorker<'a, F> for Worker<'a, F, M> {
fn new(cfg: &'a Self::Config) -> Self {
Worker::<F, M> {
scratch: Scratch::new(cfg.tile_sizes.last().pow(2)),
image: Image::default(),
image: Default::default(),
tile_sizes: &cfg.tile_sizes,
eval_float_slice: Default::default(),
eval_interval: Default::default(),
Expand Down
25 changes: 13 additions & 12 deletions fidget/src/render/render3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
eval::Function,
render::{
config::{Tile, VoxelRenderConfig},
RenderWorker, TileSizes, VoxelSize,
DepthImage, NormalImage, RenderWorker, TileSizes, VoxelSize,
},
shape::{Shape, ShapeBulkEval, ShapeTracingEval, ShapeVars},
types::{Grad, Interval},
Expand Down Expand Up @@ -64,21 +64,21 @@ struct Worker<'a, F: Function> {
workspace: F::Workspace,

/// Output images for this specific tile
depth: Vec<u32>,
color: Vec<[u8; 3]>,
depth: DepthImage,
color: NormalImage,
}

impl<'a, F: Function> RenderWorker<'a, F> for Worker<'a, F> {
type Config = VoxelRenderConfig<'a>;
type Output = (Vec<u32>, Vec<[u8; 3]>);
type Output = (DepthImage, NormalImage);

fn new(cfg: &'a Self::Config) -> Self {
let buf_size = cfg.tile_sizes.last();
let scratch = Scratch::new(buf_size);
Worker {
scratch,
depth: vec![],
color: vec![],
depth: Default::default(),
color: Default::default(),
tile_sizes: &cfg.tile_sizes,
image_size: cfg.image_size,

Expand All @@ -99,9 +99,9 @@ impl<'a, F: Function> RenderWorker<'a, F> for Worker<'a, F> {
tile: super::config::Tile<2>,
) -> Self::Output {
// Prepare local tile data to fill out
self.depth = vec![0; self.tile_sizes[0].pow(2)];
self.color = vec![[0u8; 3]; self.tile_sizes[0].pow(2)];
let root_tile_size = self.tile_sizes[0];
self.depth = DepthImage::new(root_tile_size, root_tile_size);
self.color = NormalImage::new(root_tile_size, root_tile_size);
for k in (0..self.image_size[2].div_ceil(root_tile_size as u32)).rev() {
let tile = Tile::new(Point3::new(
tile.corner.x,
Expand Down Expand Up @@ -325,7 +325,8 @@ impl<F: Function> Worker<'_, F> {
.unwrap();

for (index, o) in self.scratch.columns[0..grad].iter().enumerate() {
self.color[*o] = out[index].to_rgb().unwrap_or([255, 0, 0]);
let g = out[index];
self.color[*o] = [g.dx, g.dy, g.dz];
}
}
}
Expand All @@ -349,15 +350,15 @@ pub fn render<F: Function>(
shape: Shape<F>,
vars: &ShapeVars<f32>,
config: &VoxelRenderConfig,
) -> Option<(Vec<u32>, Vec<[u8; 3]>)> {
) -> Option<(DepthImage, NormalImage)> {
let shape = shape.apply_transform(config.mat());

let tiles = super::render_tiles::<F, Worker<F>>(shape, vars, config)?;

let width = config.image_size.width() as usize;
let height = config.image_size.height() as usize;
let mut image_depth = vec![0; width * height];
let mut image_color = vec![[0; 3]; width * height];
let mut image_depth = DepthImage::new(width, height);
let mut image_color = NormalImage::new(width, height);
for (tile, (depth, color)) in tiles {
let mut index = 0;
for j in 0..config.tile_sizes[0] {
Expand Down

0 comments on commit cc749be

Please sign in to comment.