UNPKG

globe.gl

Version:

UI component for Globe Data Visualization using ThreeJS/WebGL

128 lines (113 loc) 4.12 kB
<head> <style> body { margin: 0; } #time { position: absolute; bottom: 8px; left: 8px; color: lightblue; font-family: monospace; } </style> <script src="//cdn.jsdelivr.net/npm/globe.gl"></script> <!-- <script src="../../dist/globe.gl.js"></script>--> </head> <body> <div id="globeViz"></div> <div id="time"></div> <script type="module"> import { TextureLoader, ShaderMaterial, Vector2 } from 'https://esm.sh/three'; import * as solar from 'https://esm.sh/solar-calculator'; const VELOCITY = 1; // minutes per frame // Custom shader: Blends night and day images to simulate day/night cycle const dayNightShader = { vertexShader: ` varying vec3 vNormal; varying vec2 vUv; void main() { vNormal = normalize(normalMatrix * normal); vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` #define PI 3.141592653589793 uniform sampler2D dayTexture; uniform sampler2D nightTexture; uniform vec2 sunPosition; uniform vec2 globeRotation; varying vec3 vNormal; varying vec2 vUv; float toRad(in float a) { return a * PI / 180.0; } vec3 Polar2Cartesian(in vec2 c) { // [lng, lat] float theta = toRad(90.0 - c.x); float phi = toRad(90.0 - c.y); return vec3( // x,y,z sin(phi) * cos(theta), cos(phi), sin(phi) * sin(theta) ); } void main() { float invLon = toRad(globeRotation.x); float invLat = -toRad(globeRotation.y); mat3 rotX = mat3( 1, 0, 0, 0, cos(invLat), -sin(invLat), 0, sin(invLat), cos(invLat) ); mat3 rotY = mat3( cos(invLon), 0, sin(invLon), 0, 1, 0, -sin(invLon), 0, cos(invLon) ); vec3 rotatedSunDirection = rotX * rotY * Polar2Cartesian(sunPosition); float intensity = dot(normalize(vNormal), normalize(rotatedSunDirection)); vec4 dayColor = texture2D(dayTexture, vUv); vec4 nightColor = texture2D(nightTexture, vUv); float blendFactor = smoothstep(-0.1, 0.1, intensity); gl_FragColor = mix(nightColor, dayColor, blendFactor); } ` }; const sunPosAt = dt => { const day = new Date(+dt).setUTCHours(0, 0, 0, 0); const t = solar.century(dt); const longitude = (day - dt) / 864e5 * 360 - 180; return [longitude - solar.equationOfTime(t) / 4, solar.declination(t)]; }; let dt = +new Date(); const timeEl = document.getElementById('time'); const world = new Globe(document.getElementById('globeViz')); Promise.all([ new TextureLoader().loadAsync('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-day.jpg'), new TextureLoader().loadAsync('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg') ]).then(([dayTexture, nightTexture]) => { const material = new ShaderMaterial({ uniforms: { dayTexture: { value: dayTexture }, nightTexture: { value: nightTexture }, sunPosition: { value: new Vector2() }, globeRotation: { value: new Vector2() } }, vertexShader: dayNightShader.vertexShader, fragmentShader: dayNightShader.fragmentShader }); world.globeMaterial(material) .backgroundImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/night-sky.png') // Update globe rotation on shader .onZoom(({ lng, lat }) => material.uniforms.globeRotation.value.set(lng, lat)); requestAnimationFrame(() => (function animate() { // animate time of day dt += VELOCITY * 60 * 1000; timeEl.textContent = new Date(dt).toLocaleString(); material.uniforms.sunPosition.value.set(...sunPosAt(dt)); requestAnimationFrame(animate); })() ); }); </script> </body>