Rendering Module
Blender scene setup and rendering functions.
Scene Setup
- src.terrain.scene_setup.clear_scene()[source]
Clear all objects from the Blender scene.
Resets the scene to factory settings (empty scene) and removes all default objects. Useful before importing terrain meshes to ensure a clean workspace.
- Raises:
RuntimeError – If Blender module (bpy) is not available.
- src.terrain.scene_setup.position_camera_relative(mesh_obj, direction='south', distance=1.5, elevation=0.5, look_at='center', camera_type='ORTHO', sun_angle=0, sun_energy=0, sun_azimuth=None, sun_elevation=None, focal_length=50, ortho_scale=1.2, distance_mode='diagonal', center_in_frame=True)[source]
Position camera relative to mesh(es) using intuitive cardinal directions.
Simplifies camera positioning by using natural directions (north, south, etc.) instead of absolute Blender coordinates. The camera is automatically positioned relative to the mesh bounds and rotated to point at the mesh center.
Supports multiple meshes by computing a combined bounding box that encompasses all provided mesh objects. This is useful for dual terrain renders or scenes with multiple terrain meshes that need to be viewed together.
- Parameters:
mesh_obj – Blender mesh object or list of mesh objects to position camera relative to. If a list is provided, a combined bounding box is computed.
direction –
Compass direction using 16-wind compass rose, one of: Cardinal: ‘north’, ‘south’, ‘east’, ‘west’ Primary intercardinal: ‘northeast’, ‘southeast’, ‘southwest’, ‘northwest’ Secondary intercardinal: ‘north-northeast’, ‘east-northeast’, ‘east-southeast’,
’south-southeast’, ‘south-southwest’, ‘west-southwest’, ‘west-northwest’, ‘north-northwest’
Special: ‘above’ (directly overhead), ‘above-tilted’ (overhead but angled) Default: ‘south’
distance – Distance multiplier relative to mesh diagonal (e.g., 1.5 means 1.5x mesh_diagonal away). Default: 1.5
elevation – Height as fraction of mesh diagonal added to Z position (0.0 = ground level, 1.0 = mesh_diagonal above ground). Default: 0.5
look_at – Where camera points - ‘center’ to point at mesh center, or tuple (x, y, z) for custom target. Default: ‘center’
camera_type – ‘ORTHO’ (orthographic) or ‘PERSP’ (perspective). Default: ‘ORTHO’
sun_angle – Angular diameter of sun in degrees (affects shadow softness). Default: 0 (no light)
sun_energy – Intensity of sun light. Default: 0 (no light created unless > 0)
sun_azimuth – Direction sun comes FROM in degrees (0=North, 90=East, 180=South, 270=West)
sun_elevation – Angle of sun above horizon in degrees (0=horizon, 90=overhead)
focal_length – Camera focal length in mm (perspective cameras only). Default: 50
ortho_scale – Multiplier for orthographic camera scale relative to mesh diagonal. Higher values zoom out (show more area), lower values zoom in. Only affects orthographic cameras. Default: 1.2
distance_mode –
How to interpret the distance parameter. Options: ‘diagonal’ (default): distance relative to mesh diagonal (scale-independent, backward compat) ‘fit’: photographer-friendly framing mode where distance=1.0 fits mesh in frame,
and camera distance adjusts with elevation to maintain framing
center_in_frame – If True, adjust look_at target to center the mesh’s 2D projection in the camera frame, not just point at the 3D geometric center. This accounts for perspective distortion and viewing angles. Default: True
- Returns:
Camera object
- Raises:
ValueError – If direction is not recognized or camera_type is invalid
Smart camera positioning using cardinal directions. Used in Great Lakes Elevation Visualization.
Example:
camera = position_camera_relative( mesh, direction='south', elevation=0.3, camera_type='ORTHO', ortho_scale=7.0 )
- src.terrain.scene_setup.setup_hdri_lighting(sun_elevation=30.0, sun_rotation=225.0, sun_intensity=1.0, sun_size=0.545, air_density=1.0, visible_to_camera=False, camera_background=None, sky_strength=None)[source]
Set up HDRI-style sky lighting using Blender’s Nishita sky model.
Creates realistic sky lighting that contributes to ambient illumination without being visible in the final render (by default).
The Nishita sky model provides physically-based atmospheric scattering for natural-looking outdoor lighting.
- Parameters:
sun_elevation (float) – Sun elevation angle in degrees (0=horizon, 90=overhead)
sun_rotation (float) – Sun rotation/azimuth in degrees (0=front, 180=back)
sun_intensity (float) – Multiplier for sun disc brightness in sky texture (default: 1.0)
sun_size (float) – Angular diameter of sun disc in degrees (default: 0.545 = real sun). Larger values create softer shadows, smaller values create sharper shadows.
air_density (float) – Atmospheric density (default: 1.0, higher=hazier)
visible_to_camera (bool) – If False, sky is invisible but still lights scene
camera_background (tuple) – RGB tuple for background color when sky is invisible. Default None = use transparent (black behind scene). Use (0.9, 0.9, 0.9) for light gray if using atmosphere.
sky_strength (float) – Overall sky emission strength (ambient light level). If None, defaults to sun_intensity for backwards compatibility.
- Returns:
The configured world object
- Return type:
bpy.types.World
Nishita sky model for realistic ambient lighting. Used in Combined Render: Full-Featured Example.
- src.terrain.scene_setup.setup_two_point_lighting(sun_azimuth=225.0, sun_elevation=30.0, sun_energy=7.0, sun_angle=1.0, sun_color=(1.0, 0.85, 0.6), fill_azimuth=45.0, fill_elevation=60.0, fill_energy=0.0, fill_angle=3.0, fill_color=(0.7, 0.8, 1.0))[source]
Set up two-point lighting with primary sun and optional fill light.
Creates professional-quality lighting for terrain visualization: - Primary sun: Creates shadows and defines form (warm color by default) - Fill light: Softens shadows, adds depth (cool color by default)
The warm/cool color contrast creates a natural outdoor lighting look similar to golden hour photography.
- Parameters:
sun_azimuth (float) – Direction sun comes FROM in degrees (0=N, 90=E, 180=S, 270=W). Default: 225° (southwest, afternoon sun)
sun_elevation (float) – Sun angle above horizon in degrees (0=horizon, 90=overhead). Default: 30° (mid-afternoon)
sun_energy (float) – Sun light strength. Default: 7.0
sun_angle (float) – Sun angular size in degrees (smaller=sharper shadows). Default: 1.0°
sun_color (tuple) – RGB tuple for sun color. Default: (1.0, 0.85, 0.6) warm golden
fill_azimuth (float) – Direction fill light comes FROM in degrees. Default: 45° (northeast, opposite sun)
fill_elevation (float) – Fill light angle above horizon in degrees. Default: 60° (higher angle for even fill)
fill_energy (float) – Fill light strength. Default: 0.0 (no fill light). Set to ~1-3 for subtle fill, ~5+ for strong fill.
fill_angle (float) – Fill light angular size in degrees. Default: 3.0° (softer than sun)
fill_color (tuple) – RGB tuple for fill color. Default: (0.7, 0.8, 1.0) cool blue
- Returns:
List of created light objects (1-2 lights depending on fill_energy)
- Return type:
Examples
>>> # Basic sun-only lighting >>> lights = setup_two_point_lighting(sun_azimuth=180, sun_elevation=45)
>>> # Sun with fill for softer shadows >>> lights = setup_two_point_lighting( ... sun_azimuth=225, sun_elevation=30, sun_energy=7, ... fill_energy=2, fill_azimuth=45, fill_elevation=60 ... )
>>> # Low sun for dramatic shadows >>> lights = setup_two_point_lighting(sun_elevation=10)
Traditional sun + fill lighting.
- src.terrain.scene_setup.create_background_plane(camera, mesh_or_meshes, distance_below=50.0, color='#F5F5F0', size_multiplier=2.0, receive_shadows=False, flat_color=False, roughness=1.0)[source]
Create a background plane for Blender terrain renders.
Creates a plane mesh positioned below the terrain that fills the camera view. The plane is sized to fill the camera’s frustum with a safety margin and positioned below the lowest point of the terrain mesh(es).
This is useful for adding a clean background color to terrain renders without drop shadows (by default) or with shadows for depth effect.
- Parameters:
camera – Blender camera object to size plane relative to
mesh_or_meshes – Single mesh object or list of mesh objects to position plane below. The plane will be positioned below the lowest Z point.
distance_below (float) – Distance below the lowest mesh point to place the plane (default: 50.0 units)
color (str | tuple) – Color for the background plane as hex string (e.g., “#F5F5F0”) or RGB tuple (default: eggshell white #F5F5F0)
size_multiplier (float) – How much larger than camera frustum to make the plane, for safety margin (default: 2.0, makes plane 2x frustum size)
receive_shadows (bool) – Whether the plane receives shadows from objects (default: False for clean background)
flat_color (bool) – If True, use emission shader for exact color that ignores scene lighting. If False (default), use Principled BSDF that responds to lighting (darker colors may appear lighter due to ambient light).
roughness (float) – Material roughness value 0.0-1.0 when flat_color=False (default: 1.0 = fully matte). Lower values (0.3-0.5) make the surface less affected by ambient light. Only used when flat_color=False.
- Returns:
Blender plane object with material applied and positioned
- Raises:
ValueError – If camera or mesh is invalid
RuntimeError – If called outside of Blender environment
- Return type:
Object
Rendering
- src.terrain.rendering.render_scene_to_file(output_path, width=1920, height=1440, file_format='PNG', color_mode='RGBA', compression=90, save_blend_file=True, show_progress=True, max_retries=3, retry_delay=5.0)[source]
Render the current Blender scene to file.
Includes automatic retry logic for GPU memory errors. If rendering fails due to CUDA/GPU memory exhaustion, the function will wait and retry up to max_retries times before giving up.
- Parameters:
output_path (str or Path) – Path where output file will be saved
width (int) – Render width in pixels (default: 1920)
height (int) – Render height in pixels (default: 1440)
file_format (str) – Output format ‘PNG’, ‘JPEG’, etc. (default: ‘PNG’)
color_mode (str) – ‘RGBA’ or ‘RGB’ (default: ‘RGBA’)
compression (int) – PNG compression level 0-100 (default: 90)
save_blend_file (bool) – Also save .blend project file (default: True)
show_progress (bool) – Show render progress updates (default: True). Logs elapsed time every 5 seconds during rendering.
max_retries (int) – Maximum number of retry attempts for GPU memory errors (default: 3). Set to 0 to disable retries.
retry_delay (float) – Seconds to wait between retry attempts (default: 5.0). Allows GPU memory to be freed by other processes.
- Returns:
Path to rendered file if successful, None otherwise
- Return type:
Path
Example:
result = render_scene_to_file( 'output.png', width=1920, height=1080, file_format='PNG' )
- src.terrain.rendering.setup_render_settings(use_gpu=True, samples=128, preview_samples=32, use_denoising=True, denoiser='OPTIX', compute_device='OPTIX', use_ambient_occlusion=True, ao_distance=1.0, ao_factor=1.0, use_persistent_data=False, use_auto_tile=False, tile_size=2048)[source]
Configure Blender render settings for high-quality terrain visualization.
- Parameters:
use_gpu (bool) – Whether to use GPU acceleration
samples (int) – Number of render samples
preview_samples (int) – Number of viewport preview samples
use_denoising (bool) – Whether to enable denoising
denoiser (str) – Type of denoiser to use (‘OPTIX’, ‘OPENIMAGEDENOISE’, ‘NLM’)
compute_device (str) – Compute device type (‘OPTIX’, ‘CUDA’, ‘HIP’, ‘METAL’)
use_ambient_occlusion (bool) – Enable ambient occlusion (darkens crevices)
ao_distance (float) – AO sampling distance (default: 1.0 Blender units)
ao_factor (float) – AO strength multiplier (default: 1.0)
use_persistent_data (bool) – Keep scene data in memory between frames (default: False)
use_auto_tile (bool) – Enable automatic tiling for large renders (default: False). Splits large images into smaller GPU-friendly tiles to reduce VRAM usage. Essential for print-quality renders (3000x2400+ pixels).
tile_size (int) – Tile size in pixels when auto_tile is enabled (default: 2048). Smaller tiles = less VRAM but slower rendering. Try 512-1024 for limited VRAM.
- Return type:
None
Materials
- src.terrain.blender_integration.apply_vertex_colors(mesh_obj, vertex_colors, y_valid=None, x_valid=None, n_surface_vertices=None, logger=None)[source]
Apply colors to an existing Blender mesh.
Accepts colors in either vertex-space (n_vertices, 3/4) or grid-space (height, width, 3/4). When grid-space colors are provided with y_valid/x_valid indices, colors are extracted for each vertex using those coordinates.
Uses Blender’s foreach_set for ~100x faster bulk operations.
- Parameters:
mesh_obj (bpy.types.Object) – The Blender mesh object to apply colors to
vertex_colors (np.ndarray) – Colors in one of two formats: - Vertex-space: shape (n_vertices, 3) or (n_vertices, 4) - Grid-space: shape (height, width, 3) or (height, width, 4)
y_valid (np.ndarray, optional) – Y indices for grid-space colors
x_valid (np.ndarray, optional) – X indices for grid-space colors
n_surface_vertices (int, optional) – Number of surface vertices. If provided, boundary vertices (index >= n_surface_vertices) will be skipped to preserve their existing colors (e.g., two-tier edge colors). Default: None (apply to all)
logger (logging.Logger, optional) – Logger for progress messages
- src.terrain.materials.apply_terrain_with_obsidian_roads(material, terrain_style=None, road_color='obsidian', terrain_material=None)[source]
Create a material with glossy roads and terrain colors/test material.
Reads from two vertex color layers: - “TerrainColors”: The actual terrain colors (used for non-road areas) - “RoadMask”: R channel marks road pixels (R > 0.5 = road)
Roads render with glossy dielectric properties (like polished stone). Non-road areas use either vertex colors or a test material.
- Parameters:
material (Material) – Blender material to configure
terrain_style (str | None) – Optional test material for terrain (“chrome”, “clay”, etc.) If None, uses vertex colors with terrain_material preset.
road_color (str | Tuple[float, float, float]) – Road color - either a preset name from ROAD_COLORS (“obsidian”, “azurite”, “azurite-light”, “malachite”, “hematite”) or an RGB tuple (0-1 range). Default: “obsidian” (near-black).
terrain_material (str | None) – Optional preset name for terrain material appearance when using vertex colors (terrain_style=None). One of: “matte”, “eggshell”, “satin”, “ceramic”, “lacquered”, “clearcoat”, “velvet”. Default: DEFAULT_TERRAIN_MATERIAL.
- Return type:
None
Used in Combined Render: Full-Featured Example for glossy road rendering.
- src.terrain.materials.apply_test_material(material, style)[source]
Apply a test material to the entire terrain mesh.
Test materials ignore vertex colors and apply a uniform material style for testing lighting, shadows, and mesh geometry.
- Parameters:
material (Material) – Blender material to configure
style (str) – Material style name - any color from ALL_COLORS ({get_all_colors_help()}) or “terrain” for normal vertex colors.
- Raises:
ValueError – If style is not recognized
- Return type:
None
Debug materials: obsidian, chrome, clay, plastic, gold.
Water Detection
- src.terrain.water.identify_water_by_slope(dem_data, slope_threshold=0.1, fill_holes=True)[source]
Identify water bodies by detecting flat areas (low slope).
Water bodies typically have very flat surfaces with near-zero slope. This function calculates local slope using Horn’s method and identifies pixels below the threshold as potential water. Optionally applies morphological operations to fill small gaps and smooth the water mask.
- Parameters:
dem_data (np.ndarray) – Digital elevation model as 2D array (height values)
slope_threshold (float) – Maximum slope magnitude to classify as water. Default: 0.1 (very flat surfaces) Typical range: 0.05 to 0.5 depending on DEM resolution Values are gradient magnitude from Horn’s method
fill_holes (bool) – Apply morphological operations to fill small gaps in water mask and smooth boundaries. Default: True
- Returns:
- Boolean mask (dtype=bool) where True = water, False = land.
Same shape as dem_data.
- Return type:
np.ndarray
- Raises:
ValueError – If dem_data is not 2D or slope_threshold is negative
Examples
>>> dem = np.array([[100, 100, 110], [100, 100, 110], [110, 110, 120]]) >>> water_mask = identify_water_by_slope(dem, slope_threshold=0.1) >>> water_mask.dtype dtype('bool') >>> water_mask.shape (3, 3)
Slope-based water detection. Used in Great Lakes Elevation Visualization.
Example:
water_mask = identify_water_by_slope( dem, slope_threshold=0.01, fill_holes=True )