library(rgl)
open3d()
shade3d(mesh_vcg, col = "#C0C0C0", alpha = 0.9)
bg3d("white")
axes3d()
title3d("Aerial DWG — Translated Mesh")9 3D Visualisation
Numbers are useful; actually seeing the mesh is better. This chapter covers two routes: rgl (Adler et al. 2024) for interactive exploration in RStudio (spin it with the mouse, zoom in, inspect details), and rayshader (Morgan-Wall 2024) for rendering publication-quality static images.
Both work on mesh_vcg from Reading OBJ and STL Meshes.
9.1 Interactive 3D with rgl
shade3d() opens an OpenGL window where you can rotate and zoom with the mouse. Takes one line:
9.1.1 Embedding in HTML Output
rgl windows are interactive in RStudio but don’t embed in HTML documents on their own. rglwidget() wraps the scene in a self-contained WebGL widget that works in any browser:
# Add this once at the top of your document (setup chunk)
knitr::knit_hooks$set(webgl = rgl::hook_webgl)
open3d()
shade3d(mesh_vcg, col = "#4A90D9", alpha = 0.85)
rglwidget()The WebGL widget is fully self-contained in the rendered HTML. Readers can rotate and zoom without a running R server. It does require a modern browser (Chrome, Firefox, Safari, Edge all work fine).
9.2 Colouring by Height
Map vertex Z-values to a colour ramp to reveal the model’s vertical structure instantly. Blues for low, reds for high:
z_vals <- mesh_vcg$vb[3, ]
z_norm <- (z_vals - min(z_vals)) / diff(range(z_vals)) # 0–1
cols <- colorRampPalette(c("#2166ac", "#92c5de", "#f7f7f7",
"#f4a582", "#d6604d"))(100)
vert_cols <- cols[ceiling(z_norm * 99) + 1]
open3d()
shade3d(mesh_vcg, col = vert_cols)
rglwidget()9.3 Rendered Images with rayshader
rayshader renders the active rgl scene with ray-traced lighting and ambient occlusion, great for reports and presentations. The output is a PNG file.
library(rayshader)
vertices <- t(mesh_vcg$vb[1:3, ])
triangles <- t(mesh_vcg$it)
open3d()
triangles3d(vertices[triangles[, 1], ],
vertices[triangles[, 2], ],
vertices[triangles[, 3], ],
col = "steelblue")
render_snapshot(filename = "aerial_render.png",
width = 1200,
height = 900,
title_text = "Aerial DWG — Rayshader Render",
title_color = "white",
title_size = 24,
clear = TRUE)Include the render in your Quarto document with a standard image link:
9.4 Depth of Field
Add a depth-of-field effect to emphasise the model’s 3D structure in the output image, works especially well for tall structures:
render_depth(focus = 0.7,
focallength = 200,
filename = "aerial_dof.png")9.5 Combining Multiple Meshes
Overlay two meshes in one scene to compare them — useful when you have an original and a repaired or simplified version. Rvcg::vcgMerge() fuses tmesh3d objects into a single mesh; for separate colours, render them individually before merging:
library(Rvcg)
mesh_a <- vcgImport("aerial_v1.obj") # original
mesh_b <- vcgImport("aerial_v2.obj") # repaired
open3d()
shade3d(mesh_a, col = "#4A90D9", alpha = 0.8) # blue — original
shade3d(mesh_b, col = "#E74C3C", alpha = 0.6) # red — changes
title3d("v1 (blue) vs v2 (red)")
rglwidget()To merge them into one object (e.g., for a metrics pass on the combined geometry):
mesh_combined <- vcgMerge(mesh_a, mesh_b)
cat("Combined vertices:", ncol(mesh_combined$vb), "\n")9.6 Exporting the Scene
Save the interactive rgl scene as a standalone HTML file — no R session needed to view it:
open3d()
shade3d(mesh_vcg, col = "#4A90D9")
# Self-contained HTML with embedded WebGL
writeWebGL(dir = "webgl_export", filename = "aerial_viewer.html",
width = 900, height = 600)For a quick PNG without launching rayshader:
snapshot3d("aerial_snapshot.png", width = 1200, height = 900)9.7 Quick Comparison: rgl vs. rayshader
rgl |
rayshader |
|
|---|---|---|
| Output | Interactive HTML widget | Static PNG |
| Use for | Exploration, dashboards | Reports, presentations |
| Render time | Instant | Seconds–minutes |
| WebGL support | Yes (via rglwidget()) |
No |