Repository navigation
Virtual scans
extensions/scan/virtual.mojo makes a scan of a rendered scene. A renderer that draws a scene well, with materials, shadows, water and woods, draws it slowly. A scan of the scene as splats draws it in milliseconds. The scan can be walked through, cut into parts, weighed and fitted as a building, as a scan of a real building can.

examples/wijnendale_scan.mojo made the picture's scan. It takes 24 pictures of Wijnendale Castle, each with its depth, and keeps one splat for each 15 cm cell of what they see. The pictures take about ten minutes on 24 CPU threads. The scan is a .splat file of 116.9 MB, 3,653,318 splats, in meters with y up and z south. A frame draws in 37 ms on the CPU at 800 by 600. On the GPU at 1920 by 1080 from 90 m, it draws in 35 ms.
.venv/bin/mojo run -I . examples/wijnendale_scan.mojo
.venv/bin/mojo run -I . examples/scan_view.mojo out/castle.png .cache/wijnendale/wijnendale.splat +y 24 0.22The file is in .cache/wijnendale/, which git ignores. Give the example a path that ends in .png, and it also draws the scan from above the woods to that picture. The gallery makes out/wijnendale-scan.png this way.
Each pixel of a picture is a point of the scene, and the picture's color is the color of the point.
- The point. The pixel's center and its stored depth, an NDC depth from -1 to 1, go back through the camera. The inverse of the projection times the view gives a point of the scene.
-
The surface. The points of the pixel's right and lower neighbors give two tangents. Their cross product is the normal, turned toward the camera. A pixel on the edge of a surface is left out. It has a neighbor with no depth, or a neighbor more than
edgeof the distance nearer or farther. A normal withingrazingof the line of sight also leaves it out. -
The splat. It lies in the surface. Its deviation along each tangent is
spreadof the pixel's footprint along it, and its deviation across the surface isthinof the smaller one. It has the pixel's color.
The views overlap. Each point goes into a cell, voxel on an edge. A cell keeps the sample that was seen best. That is the sample with the greatest cosine between the normal and the line of sight, over its distance. So a scan of 24 views holds a splat for each cell of the surface and no more.
var scan = VirtualScan(default_virtual_scan_options())
for view in views:
var image = draw_castle(castle, renderer, camera, cloud_cover, view)
_ = scan.add_view(image, camera.view_matrix(), camera.projection_matrix())
scan.write(".cache/wijnendale/wijnendale.splat")
var geometry = scan.geometry() # or the splats themselves| Option | Default | Meaning |
|---|---|---|
voxel |
0.15 m | The edge of a cell: the scan keeps one splat in each |
edge |
0.03 | The most that a neighbor can be nearer or farther, as a share of the distance, before the pixel is on an edge |
grazing |
0.2 | The least cosine between the line of sight and the normal |
spread |
0.7 | A splat's deviation, as a share of the pixel's footprint |
thin |
0.15 | A splat's deviation across the surface, as a share of the smaller deviation in it |
opacity |
240 | The opacity byte of every splat |
geometry gives the splats with covariances sa^2 a a^T + sb^2 b b^T + sn^2 n n^T, for the tangents a and b, the normal n, and the deviations. splat_bytes and write give the rows of a .splat file. A row has 32 bytes: the center, the three deviations, the color and a rotation. The rotation's bytes are 128 + 128 q. loaders.splat.parse_splat reads the rows back.
The scan's cameras stand on three rings round the middle of the castle. The first ring has 8 views at a radius of 150 m and a height of 0.9 radii. The second has 8 at 105 m and 0.45 radii, and the third 8 at 75 m and 0.22 radii. The views are 800 by 800 pixels with a field of view of 50 degrees. The sun is the sun of 15:00 on 19 October 2026 at Torhout. The scan keeps the woods as well, so the box of its splats is 504 m by 516 m by 29 m.
Use UP of +y when you draw it. The scene's y is up and its z is south, so a frame of up +y and north -z reads it.
- The light is baked. The shadows, the sun and the reflections in the water are those of the pictures. A splat takes the color of its best view, with no spherical harmonics, so the water does not change as the camera moves.
- A surface that no view saw has no splat. The scan holds the outside of the castle and the ground. It holds no interior, no underside of a roof and nothing behind a tree that every view missed.
- The edge test drops the pixels on a silhouette. A thin object, such as a railing, loses some of its splats.
- The cells are of one size. A close view and a distant one are both held to 15 cm, so the detail of a near view is not kept. Use a smaller
voxeland more views for a scan that is drawn closer. - The render's supersampling blends the color of an edge pixel with the sky or the water behind it. The edge test drops most of these. Some remain as pale splats on a silhouette.
Tutorials
How-to
- Install
- Run the checks
- GPU backend
- Coverage
- Measure examples
- Add a feature
- Add a scanned building
- Write documentation
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- C ABI and hosts
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Warp drives
- Vegetation
- Village
- Streamline
- Animals
-
Buildings
- Numerics
- Building topology
- Building model
- IFC exchange
- Procedural towers
- Floor plans and interiors
- Frame analysis
- Shell analysis
- Building energy
- Construction kits
- Bearing walls
- Scanned buildings
- SOG files
- Compressed PLY files
- Scan parts
- Cleaning and orienting scans
- Walking through a scan
- Virtual scans
- Castle
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Mixamo animations
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Garments
- Outfits and armor
- Cloth and spring bones
- Attire estimates
- Segment inertia
- Process chemistry
- Thermochemical data
- Chemical equilibrium
- Process maps
- Rates and transport
- Electrolysis and film color
- Binary solutions
- Flowsheets and routes
- Materials and coatings
- Crystals and figures
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- Process temperatures
- Scans as building models
- CUDA teardown hang
- Mojo compiler hang