tylermorganwall / rayrender

A pathtracer for R. Build and render complex scenes and 3D data visualizations directly from R
http://www.rayrender.net
622 stars 42 forks source link

`segment` works badly with low FOV #31

Closed klmr closed 2 years ago

klmr commented 2 years ago

In trying to create an orthogonal projection of a cube’s outline I ran into an issue with the render quality of segments, which reduces quickly with lower FOV. By contrast, when creating the same object as a path object rather than a collection of segments, reducing the FOV does not impact render quality.

Here’s a comparison, based on the example from the path documentation; left: FOV = 20, right: FOV = 1 (camera position adjusted to capture the same scene):

path:

path

segments:

segments

Rendering via render_preview also exhibits this issue.

A quick fix might be to simply have segment generate a path object internally, but of course this wouldn’t solve the underlying issue, which presumably presents with any kind of cylinder object. Or maybe I’m doing something wrong? I’ve got approximately zero experience with path tracing (or 3D rendering in general) so this is actually very likely.

Full code ```r library(rayrender) segment_path = function (points) { seg = segment(points[[1L]], points[[2L]], radius = 0.05) for (i in seq_along(points)[-c(1L, 2L)]) { seg = add_object(seg, segment(points[[i - 1L]], points[[i]], radius = 0.05)) } seg } wave = list(c(-2, 1, 0), c(-1, -1, 0), c(0, 1, 0), c(1, -1, 0), c(2, 1, 0)) light = sphere(z = 5, x = 5, y = 5, radius = 2, material = light(intensity = 15)) scene_path = generate_studio(depth = -1.5) |> add_object(path(points = wave, straight = TRUE)) |> add_object(light) scene_seg = generate_studio(depth = -1.5) |> add_object(segment_path(points = wave)) |> add_object(light) render_my_scene = function (scene, fov, lookfrom) { render_scene( scene, width = 200L, height = 200L, fov = fov, samples = 1000L, min_variance = 0, lookfrom = lookfrom, parallel = TRUE ) } save_rendered_scene = function (filename, scene) { persp = render_my_scene(scene, fov = 20, lookfrom = c(0, 1, 10)) ortho = render_my_scene(scene, fov = 1, lookfrom = c(0, 20, 200)) png::writePNG(abind::abind(persp, ortho, along = 2L), filename) } save_rendered_scene('path.png', scene_path) save_rendered_scene('segments.png', scene_seg) ```
Session info ``` R version 4.1.1 (2021-08-10) Platform: x86_64-apple-darwin17.0 (64-bit) Running under: macOS Mojave 10.14.6 Matrix products: default BLAS: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRblas.0.dylib LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib locale: [1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8 attached base packages: [1] stats graphics grDevices utils datasets methods base other attached packages: [1] rayrender_0.24.0 nvimcom_0.9-122 loaded via a namespace (and not attached): [1] Rcpp_1.0.7 rstudioapi_0.13 raster_3.5-11 magrittr_2.0.1 tidyselect_1.1.1 lattice_0.20-44 R6_2.5.1 [8] rlang_0.4.11 fansi_0.5.0 dplyr_1.0.7 tools_4.1.1 parallel_4.1.1 grid_4.1.1 png_0.1-7 [15] utf8_1.2.2 cli_3.0.1 terra_1.4-22 ellipsis_0.3.2 abind_1.4-5 tibble_3.1.4 lifecycle_1.0.1 [22] crayon_1.4.1 purrr_0.3.4 vctrs_0.3.8 codetools_0.2-18 glue_1.4.2 sp_1.4-6 compiler_4.1.1 [29] pillar_1.6.3 generics_0.1.0 rayimage_0.6.2 pkgconfig_2.0.3 ```
tylermorganwall commented 2 years ago

This is a result of lack of float precision—moving farther away from the rendered results in less precision available for solving the ray-cylinder intersection equation. Paths don't suffer from this because they're rendered in a way that isn't affected by precision as badly as cylinders/segments.

You can build the package with the compilation flag -D RAY_FLOAT_AS_DOUBLEand it will use double precision for Floats, which will eliminate this issue. I've made a few updates that should make the compilation work, so update rayrender to the latest version (v0.25.2) and add the compilation flag (edit the Makevars file and add the compilation flag above).