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:

list

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:
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
)