Rendering And Analysis
This guide covers the rendering-side modules: what drives image generation, how terrain quality is configured, and where the lower-level helpers fit.
Viewer-First Rendering
For most users, forge3d rendering begins with the viewer:
open_viewer_async()launches the scene runtimeViewerHandleapplies terrain, camera, sun, overlay, and point-cloud changesterrain_paramsandpresetsdefine reproducible scene settingsanimationandcamera_rigsautomate camera motionterrain_scatteradds deterministic terrain population and related helpers
This is the path behind most of the terrain, overlay, point-cloud, and camera examples in the repo.
Terrain Configuration
forge3d.terrain_params is the central configuration module for advanced
terrain rendering. It groups settings into focused dataclasses such as:
lighting, shadows, fog, reflections, bloom, height AO, and sun visibility
material noise, POM, triplanar, and other terrain surface controls
LOD, sampling, clamp, denoise, and offline quality settings
terrain virtual-texture settings and overlay-related settings
Use it directly when you are building repeatable terrain render presets or
feeding TerrainRenderer.
DetailSettings supports a precomputed DEM detail-normal image on the native
terrain path. Set enabled=True, detail_normal_path="ridge.png", and
detail_strength in (0, 1]; normal_strength controls procedural detail
separately. The image covers the heightmap’s UV extent, with linear RGB normals
encoded as normal * 0.5 + 0.5: red points east (+U), green points south (+V,
increasing raster row), and blue points away from the surface. The renderer
uses a terrain-UV-aligned tangent frame and the existing distance fade. Water
does not receive detail normals.
Fade uses camera-to-fragment distance in terrain world units: contribution is
full at or below fade_start (default 50), smoothly decreases between the two
distances, and is zero at or beyond fade_end (default 200). For a map viewed
from kilometres away, set fade_end beyond the visible terrain’s distance;
set fade_start beyond it too if you want the full specified strength.
examples/terrain_demo.py forwards --detail-normals, --detail-strength,
and --detail-sigma-px, with --detail-fade-start and --detail-fade-end for
these distances. Its JSON detail_normals section accepts fade_start and
fade_end; explicit CLI values take precedence. P6 automatic generation uses
the packaged python -m forge3d._detail_normals encoder and raises an error
if generation fails. The source-tree scripts/detail_normals.py CLI remains available.
The generated map is saved beside the output image and uses --flip-y to
match the demo’s legacy DEM row flip. --overwrite applies to that generated
map too; an existing map is never silently reused for different DEM inputs.
scripts/detail_normals.py generates this convention from a DEM residual.
detail_sigma_px records the generation sigma; it does not request runtime
blurring. Active maps that cannot be read raise an error naming
detail_normal_path. Disabled detail and zero DEM strength do not load the map
and preserve the existing rendering path. The static terrain shader contract
continues to admit default-off micro-detail; enabled DEM detail is validated
by physical-render tests, not certified by that default-only proof profile.
Spatial orientation tests use a nadir mesh:zup view of the complete map.
Screen mode’s oversized triangle currently shows only UV [0, 0.5]²; use
constant-tilt inputs when checking channel orientation in that mode.
Native And Offscreen Rendering
forge3d exposes two lower-level rendering layers.
Scene
Use Scene when you want a compact native offscreen surface. It is suitable for
image generation, quick experiments, and targeted feature testing.
Session + TerrainRenderer
Use this stack when you need the full terrain-native pipeline:
Sessionowns the native runtime contextMaterialSetdefines terrain materialsIBLprovides environment mapsTerrainRenderParamsconfigures the terrain rendererTerrainRendererrenders frames, AOVs, and offline accumulation passes
terrain_atmosphere_path_demo.py is the clearest example of this lower-level
terrain path in the repo today.
Offline Accumulation And Denoise
forge3d.offline.render_offline() wraps the multi-batch accumulation flow
around TerrainRenderer. Use it when you want progressive offline quality
instead of a single interactive snapshot.
For the exact accumulation sequence, output contract, and denoise handoff, see
terrain/offline-render-quality.md.
forge3d.denoise_oidn.oidn_denoise() applies CPU denoise to beauty buffers,
optionally with albedo and normal guidance.
This pair is the main route for high-quality terrain output without building your own accumulation loop from scratch.
Geometry, Mesh, And Vector Helpers
These modules support scene construction before rendering:
forge3d.geometry: mesh generation, extrusion, subdivision, displacement, tangents, transforms, and validationforge3d.mesh: TBN generation and mesh-side utilitiesforge3d.io: DEM and mesh IO helpersforge3d.vector: 2D vector scene primitives and rendering helpers
They are not just internal plumbing. They are the intended building blocks when you need to generate or preprocess geometry in Python before passing it into a viewer or export pipeline.
Path Tracing, SDF, And Lighting
forge3d also exposes lower-level rendering utilities outside the terrain viewer:
forge3d.path_tracing: path-tracing helpers and camera creationforge3d.sdf: SDF primitives, builders, and scene helpersforge3d.lighting: lower-level lighting and ReSTIR-oriented helpersforge3d.materialsandforge3d.textures: material containers and supporting wrappers
These modules are more specialized than the viewer workflow, but they are part of the public package surface and documented in the API reference.
Match A Compatible Terrain Reference
recover_scene() is an optional matching tool, not something that runs when
you open or render a map. Give it a known terrain shape and a compatible image
made with Forge3D’s terrain renderer, and it can estimate the land colours,
sun position, sun strength, and haze that make a new render look like that
reference.
In 1.37.1, this matching is more dependable around ridges, the seams between terrain cells, and the borders of cast shadows. Those are places where a tiny change in the terrain or light can make a visible difference. If Forge3D cannot establish a trustworthy boundary calculation there, it rejects that calculation rather than treating a guess as an answer.
Existing maps are unchanged by this release. The improvement matters only when
you explicitly run recover_scene() again; a rerun can choose slightly
different land colours or lighting near those difficult boundaries. This is not
a general photo-to-map converter: it currently supports flat, terrain-only
Forge3D reference scenes with known terrain shape, not arbitrary aerial or
satellite photographs.
Diagnostics And Device Utilities
Use the top-level helpers and forge3d.mem when you need environment or memory
information:
has_gpu(),enumerate_adapters(),device_probe(),get_device()memory_metrics(),budget_remaining(),utilization_ratio()
These are useful when a script needs to decide between a viewer path, a native offscreen path, or a pure-Python fallback.
device_probe() never raises. It always returns a dict whose status field is
one of:
"ok"— an adapter was found; the dict carriesname,backend,device_type, andsoftware_fallback(True when the adapter is a software rasterizer such as WARP or lavapipe)."no_adapter"— the native module works but no hardware or software adapter is exposed for the requested backends."probe_error"— the probe itself failed;reasoncarries the exception."native_missing"— the compiledforge3d._forge3dextension did not import;reasonpreserves the original import error.
Every non-"ok" result includes reason and remediation fields, so scripts
can branch or report without string-matching exception text.
If the native module fails to import
When forge3d._forge3d cannot be imported (missing DLL, ABI mismatch, wheel
built for another interpreter), native-only names such as Scene,
TerrainRenderer, and Session are absent from the package. Accessing one
raises an AttributeError that preserves the root cause instead of a bare
name error:
forge3d.Scene requires the native extension, but the compiled extension
forge3d._forge3d failed to import: ImportError('DLL load failed ...').
Reinstall the wheel (pip install --force-reinstall forge3d) or rebuild from a
checkout with: maturin develop --release
Call forge3d.native_import_error() to retrieve the original import exception
programmatically, and device_probe() to get the same information as a
structured "native_missing" result.
If no GPU adapter is found
GPU acquisition never panics. forge3d first requests a hardware adapter and,
when none exists, automatically retries with the platform’s software fallback
adapter (WARP on Windows, Mesa’s lavapipe on Linux) so headless hosts keep
working — engine_info()["software_fallback"] reports honestly when the
software path is in use, and a warning is logged. Only when even the software
fallback is unavailable do GPU-touching calls raise a catchable
RuntimeError:
[Device] Device error: No suitable GPU adapter found (backends tried: DX12 |
VULKAN | ...; WGPU_BACKENDS/WGPU_BACKEND not set). A hardware adapter was not
found and the software fallback adapter is also unavailable. Verify GPU
drivers are installed, pin a backend via WGPU_BACKENDS (vulkan|dx12|metal|gl),
or install a software rasterizer for headless use (Windows ships WARP; on
Linux install Mesa's lavapipe).
Example Map
triangle_png.py: minimal native renderer sanity checkterrain_atmosphere_path_demo.py: full terrain-native rendering stackcamera_animation_demo.py: animation plus viewer-driven frame exportterrain_camera_rigs_demo.py: camera-rig automation on top of the viewerpnoa_river_showcase_video.py: terrain composition plus cinematic output