Step-by-Step: World Wind Extruded Polygons Aligned to TerrainExtruded polygons aligned to terrain are a powerful visualization technique for geospatial applications: they let you represent 3D volumes (buildings, walls, bars, or thematic columns) that follow the real surface of the Earth. This guide walks through a clear, practical workflow for creating extruded polygons that stay properly aligned to terrain using NASA World Wind (Java or Web) as the base visualization platform. You’ll learn concepts, code examples, tips for accuracy and performance, and how to handle common pitfalls like clipping, lighting, and large datasets.
Overview and goals
This article covers:
- Key concepts: terrain sampling, altitude modes, extrusion geometry, and normals.
- Preparing data: polygon coordinates, heights (absolute or relative), and top/bottom attributes.
- Implementation approaches for World Wind Java and World Wind Web (Web WorldWind).
- Snapping polygons to terrain and computing extruded geometry.
- Visual enhancements: lighting, textures, outline/stroke, transparency.
- Performance and scalability best practices.
- Troubleshooting common issues.
By the end you’ll be able to render extruded polygons that conform to the terrain surface and display correctly from any camera angle.
Concepts you need to know
- Extrusion: extending a 2D polygon vertically to create a 3D prism (or tapered column).
- Terrain sampling (height sampling): querying the globe for elevation at given lat/lon points so the polygon base can conform to the surface.
- Altitude modes:
- Absolute: geometry uses elevations expressed relative to sea level.
- Relative-to-ground (or clamp-to-ground + offset): geometry positioned relative to terrain height.
- Ground-clamped: aligns directly to the surface, updating as terrain resolution changes.
- Normals: vectors used for proper lighting/shading of faces; must be computed for extruded side faces and top surfaces.
- Tesselation & triangulation: converting polygon outlines to triangles for rendering. For non-convex polygons and polygons with holes, robust triangulation is required.
- Level of Detail (LOD): reduce geometry complexity with distance to camera to maintain interactive frame rates.
Data preparation
- Coordinate format:
- Use (latitude, longitude) arrays in decimal degrees. For World Wind Java, many classes accept Position objects; Web WorldWind uses {lat, lon} or similar.
- Heights:
- Decide whether heights are absolute elevations (meters above mean sea level) or relative (meters above ground).
- Provide top height and bottom height; bottom will usually be the terrain elevation. If the user wants a wall from surface up N meters, set top = terrain + N and bottom = terrain (or terrain + offset).
- Holes and complex polygons:
- Represent polygons with outer boundary and optional inner rings for holes. Ensure your triangulation routine accepts this structure.
- Coordinate densification:
- For terrain that changes between vertices or steep slopes, densify edges (add intermediate points) so the polygon follows terrain contours more smoothly.
Approach summary (two main methods)
Method A — Geometry baked to terrain (precomputed):
- Sample terrain elevation at polygon vertices (and densified points) and compute a single extruded mesh using those elevations.
- Pros: simpler, efficient at runtime, predictable visuals.
- Cons: doesn’t update automatically for changing terrain datasets or very coarse sampling; can look detached at oblique views when terrain varies between vertices.
Method B — On-the-fly terrain following (dynamic):
- Attach polygon to terrain by sampling elevation continuously (or when camera/tiles change) and update mesh when underlying elevation changes.
- Pros: stays accurate when terrain LODs change; better for interactive editing and variable terrain.
- Cons: more complex; needs throttling and batching for performance.
World Wind Web (Web WorldWind) — Step-by-step implementation
- Set up Web WorldWind
- Use the official library and initialize a WorldWindow (canvas) with a globe and base layers.
- Sample terrain elevations
- Web WorldWind offers a method to request elevations for a list of locations. If not available in your build, query a terrain tile service or precompute DEMs.
Example (pseudocode; adapt to your API version):
// positions: [{lat, lon}] array worldWindow.globe.sampleTerrain(worldWindow.globe.terrainProvider, positions) .then(samples => { // samples: [{lat, lon, elevation}, ...] });
- Densify polygon edges
- Insert points every N meters along edges to better follow steep terrain. Use great-circle interpolation per segment.
- Build extruded mesh
- For each sampled vertex v: compute bottom vertex at elevation_e (terrain) + bottomOffset, top vertex at elevation_e + topOffset.
- Create side faces by connecting successive vertex pairs into quads (split into two triangles).
- Triangulate the top surface. Use an ear-clipping or robust polygon triangulation library for polygons with holes (e.g., earcut).
Example structure:
// verticesTriangles should contain positions for top and side triangles in counter-clockwise order const positions = []; // [x,y,z,...] const normals = []; // per-vertex normals const indices = []; // triangle index list // Fill positions/normals/indices for top surface (triangulated) and sides
- Compute normals
- Top: normals are approximately upward (may be adjusted if the top is sloped).
- Sides: compute normal for each side triangle using cross product of edge vectors; average normals for shared vertices to smooth shading if desired.
- Create a custom SurfaceShape/Renderable
- Use Web WorldWind’s Renderable or SurfaceShape subclass to render your custom mesh. Provide one draw call with the vertex buffer, normals, and indices.
- Apply material, texture, and lighting settings.
- Optimize
- Use index buffers and shared vertices to reduce memory.
- Combine adjacent polygons into a single mesh where possible.
- Use frustum culling and distance-based LOD: simplify geometry farther from the camera or replace with 2D representations.
World Wind Java — Step-by-step implementation
- Initialize World Wind and the Model
- Use a basic WorldWindowGLCanvas and add layers (placenames, BMNG, etc.).
- Sample terrain
- Use the Globe.getElevation or ElevationModel for sampling positions:
double elev = ww.getModel().getGlobe().getElevation(latitude, longitude);
- For batch queries, use an elevation model’s getElevations method if available.
- Densify edges
- Interpolate along geodesic segments; World Wind Java provides utility classes (e.g., LatLon.interpolate) or implement great-circle interpolation.
- Build the extruded geometry
- Create arrays of Vec4 or Position for top and bottom vertices.
- Use the Geometry or Shape API (e.g., SurfacePolygons are typically for draped 2D; for extrusions you’ll create a custom Renderable or use the AVList/Geometry classes).
- A common approach is to build a custom Geometry and a BasicShape or custom Renderable that uses the GPU Vertex Buffer Objects (VBOs).
- Triangulate
- Use an ear-clipping implementation or integrate a library (e.g., JTS Topology Suite can help triangulate polygons with holes—convert coordinates to JTS, then extract triangles).
- Normals & lighting
- Compute per-vertex normals. World Wind Java lighting expects normals for shaded surfaces.
- For flat shading, give each triangle a single normal; for smooth shading, average adjacent normals.
- Renderable setup
- Implement a class that extends AbstractShape or implements Renderable and overrides the render method to bind VBOs and issue GL draw calls.
- Respect the current globe transform: convert lat/lon/elevation to Cartesian (Vec4) using globe.computePointFromPosition or Position to Cartesian conversion.
- Interactivity (picking, highlighting)
- Implement shape picking by converting screen coordinates to a ray and intersecting with your extruded mesh triangles.
- Highlighting: update the material or add an outline mesh rendered with polygon offset to avoid z-fighting.
Example code snippets
Note: these are simplified pseudocode to illustrate core steps. Adapt to your World Wind version and graphics pipeline.
Web WorldWind: creating side faces (simplified)
for (let i = 0; i < verts.length; i++) { const aTop = topVerts[i]; const aBottom = bottomVerts[i]; const bTop = topVerts[(i+1)%verts.length]; const bBottom = bottomVerts[(i+1)%verts.length]; // two triangles per side quad pushTriangle(aTop, aBottom, bBottom); pushTriangle(aTop, bBottom, bTop); }
World Wind Java: convert Position to Cartesian
Vec4 cart = globe.computePointFromPosition( LatLon.fromDegrees(lat, lon), elevationMeters);
Triangulation (using earcut for Web):
// flatten polygon ring coordinates into a single array const flattened = []; for (const p of outerRing) { flattened.push(p.lon, p.lat); } const holeIndices = []; // if any holes const triangles = earcut(flattened, holeIndices, 2);
Visual enhancements
- Lighting: give top and side faces different materials (e.g., top brighter) and enable per-vertex normals for smooth shading.
- Outlines: render an outline mesh or draw GL lines along polygon edges. Use polygon offset or render the outline after the fill with depth testing adjusted to avoid z-fighting.
- Textures: apply a repeating texture to sides (brick, concrete) by assigning UVs based on vertical height and horizontal distance along perimeter.
- Transparency: sort and draw transparent extrusions after opaque geometry; depth-peeling is advanced but improves correctness.
- Labels: place labels above the top face using billboarded text or screen-space overlays.
Performance tips
- Batch geometry: merge multiple nearby extruded polygons into a single draw call when they share material.
- Reduce vertex count: avoid excessive densification; adapt densification based on curvature/steepness.
- LOD: generate simplified meshes for distant polygons (fewer vertices, no textures).
- Tile-based updating: only recompute meshes when their underlying terrain tiles change.
- Use GPU buffers (VBO/VertexArray) and indexed drawing to minimize CPU-GPU overhead.
- Throttle sampling: when dragging the globe, suspend expensive terrain re-sampling and update on interaction end or low-frequency intervals.
Common pitfalls and how to fix them
- Z-fighting between the bottom face and terrain:
- Offset the bottom slightly above the terrain or use polygon offset when rendering the extruded mesh.
- Holes rendering incorrectly:
- Ensure triangulation supports holes; test with counter-clockwise vs clockwise ring orientation.
- Poor lighting on steep sides:
- Compute per-vertex normals from geometry rather than assuming vertical normals.
- Misalignment after terrain LOD change:
- Listen for terrain tile/LOD events and recompute mesh or use method B (dynamic update).
- Visible seams between adjacent extrusions:
- Merge shared edges into one mesh or ensure identical vertex positions and normals at shared boundaries.
Example workflow for a simple building extrusion (summary)
- Get polygon footprint coordinates.
- Densify footprint edges for terrain fidelity.
- Sample terrain elevation at each vertex.
- Compute bottom = terrain + bottomOffset; top = bottom + height.
- Triangulate top surface; generate side faces as quads split to triangles.
- Compute normals for lighting.
- Create mesh buffers (positions, normals, indices, uvs).
- Add mesh to World Wind renderable layer.
- Optimize: batch nearby buildings, implement LOD, and limit sampling frequency.
Advanced topics and further improvements
- Procedural roof shapes: generate gabled or pyramidal roofs by altering top vertex heights based on building footprint and roof parameters.
- Boolean operations: cut extrusions with other geometry (e.g., creating tunnels) using CSG libraries.
- Physics and collision: integrate with a physics engine for interactive simulations (collisions, doors).
- Streaming DEMs for offline rendering: precompute per-building meshes against a known DEM for fast display when terrain service is remote.
- GPU tessellation: advanced pipelines can use tessellation shaders to generate vertical sides procedurally on the GPU.
Closing notes
Rendering extruded polygons aligned to terrain involves a blend of geodesy (great-circle interpolation and sampling), mesh generation (triangulation and normals), and performance engineering. Start with a baked geometry approach for simplicity, then move to dynamic updates if your application needs real-time accuracy with changing terrain LODs. With proper densification, triangulation, and attention to normals and z-fighting, World Wind can display convincing, interactive extruded features suited to many mapping and visualization use cases.
Leave a Reply