A Single Fragment Shader Adds a Physically Plausible, Twinkling Night Sky to CesiumJS
GIS and digital-twin applications often need a convincing night mode that doesn't look like a cheap Instagram filter. This approach costs one draw call, runs entirely on the GPU, and gives users a real-time slider to dial in the exact atmosphere, which is far more flexible than swapping static imagery or toggling a hard-coded dark layer.
A single fragment shader, registered as a Cesium PostProcessStage, darkens the scene, masks the sky region by Y-coordinate, and scatters procedurally generated stars across the upper half of the viewport. Each star's brightness oscillates independently via a sine function keyed to a random seed, producing a scattered, breathing twinkle rather than a uniform blink. A low-saturation cool-blue base color is mixed into the darkened pixels to prevent the flat, gray-black look that comes from simple brightness reduction.
The effect is fully parametric: a Vue slider drives a `nightStrength` uniform that controls overall darkness, star visibility, and the intensity of the blue tint. Toggling the effect off removes the stage from the post-process pipeline and stops the `requestAnimationFrame` loop that advances the time uniform, so no idle GPU work remains.
The shader logic is compact enough to study in one sitting. A `smoothstep` on the Y texture coordinate confines stars to the sky, a classic `fract(sin(dot(...)))` hash seeds their positions, and a `step` threshold at 0.997 keeps the star density sparse. The result is a night sky that feels physically motivated rather than a screen-space overlay.
Treating the night sky as a shader-driven post-process effect rather than a baked texture or a separate overlay keeps the implementation decoupled from the terrain and imagery layers, so it works with any base map without modification.
The `smoothstep` sky mask is the detail that sells the effect; without it, stars would appear on mountains and buildings, instantly breaking the illusion of a real night scene.
Using `requestAnimationFrame` to advance a time uniform is a lightweight alternative to Cesium's own clock system when the only thing that needs animating is a shader parameter, avoiding unnecessary scene ticks.