Add enhanced terrain generation features#929
Merged
brendancol merged 4 commits intomasterfrom Mar 2, 2026
Merged
Conversation
…rley blending, and hydraulic erosion
New generate_terrain parameters: octaves, lacunarity, persistence,
noise_mode ('fbm'/'ridged'), warp_strength, warp_octaves,
worley_blend, worley_seed, erode, erosion_iterations, erosion_params.
Defaults reproduce existing behavior.
- perlin.py: add _perlin_gpu_xy kernel for non-linear coordinate arrays
- worley.py: new Worley (cellular) noise module with 4-backend support
- erosion.py: new particle-based hydraulic erosion module
- terrain.py: configurable octave loop, ridged multifractal, domain
warping, Worley blending, erosion post-pass across all 4 backends
- __init__.py: export erode
- tests: 24 tests covering each feature, cross-backend matching,
combined smoke tests, and input validation
- perlin.py: persist noise before computing min/ptp in both dask+numpy and dask+cupy paths, preventing a full recompute on normalization - terrain.py: persist warped coordinates before the octave loop so each iteration doesn't rebuild the warp subgraph; persist worley noise before min/max so the blend doesn't recompute it - terrain.py: pre-allocate scaled_x/scaled_y GPU buffers and use cupy.multiply(out=) instead of allocating temporaries per iteration
Use floor instead of int truncation for cell coordinates in perlin and worley noise. int() truncates toward zero, which gives wrong cell indices and negative fractional parts for coordinates below zero (common with domain warping). Switched to np.floor / math.floor throughout _perlin, _perlin_gpu, _perlin_gpu_xy, _worley_cpu, _worley_gpu, and _worley_gpu_xy. Fix _worley_gpu writing last-computed dist instead of min_dist. Fix per-chunk worley normalization in the dask+cupy terrain backend. Each chunk was normalizing worley noise with its own min/max, producing visible seams at chunk boundaries. Added a worley pre-pass that computes global min/max before the main terrain computation, and a worley_norm_range parameter on _terrain_gpu to accept externally computed bounds. Added chunk-boundary continuity tests with odd chunk sizes (13x17) for warp, worley, and combined features.
Add Worley noise and hydraulic erosion rows. Update Terrain Generation description to mention ridged noise, domain warping, Worley blending, and erosion options.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
generate_terrainparameters: ridged multifractal noise, domain warping, lacunarity/persistence, adaptive octave count, Worley noise blending, hydraulic erosionworley.pyanderosion.pymodules, all 4 backends (numpy, cupy, dask+numpy, dask+cupy)_perlin_gpu_xyCUDA kernel for non-linear coordinate arrays (domain warping needs this)What changed
generate_terrainnew parameters:octaves(int or None) -- None = adaptiveceil(log2(min(H,W)))lacunarity/persistence-- frequency and amplitude ratios per octavenoise_mode--'fbm'(default) or'ridged'for sharp ridgeswarp_strength/warp_octaves-- domain warpingworley_blend/worley_seed-- Worley cellular noiseerode/erosion_iterations/erosion_params-- particle-based hydraulic erosion post-passNew modules:
worley.py-- Worley noise:@ngjitCPU kernel, CUDA kernels (linear + xy variants), dask wrappers,ArrayTypeFunctionMappingdispatcherosion.py-- droplet simulation with bilinear gradient, inertia, sediment capacity, distance-weighted brush kernel. Materializes dask before processing since erosion is global.Modified modules:
perlin.py-- added_perlin_gpu_xykernel (same noise math, reads from 2D coordinate arrays instead of linear ranges)terrain.py-- rewrote octave loop for all 4 backends; added warping, ridged, and Worley stages. dask+cupy backend inlines the full pipeline into onemap_blockscallable.Test plan
pytest xrspatial/tests/test_terrain.py -v-- 24 tests: basic generation, each feature individually, cross-backend matching (numpy vs dask, numpy vs cupy), combined smoke tests, input validationpytest xrspatial/tests/test_perlin.py -v-- existing perlin tests still passgenerate_terrain(raster, noise_mode='ridged', warp_strength=0.4, worley_blend=0.1)on 512x512 should show ridged mountains with warping and rocky texture