@ubilabs/typegpu-globe-renderer
v0.0.2
Published
A high-performance, standalone **WebGPU / [TypeGPU](https://docs.swmansion.com/TypeGPU/) (v0.11)** globe renderer that streams **Google Photorealistic 3D Tiles** and renders them entirely in WebGPU pipelines. Shaders are authored in TypeScript (TGSL) via
Keywords
Readme
typegpu-globe-renderer
A high-performance, standalone WebGPU / TypeGPU (v0.11) globe renderer that streams Google Photorealistic 3D Tiles and renders them entirely in WebGPU pipelines. Shaders are authored in TypeScript (TGSL) via TypeGPU, compiling directly to WebGPU pipelines with no dependencies on Three.js, Babylon.js, or Cesium.
Features
- Photorealistic 3D Tiles: Streams global 3D geometry and photogrammetry.
- Physical Atmosphere & Haze: Single-scattering Rayleigh/Mie/Ozone atmosphere and matches distant terrain fog.
- Cascaded Shadow Mapping (CSM): Sharp close-up shadows and stable far shadows.
- Dynamic Day/Night Cycle: Corrected solar declination for realistic seasonal terminators and procedural night windows.
- Modular Plugin System: Add custom GPU layers (like simulated trees, weather, or custom geometry).
Installation
npm install @ubilabs/typegpu-globe-rendererQuick Start
import {GlobeRenderer} from '@ubilabs/typegpu-globe-renderer';
const renderer = await GlobeRenderer.create(document.querySelector('#view'), {
apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
initialCamera: {
center: {lat: 53.5511, lng: 9.9937, altitude: 0},
range: 500
}
});
// Clean up when done
// renderer.dispose();Writing Custom Plugins
The library features a lightweight, direct GPU plugin API. Custom plugins can hook into the render loop, bind core uniforms, and draw custom geometry inside the main pass and the shadow passes.
Plugin Interface
To write a plugin, implement the GlobePlugin interface:
import {type GlobePlugin} from '@ubilabs/typegpu-globe-renderer';
export const myPlugin: GlobePlugin = {
name: 'my-custom-plugin',
// 1. Initialize GPU resources
init(ctx) {
// ctx.root, ctx.device, ctx.shared, ctx.sun, ctx.ground
},
// 2. Contribute to the core tile fragment shader (e.g. emissive lights)
tileShader() {
return {emissive: myEmissiveFn, bindings: []};
},
// 3. Perform frame bookkeeping/simulations
update(frame) {
// frame.dt, frame.timeSeconds, frame.hopShift
},
// 4. Draw scene objects (after buildings, before composite)
recordScene(pass, frame) {
pass.setPipeline(pipeline);
pass.draw(3);
},
// 5. Draw after the main scene is composed (e.g. clouds, UI)
recordComposite(pass, frame) {
pass.setPipeline(compositePipeline);
pass.draw(3);
},
// 6. Draw extra shadow casters into the active shadow cascades
recordShadowCasters(pass, cascadeIndex) {
pass.setPipeline(shadowPipeline);
pass.draw(3);
},
// 7. React to canvas size changes
onResize(width, height) {
// ...
},
// 8. Clean up resources
dispose() {
// ...
}
};Streamlined Shadow & Ground Clamping API
The core library exposes several helpers via the GlobePluginContext to make custom shadow-casting and ground-clamping easy:
1. Reusing Vertex Shaders (TypeGPU Accessors)
Avoid duplicating your vertex shader for the scene and shadow passes. Declare a TypeGPU accessor for the projection matrix:
const viewProj = tgpu.accessor(d.mat4x4f);
const vertexShader = tgpu.vertexFn(...)((input) => {
'use gpu';
// Project vertices using the accessor:
return { pos: viewProj.$.mul(d.vec4f(vertexPos, 1.0)) };
});Then, bind the respective core uniforms during pipeline initialization:
// Scene Pipeline (renders to screen):
const pipeline = ctx.root
.with(viewProj, ctx.shared.viewProjUniform)
.createRenderPipeline({ vertex: vertexShader, fragment: fragmentShader, ... });2. Automatic Shadow Pipeline Creation
Use the core helper ctx.sun.createCasterPipeline to automatically compile a depth-only caster pipeline matching your vertex shader, binding the shadow matrix and applying the library's depth-bias settings:
const shadowPipeline = ctx.sun.createCasterPipeline(
vertexShader,
viewProj,
[ctx.ground.decoration.bindGroup] // Any bind groups needed by the vertex shader
);3. Ground Clamping (Height Map Lookup)
Clamping custom geometry (like trees or buildings) to the photogrammetry ground mesh can be done inside your vertex shader using the core-exported sampleHeightRegion GPU function:
import { HALF_EXTENT, sampleHeightRegion } from '@ubilabs/typegpu-globe-renderer';
const vertexShader = tgpu.vertexFn(...)((input) => {
'use gpu';
// Look up the exact ground Z coordinate in the terrain heightmap:
const groundZ = sampleHeightRegion(localPos.xy, HALF_EXTENT, 1024.0);
const vertexPos = d.vec3f(localPos.xy, groundZ + localPos.z);
// ...
});