# Cesium for Unity Cesium for Unity is a plugin that enables building 3D geospatial applications using Unity 3D with the 3D Tiles open standard. It provides a high-accuracy full-scale WGS84 globe, streaming of real-world 3D content from Cesium ion (terrain, imagery, buildings, photogrammetry), and integration with Unity's game objects, physics, and collisions. The plugin bridges global geospatial coordinate systems with Unity's local coordinate system through specialized components. The core architecture centers around the `CesiumGeoreference` component which defines how global Earth-Centered, Earth-Fixed (ECEF) coordinates map to Unity's local coordinate space. All Cesium tilesets and globe-anchored objects must be nested within a georeference. The `Cesium3DTileset` component streams 3D Tiles content either from Cesium ion or custom URLs, while `CesiumGlobeAnchor` allows any Unity game object to be positioned precisely on the globe with geographic coordinates. ## Core Components ### Cesium3DTileset - Stream 3D Tiles Content The `Cesium3DTileset` component streams 3D geospatial content from Cesium ion or custom URLs. It supports level-of-detail selection based on camera view, physics mesh generation, and raster overlay draping. Tiles are loaded progressively based on screen-space error and camera distance. ```csharp using CesiumForUnity; using UnityEngine; public class TilesetSetup : MonoBehaviour { void Start() { // Create the georeference parent object GameObject georefObject = new GameObject("Georeference"); CesiumGeoreference georeference = georefObject.AddComponent(); georeference.SetOriginLongitudeLatitudeHeight(-122.4194, 37.7749, 0); // San Francisco // Create tileset from Cesium ion GameObject tilesetObject = new GameObject("WorldTerrain"); tilesetObject.transform.parent = georefObject.transform; Cesium3DTileset tileset = tilesetObject.AddComponent(); // Configure to load Cesium World Terrain from ion tileset.tilesetSource = CesiumDataSource.FromCesiumIon; tileset.ionAssetID = 1; // Cesium World Terrain tileset.ionAccessToken = "YOUR_CESIUM_ION_ACCESS_TOKEN"; // Configure rendering quality tileset.maximumScreenSpaceError = 16.0f; // Lower = higher quality tileset.maximumSimultaneousTileLoads = 20; tileset.maximumCachedBytes = 512 * 1024 * 1024; // 512 MB cache tileset.createPhysicsMeshes = true; // Add event listener for load failures Cesium3DTileset.OnCesium3DTilesetLoadFailure += OnTilesetLoadFailure; // Add event listener for tile creation tileset.OnTileGameObjectCreated += OnTileCreated; } void OnTilesetLoadFailure(Cesium3DTilesetLoadFailureDetails details) { Debug.LogError($"Tileset load failed: {details.message}"); } void OnTileCreated(GameObject tileGameObject) { // Customize tiles as they load (add components, modify materials, etc.) tileGameObject.layer = LayerMask.NameToLayer("Terrain"); } } ``` ### CesiumGeoreference - Globe-to-Unity Coordinate Mapping The `CesiumGeoreference` component controls how global geospatial coordinates (ECEF) are mapped to Unity's local coordinate system. It creates a left-handed coordinate system where +X points East, +Y points up, and +Z points North at the specified origin location. All Cesium tilesets and globe-anchored objects must be children of this component. ```csharp using CesiumForUnity; using Unity.Mathematics; using UnityEngine; public class GeoreferenceExample : MonoBehaviour { void Start() { CesiumGeoreference georeference = gameObject.AddComponent(); // Set origin using longitude, latitude, height (degrees, degrees, meters) georeference.SetOriginLongitudeLatitudeHeight(-105.25737, 39.736401, 2250.0); // Alternative: Set origin using ECEF coordinates (meters) // georeference.SetOriginEarthCenteredEarthFixed(6378137.0, 0.0, 0.0); // Scale the entire globe (0.5 = half size in Unity) georeference.scale = 1.0; // Listen for origin changes georeference.changed += OnGeoreferenceChanged; } void OnGeoreferenceChanged() { Debug.Log("Georeference origin changed!"); } void ConvertCoordinates() { CesiumGeoreference georeference = GetComponent(); // Convert Unity position to ECEF double3 unityPosition = new double3(100, 50, 200); double3 ecefPosition = georeference.TransformUnityPositionToEarthCenteredEarthFixed(unityPosition); // Convert ECEF to Unity position double3 backToUnity = georeference.TransformEarthCenteredEarthFixedPositionToUnity(ecefPosition); // Convert directions double3 unityDir = new double3(0, 1, 0); // Up in Unity double3 ecefDir = georeference.TransformUnityDirectionToEarthCenteredEarthFixed(unityDir); } } ``` ### CesiumGlobeAnchor - Position Objects on the Globe The `CesiumGlobeAnchor` component anchors a game object to a specific geographic location. The object maintains its correct position on the globe even when the `CesiumGeoreference` origin changes. It supports positioning via longitude/latitude/height or ECEF coordinates and automatically adjusts orientation to keep objects upright as they move across the curved globe surface. ```csharp using CesiumForUnity; using Unity.Mathematics; using UnityEngine; public class GlobeAnchorExample : MonoBehaviour { void Start() { // Object must be a child of CesiumGeoreference CesiumGlobeAnchor anchor = gameObject.AddComponent(); // Position using longitude, latitude, height anchor.longitudeLatitudeHeight = new double3( -122.4194, // Longitude (degrees) 37.7749, // Latitude (degrees) 100.0 // Height above ellipsoid (meters) ); // Alternative: Position using ECEF coordinates // anchor.positionGlobeFixed = new double3(-2703674.0, -4261486.0, 3887604.0); // Auto-adjust orientation as object moves on curved Earth anchor.adjustOrientationForGlobeWhenMoving = true; // Detect and sync Transform changes automatically anchor.detectTransformChanges = true; // Get/set rotation relative to East-Up-North axes quaternion rotation = anchor.rotationEastUpNorth; anchor.rotationEastUpNorth = Quaternion.Euler(-15f, 0f, 0f); // Pitch down 15 degrees } void Update() { CesiumGlobeAnchor anchor = GetComponent(); // Move the object north by 1 meter per second double3 pos = anchor.longitudeLatitudeHeight; pos.y += 0.00001 * Time.deltaTime; // Small latitude change anchor.longitudeLatitudeHeight = pos; // Manually sync if detectTransformChanges is disabled // anchor.Sync(); } } ``` ### CesiumCameraController - Globe Navigation The `CesiumCameraController` provides intuitive globe navigation with WASD/QE movement, mouse look, and automatic speed adjustment based on altitude. It maintains proper orientation as the camera moves across the curved Earth and dynamically adjusts clipping planes for optimal rendering at any altitude. ```csharp using CesiumForUnity; using UnityEngine; public class CameraSetup : MonoBehaviour { void Start() { // Create camera hierarchy GameObject georefObject = new GameObject("Georeference"); CesiumGeoreference georeference = georefObject.AddComponent(); georeference.SetOriginLongitudeLatitudeHeight(-74.0060, 40.7128, 1000); // New York // Create camera with required components GameObject cameraObject = new GameObject("DynamicCamera"); cameraObject.transform.parent = georefObject.transform; Camera camera = cameraObject.AddComponent(); CesiumGlobeAnchor anchor = cameraObject.AddComponent(); CesiumOriginShift originShift = cameraObject.AddComponent(); CesiumCameraController controller = cameraObject.AddComponent(); // Position camera above the origin anchor.longitudeLatitudeHeight = new double3(-74.0060, 40.7128, 500); // Configure controller controller.enableMovement = true; controller.enableRotation = true; controller.enableDynamicSpeed = true; controller.dynamicSpeedMinHeight = 20.0f; controller.enableDynamicClippingPlanes = true; controller.dynamicClippingPlanesMinHeight = 10000.0f; } } ``` ### CesiumFlyToController - Smooth Globe Flights The `CesiumFlyToController` enables smooth animated flights between locations on the globe. It supports customizable flight duration, altitude profiles, and progress curves. Flights can be interrupted by user input and fire events on completion or interruption. ```csharp using CesiumForUnity; using Unity.Mathematics; using UnityEngine; public class FlyToExample : MonoBehaviour { private CesiumFlyToController flyToController; void Start() { // FlyToController requires CesiumGlobeAnchor and CesiumOriginShift flyToController = gameObject.AddComponent(); // Configure flight characteristics flyToController.flyToDuration = 5.0; // 5 seconds // Listen for flight events flyToController.OnFlightComplete += OnFlightComplete; flyToController.OnFlightInterrupted += OnFlightInterrupted; } public void FlyToNewYork() { // Fly to location using longitude, latitude, height double3 destination = new double3(-74.0060, 40.7128, 500); // NYC at 500m altitude float yaw = 0f; // Face north float pitch = -30f; // Look down 30 degrees bool canInterrupt = true; flyToController.FlyToLocationLongitudeLatitudeHeight( destination, yaw, pitch, canInterrupt); } public void FlyToECEF() { // Fly to location using ECEF coordinates double3 ecefDestination = new double3(-2703674.0, -4261486.0, 3887604.0); flyToController.FlyToLocationEarthCenteredEarthFixed( ecefDestination, 90f, -15f, true); } void OnFlightComplete() { Debug.Log("Flight completed!"); } void OnFlightInterrupted() { Debug.Log("Flight interrupted by user input"); } } ``` ### CesiumIonRasterOverlay - Imagery Draping The `CesiumIonRasterOverlay` drapes imagery from Cesium ion assets onto a `Cesium3DTileset`. Multiple overlays can be combined, with each overlay using a material key to match shader parameters. Overlays support configurable level-of-detail and texture size limits. ```csharp using CesiumForUnity; using UnityEngine; public class RasterOverlayExample : MonoBehaviour { void Start() { // Assume tileset already exists on this GameObject Cesium3DTileset tileset = GetComponent(); // Add Bing Maps aerial imagery CesiumIonRasterOverlay bingOverlay = gameObject.AddComponent(); bingOverlay.ionAssetID = 2; // Bing Maps Aerial bingOverlay.ionAccessToken = "YOUR_CESIUM_ION_ACCESS_TOKEN"; bingOverlay.materialKey = "0"; // Match with tileset material bingOverlay.maximumScreenSpaceError = 2.0f; bingOverlay.maximumTextureSize = 2048; // Add a second overlay (e.g., labels) with different key CesiumIonRasterOverlay labelsOverlay = gameObject.AddComponent(); labelsOverlay.ionAssetID = 3; // Bing Maps with labels labelsOverlay.ionAccessToken = "YOUR_CESIUM_ION_ACCESS_TOKEN"; labelsOverlay.materialKey = "1"; // Listen for overlay load failures CesiumRasterOverlay.OnCesiumRasterOverlayLoadFailure += OnOverlayLoadFailure; } void OnOverlayLoadFailure(CesiumRasterOverlayLoadFailureDetails details) { Debug.LogError($"Overlay load failed: {details.message}"); } void RefreshOverlay() { // Refresh the overlay (remove and re-add) CesiumIonRasterOverlay overlay = GetComponent(); overlay.Refresh(); } } ``` ### CesiumWgs84Ellipsoid - Coordinate Transformations The `CesiumWgs84Ellipsoid` provides static methods for coordinate transformations using the WGS84 ellipsoid standard. It converts between longitude/latitude/height and ECEF coordinates, computes surface normals, and provides ellipsoid geometry information. ```csharp using CesiumForUnity; using Unity.Mathematics; using UnityEngine; public class CoordinateConversion : MonoBehaviour { void Start() { // Get WGS84 ellipsoid radii (meters) double3 radii = CesiumWgs84Ellipsoid.GetRadii(); Debug.Log($"Ellipsoid radii: {radii}"); // ~6378137, 6378137, 6356752 double maxRadius = CesiumWgs84Ellipsoid.GetMaximumRadius(); double minRadius = CesiumWgs84Ellipsoid.GetMinimumRadius(); // Convert longitude/latitude/height to ECEF double3 lonLatHeight = new double3(-122.4194, 37.7749, 0); // San Francisco double3 ecef = CesiumWgs84Ellipsoid.LongitudeLatitudeHeightToEarthCenteredEarthFixed(lonLatHeight); Debug.Log($"ECEF: {ecef}"); // Convert ECEF back to longitude/latitude/height double3 backToLLH = CesiumWgs84Ellipsoid.EarthCenteredEarthFixedToLongitudeLatitudeHeight(ecef); Debug.Log($"LLH: {backToLLH}"); // Get surface normal at ECEF position (points "up" from surface) double3 surfaceNormal = CesiumWgs84Ellipsoid.GeodeticSurfaceNormal(ecef); // Scale a point to the ellipsoid surface double3? surfacePoint = CesiumWgs84Ellipsoid.ScaleToGeodeticSurface(ecef); if (surfacePoint.HasValue) { Debug.Log($"Surface point: {surfacePoint.Value}"); } } } ``` ### CesiumSubScene - Multi-Location Scenes The `CesiumSubScene` component enables placing relatively normal Unity scenes at multiple locations on the globe. When a `CesiumOriginShift` object enters a sub-scene's activation radius, that sub-scene becomes active and the georeference origin shifts to its location. This allows designing detailed local scenes without dealing with large coordinate values. ```csharp using CesiumForUnity; using UnityEngine; public class SubSceneSetup : MonoBehaviour { void Start() { // Main georeference GameObject georefObject = new GameObject("Georeference"); CesiumGeoreference georeference = georefObject.AddComponent(); // Create San Francisco sub-scene GameObject sfScene = new GameObject("SanFranciscoScene"); sfScene.transform.parent = georefObject.transform; CesiumSubScene sfSubScene = sfScene.AddComponent(); sfSubScene.SetOriginLongitudeLatitudeHeight(-122.4194, 37.7749, 0); sfSubScene.activationRadius = 5000; // 5km activation radius sfSubScene.showActivationRadius = true; // Show gizmo in editor // Add local content to the sub-scene GameObject localBuilding = GameObject.CreatePrimitive(PrimitiveType.Cube); localBuilding.transform.parent = sfScene.transform; localBuilding.transform.localPosition = new Vector3(100, 50, 0); // Create New York sub-scene GameObject nyScene = new GameObject("NewYorkScene"); nyScene.transform.parent = georefObject.transform; CesiumSubScene nySubScene = nyScene.AddComponent(); nySubScene.SetOriginLongitudeLatitudeHeight(-74.0060, 40.7128, 0); nySubScene.activationRadius = 5000; } } ``` ### CesiumOriginShift - Automatic Origin Management The `CesiumOriginShift` component automatically shifts the `CesiumGeoreference` origin to keep it near the attached object (typically a camera). This improves rendering precision by keeping coordinate values small. It also handles automatic switching between `CesiumSubScene` instances based on proximity. ```csharp using CesiumForUnity; using UnityEngine; public class OriginShiftSetup : MonoBehaviour { void Start() { // CesiumOriginShift requires CesiumGlobeAnchor CesiumGlobeAnchor anchor = gameObject.AddComponent(); CesiumOriginShift originShift = gameObject.AddComponent(); // Distance threshold before origin shifts (meters) // 0 = continuous shifting originShift.distance = 0.0; // With distance > 0, origin only shifts when object moves // beyond the threshold from current origin // originShift.distance = 1000.0; // Shift when 1km away } } ``` ### Height Sampling - Query Terrain Heights The `Cesium3DTileset` provides asynchronous height sampling to query terrain elevation at specified positions. This uses the most detailed tiles available and returns heights in meters above the WGS84 ellipsoid. ```csharp using CesiumForUnity; using Unity.Mathematics; using UnityEngine; using System.Threading.Tasks; public class HeightSamplingExample : MonoBehaviour { private Cesium3DTileset tileset; void Start() { tileset = GetComponent(); } public async void SampleHeights() { // Define positions to sample (X=longitude, Y=latitude, Z=height ignored for input) double3[] positions = new double3[] { new double3(-122.4194, 37.7749, 0), // San Francisco new double3(-74.0060, 40.7128, 0), // New York new double3(-87.6298, 41.8781, 0) // Chicago }; // Perform async height query CesiumSampleHeightResult result = await tileset.SampleHeightMostDetailed(positions); // Process results for (int i = 0; i < result.longitudeLatitudeHeightPositions.Length; i++) { if (result.sampleSuccess[i]) { double3 pos = result.longitudeLatitudeHeightPositions[i]; Debug.Log($"Position {i}: Height = {pos.z} meters"); } else { Debug.Log($"Position {i}: Height sampling failed"); } } } public void CheckLoadProgress() { // Get percentage of visible tiles loaded (0-100) float progress = tileset.ComputeLoadProgress(); Debug.Log($"Tileset loading: {progress:F1}%"); } } ``` ## Summary Cesium for Unity enables geospatial applications by providing a bridge between real-world geographic coordinates and Unity's local coordinate system. The primary use cases include: streaming massive 3D terrain and building datasets, placing virtual objects at precise real-world locations, creating globe-spanning simulations, and building location-based applications. The plugin integrates with Unity's physics, rendering, and input systems to allow standard Unity development patterns while handling the complexities of rendering a full-scale Earth. Integration typically follows a hierarchy pattern: a `CesiumGeoreference` at the root defines the mapping between globe and Unity coordinates, `Cesium3DTileset` components stream terrain and buildings as children, and `CesiumGlobeAnchor` components position user objects. For camera navigation, combine `CesiumGlobeAnchor`, `CesiumOriginShift`, `CesiumCameraController`, and `CesiumFlyToController` on the main camera. For multi-location scenarios, use `CesiumSubScene` to define local coordinate spaces at different geographic locations. All coordinate transformations use the WGS84 ellipsoid standard, with heights measured above the ellipsoid surface (not mean sea level).