用 Three.js 打造 NASA 级 3D 地球门户网——CupGIS 社区全栈实战

发布时间:2026/7/16 4:32:47

用 Three.js 打造 NASA 级 3D 地球门户网——CupGIS 社区全栈实战 用 Three.js 打造 NASA 级 3D 地球门户网——CupGIS 社区全栈实战官网地址https://cupgis.cn/前言在 WebGIS 领域大多数门户站点还停留在平面地图 列表的传统形态。CupGIS 社区尝试打破这一惯例以一颗真实渲染的 3D 地球为视觉核心通过 WebGL 着色器实现昼夜交替、大气光晕、云层漂移、星空闪烁等天文级效果同时保证页面作为门户站的简洁易读与高性能。本文从架构设计、核心着色器、性能优化、UI/UX 设计四个维度完整拆解 CupGIS 社区主站的技术实现。****一、项目概览1.1 技术栈层级技术选型说明3D 渲染Three.js 0.162 WebGLES Module importmap 加载着色器GLSL ES自定义顶点/片段着色器前端架构原生 ES Module零框架依赖极致轻量国际化自研 i18n 模块中/英双语localStorage 持久化样式CSS3 CSS 变量玻璃态、渐变、响应式数据驱动JSON 配置模块/轨道参数全配置化1.2 项目结构CupGIS/ ├── index.html # 主站入口 ├── config/ │ └── modules.json # 模块与轨道配置数据驱动 ├── css/ │ └── style.css # 全局样式设计令牌 组件 ├── js/ │ ├── main.js # 应用入口编排所有模块 │ ├── i18n.js # 国际化中/英双语 │ ├── modules.js # 模块渲染器全宽分区布局 │ ├── scroll.js # 滚动控制器 │ └── scene/ │ ├── manager.js # Three.js 场景管理器 │ └── earth.js # 真实地球着色器核心 └── assets/ ├── textures/ # NASA 4K 地球纹理 └── modules/ # 模块主题配图【在此处插入图片项目目录结构截图——展示完整的文件组织】二、核心架构设计2.1 应用编排main.js整个应用采用单一CupGISApp类编排初始化流程如下asyncinit(){// 1. 立即绑定 UI 交互不阻塞加载this._setupNav();this._setupLangToggle();this._setupSmoothScroll();this._setupScrollStates();// 2. 初始化 i18napplyTranslations(getCurrentLang());// 3. 并行模块配置 fetch 3D 场景初始化constconfigDataawaitfetchConfig();renderModules();// 4. 初始化 Three.js 场景加载进度回调this.sceneManagernewSceneManager(this.threeCanvas);awaitthis.sceneManager.init(orbitConfigs,(progress){this._updateLoading(progress);});// 5. 滚动控制器绑定 3D 场景this.scrollControllernewScrollController((progress)this.sceneManager.setScrollProgress(progress),(moduleIdx)this.sceneManager.setActiveModule(moduleIdx));}关键设计UI 交互绑定与 3D 场景初始化解耦。即使 Three.js CDN 加载失败导航、滚动、模块内容仍可正常使用。2.2 数据驱动的模块系统所有模块信息存储在modules.json中包括标题、描述、功能列表、轨道参数和主题色{id:data,title:{zh:数据索引,en:Data Index},color:#00b4d8,image:assets/modules/data.jpg,orbit:{radius:1.3,inclination:28.5,speed:0.15,startAngle:0}}每个模块的轨道参数半径、倾角、速度直接映射到 3D 场景中的卫星轨道实现配置即轨道的数据驱动设计。主题色通过 CSS 变量--module-color驱动整个分区的视觉风格。【在此处插入图片数据驱动流程图——展示从 JSON 配置到 3D 轨道 UI 渲染的数据流】三、GLSL 着色器真实地球的核心这是整个项目技术含量最高的部分。地球的昼夜交替、云层、大气光晕全部通过自定义 GLSL 着色器实现。【在此处插入图片3D 地球渲染效果——展示昼夜交替、大气光晕、云层效果】3.1 地球表面着色器// 顶点着色器输出世界空间法线 varying vec3 vNormalW; void main() { vNormalW normalize((modelMatrix * vec4(normal, 0.0)).xyz); gl_Position projectionMatrix * modelViewMatrix * vec4(position, 1.0); }// 片段着色器核心逻辑 uniform sampler2D dayTexture; uniform sampler2D nightTexture; uniform sampler2D brcMap; // Rbump, Groughness, Bclouds uniform vec3 sunPosition; uniform float transitionWidth; uniform float cloudOffset; void main() { vec3 normal normalize(vNormalW); vec3 lightDir normalize(sunPosition); // 法线与太阳方向的点积 cos(θ) float dotProduct dot(normal, lightDir); // 平滑晨昏线过渡smoothstep 混合 float dayStrength smoothstep( -transitionWidth * 0.5, transitionWidth * 0.5, dotProduct ); // 白天/夜间纹理采样 vec3 dayColor texture2D(dayTexture, vUv).rgb; vec3 nightColor texture2D(nightTexture, vUv).rgb; // 云层从 BRC 蓝色通道提取独立旋转 vec2 cloudUv vec2(vUv.x - cloudOffset, vUv.y); float clouds smoothstep(0.25, 1.0, texture2D(brcMap, cloudUv).b); // 白天侧云层混合 dayColor mix(dayColor, vec3(0.92, 0.94, 0.96), clouds * 0.7 * dayStrength); dayColor * 1.3; // 夜间灯光增强城市灯火清晰可见 nightColor * 7.0; // 昼夜混合 vec3 color mix(nightColor, dayColor, dayStrength); gl_FragColor vec4(color, 1.0); }技术要点世界空间法线法线变换到世界空间modelMatrix * normal保证昼夜分界线不随地球自转或相机移动而漂移smoothstep 晨昏线用smoothstep替代硬切step产生柔和的日出/日落过渡带BRC 三通道纹理将 bump凹凸、roughness粗糙度、clouds云层合并为一张纹理减少纹理采样次数。云层从蓝色通道提取云层独立旋转通过cloudOffsetuniform 在 UV 的 U 方向偏移使云层与地球自转产生相对运动夜间灯光增强夜间贴图乘以 7.0确保黑暗面的城市灯火清晰可见【在此处插入图片昼夜交替效果对比——白天侧与夜间侧的地球渲染效果】3.2 大气光晕着色器Fresnel 效应uniform vec3 sunPosition; varying vec3 vNormalW; varying vec3 vPositionW; void main() { vec3 normal normalize(vNormalW); vec3 viewDir normalize(cameraPosition - vPositionW); // Fresnel边缘越亮 float fresnel 1.0 - abs(dot(viewDir, normal)); float alpha clamp((fresnel - 0.6) / 0.4, 0.0, 1.0); alpha 1.0 - alpha; alpha pow(alpha, 3.0); // 太阳照射方向影响大气亮度 float sunOrientation dot(normal, normalize(sunPosition)); alpha * smoothstep(-0.5, 1.0, sunOrientation); // 大气色白天蓝暮光微暖 vec3 atmoDay vec3(0.30, 0.70, 1.00); vec3 atmoTwilight vec3(0.95, 0.45, 0.15); vec3 atmoColor mix(atmoTwilight, atmoDay, smoothstep(-0.2, 0.7, sunOrientation)); gl_FragColor vec4(atmoColor, alpha); }大气层使用BackSide渲染从内部看配合AdditiveBlending叠加模式产生从地球边缘向外扩散的蓝色光晕。暮光区域晨昏线附近呈现暖橙色模拟真实大气散射。【在此处插入图片大气光晕效果——展示地球边缘的蓝色光晕和晨昏线暖色过渡】3.3 闪烁星空着色器GPU Points// 顶点着色器每颗星独立闪烁 attribute float size; attribute vec3 color; attribute float phase; attribute float frequency; uniform float time; void main() { vColor color; float blink sin(time * frequency phase) * 0.5 0.8; float noise sin(dot(position, vec3(12.9898, 78.233, 45.5432)) * 43758.5453) * 0.1; float finalSize size * (blink noise); vec4 mvPosition modelViewMatrix * vec4(position, 1.0); gl_PointSize finalSize * (300.0 / -mvPosition.z); gl_Position projectionMatrix * mvPosition; }1200 颗星在 GPU 上并行计算闪烁动画每颗星有独立的频率和相位配合随机噪声避免规律性闪烁。星体颜色按概率分布70% 偏白、20% 偏蓝、10% 偏红橙模拟真实恒星光谱。四、性能优化策略4.1 纹理并行加载三个 4K 地球纹理白天、夜间、BRC从串行改为并行加载首屏时间缩短约 60%async_loadTextures(onProgress){constloadernewTHREE.TextureLoader();constentriesObject.entries(TEXTURE_PATHS);letcompleted0;// Promise.all 并行加载所有纹理constpromisesentries.map(([name,path]){returnnewPromise((resolve,reject){loader.load(path,(tex){tex.colorSpacecolorTextures.includes(name)?THREE.SRGBColorSpace:THREE.LinearSRGBColorSpace;tex.anisotropywindow.innerWidth768?2:8;completed;if(onProgress)onProgress(completed/entries.length);resolve([name,tex]);},undefined,reject);});});constresultsawaitPromise.all(promises);returnObject.fromEntries(results);}4.2 渲染循环优化_startRenderLoop(){constanimate(){this._animationIdrequestAnimationFrame(animate);constdeltathis.clock.getDelta();// 标签页不可见时跳过渲染节省 GPUif(document.hidden)return;this.earth.update(this.scrollProgress,delta);if(this.controls)this.controls.update();this.renderer.render(this.scene,this.camera);};animate();}优化要点document.hidden检测标签页切换到后台时暂停渲染避免无意义 GPU 消耗clock.getDelta()基于增量时间的动画保证不同帧率下速度一致像素比限制setPixelRatio(Math.min(devicePixelRatio, 2))防止高 DPI 屏幕过度采样4.3 移动端自适应降级constsegwindow.innerWidth768?64:96;// 球体细分段数constSTARS_AMOUNTwindow.innerWidth768?600:1200;// 星星数量tex.anisotropywindow.innerWidth768?2:8;// 各向异性移动端自动降低几何体精度、粒子数量和纹理过滤质量保证 60fps 流畅运行。4.4 滚动性能优化// passive: true 不阻塞主线程window.addEventListener(scroll,this._onScroll.bind(this),{passive:true});_onScroll(){if(this._ticking)return;this._tickingtrue;requestAnimationFrame((){this._recalculate();this._tickingfalse;});}使用requestAnimationFrame节流 passive事件监听滚动事件不阻塞主线程。4.5 资源加载优化字体displayswapGoogle Fonts 使用swap策略避免字体加载阻塞文字渲染preload关键资源CSS 和 JSON 配置使用link relpreload提前下载图片loadinglazy模块配图懒加载首屏不请求importmap CDN 加载Three.js 从 jsdelivr CDN 加载无需打包【在此处插入图片加载性能对比图——优化前后的首屏时间对比】五、UI/UX 设计5.1 全宽分区式布局参考 NASA 官网设计语言模块区域从传统卡片改为全宽分区.module-panel{display:grid;grid-template-columns:1fr 1fr;min-height:90vh;}/* 交替布局偶数左图右文奇数右图左文 */.module-panel.image-left .panel-visual{order:1;}.module-panel.image-left .panel-content{order:2;}.module-panel.image-right .panel-visual{order:2;}.module-panel.image-right .panel-content{order:1;}每个模块占据全屏宽度图文左右交替排列配合大号编号、图标框和装饰线标签形成类似 NASA 官网的机构级视觉层次。【在此处插入图片全宽分区布局截图——展示图文交替排列的模块分区效果】5.2 六色主题体系每个模块拥有独立主题色通过 CSS 变量--module-color驱动整个分区的视觉元素.panel-number{color:var(--module-color);text-shadow:0 0 20pxcolor-mix(in srgb,var(--module-color)40%,transparent);}.panel-visual-glow{background:radial-gradient(ellipse,transparent 50%,color-mix(in srgb,var(--module-color)20%,transparent)100%);}模块主题色色调含义数据索引#00b4d8青色遥感/海洋在线工具#9d4edd紫色科技/工具技术博客#f77f00橙色活力/内容开发文档#06a77d绿色成长/参考社区#4361ee蓝色连接/协作关于#adb5bd银色纯净/愿景【在此处插入图片六色主题色对比图——展示六个模块各自的配色方案】5.3 玻璃态导航与滚动状态.navbar{background:rgba(5,5,7,0.55);backdrop-filter:blur(20px)saturate(140%);border-bottom:1px solidvar(--color-border);}.navbar.scrolled{padding:var(--spacing-sm)var(--spacing-lg);background:rgba(5,5,7,0.85);}导航栏滚动时自动收紧、加深背景透明度实现呼吸感视觉反馈。5.4 国际化 i18n自研轻量 i18n 模块通过data-i18n属性驱动 DOM 文本支持运行时切换exportfunctionapplyTranslations(lang){constdicttranslations[lang];document.querySelectorAll([data-i18n]).forEach((el){constkeyel.getAttribute(data-i18n);constvalueresolveKey(dict,key);if(value)el.textContentvalue;});localStorage.setItem(cupgis-lang,lang);window.dispatchEvent(newCustomEvent(languagechange));}切换语言时通过languagechange事件触发模块重新渲染无需刷新页面。六、3D 场景与滚动联动6.1 滚动控制器exportclassScrollController{_recalculate(){constscrollHeightdocument.documentElement.scrollHeight-window.innerHeight;constscrollTopwindow.scrollY;this.progressMath.min(1,scrollTop/scrollHeight);// 按进度推断当前模块constheroEnd0.08;constmoduleSpan(1-heroEnd)/this.moduleCount;letmoduleIdx-1;if(this.progressheroEnd){moduleIdxMath.floor((this.progress-heroEnd)/moduleSpan);}// 通知 3D 场景if(this.onProgressChange)this.onProgressChange(this.progress);if(moduleIdx!this.activeModuleIndex){this.activeModuleIndexmoduleIdx;if(this.onModuleChange)this.onModuleChange(moduleIdx);}}}滚动进度通过回调传入 3D 场景管理器驱动地球旋转角度和卫星高亮实现滚动即航行的沉浸式体验。6.2 OrbitControls 交互this.controlsnewOrbitControls(this.camera,this.canvas);this.controls.enableRotatetrue;// 鼠标拖拽旋转this.controls.enableZoomtrue;// 滚轮缩放this.controls.enablePanfalse;// 禁用平移this.controls.autoRotatetrue;// 自动旋转this.controls.autoRotateSpeed0.4;// 缓慢自转this.controls.enableDampingtrue;// 惯性阻尼this.controls.dampingFactor0.06;用户可直接拖拽地球旋转、滚轮缩放松手后地球保持惯性缓慢自转。【在此处插入图片滚动联动效果 GIF——展示滚动时地球视角变化和卫星高亮】七、程序化纹理生成太阳和光晕纹理使用 Canvas API 程序化生成无需额外图片资源_makeSunTexture(){constcanvasdocument.createElement(canvas);canvas.widthcanvas.height512;constctxcanvas.getContext(2d);// 径向渐变从白热中心到红色边缘constgradctx.createRadialGradient(256,256,0,256,256,256);grad.addColorStop(0,#fff7d6);grad.addColorStop(0.3,#ffd24a);grad.addColorStop(0.7,#ff8a1e);grad.addColorStop(1,#c43c00);ctx.fillStylegrad;ctx.fillRect(0,0,512,512);// 随机黑子/亮斑for(leti0;i1400;i){constxMath.random()*512;constyMath.random()*512;constrMath.random()*182;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fillStyleMath.random()0.5?rgba(255, 240, 180,${Math.random()*0.150.05}):rgba(180, 60, 0,${Math.random()*0.15});ctx.fill();}returnnewTHREE.CanvasTexture(canvas);}1400 个随机黑子和亮斑模拟太阳表面活动配合AdditiveBlending的光晕精灵产生真实的恒星视觉效果。【在此处插入图片程序化太阳纹理——展示 Canvas 生成的太阳表面效果】八、降级容错3D 场景初始化失败时如 CDN 不可用自动降级为静态深空背景try{awaitthis.sceneManager.init(orbitConfigs,(progress){this._updateLoading(progress);});}catch(err){console.error(3D 场景初始化失败:,err);this._fallbackScene();}_fallbackScene(){document.body.classList.add(scene-fallback);constcanvasdocument.getElementById(threeCanvas);if(canvas)canvas.style.displaynone;}body.scene-fallback{background:radial-gradient(ellipse at 20% 30%,rgba(40,20,60,0.4)0%,transparent 50%),radial-gradient(ellipse at 80% 70%,rgba(20,30,70,0.4)0%,transparent 50%),radial-gradient(circle at 50% 50%,#0a0a12 0%,#050507 70%,#000 100%);}即使 WebGL 完全不可用门户站的内容、导航、模块分区仍可正常浏览。总结CupGIS 社区主站的技术实践表明在不依赖前端框架的前提下通过原生 ES Module Three.js 自定义 GLSL 着色器完全可以构建出 NASA 级别的 3D Web 门户。核心技术要点回顾GLSL 着色器世界空间法线 smoothstep 晨昏线 BRC 多通道纹理 Fresnel 大气性能优化纹理并行加载 渲染循环节流 移动端降级 懒加载架构设计数据驱动模块 i18n 双语 滚动联动 3D 降级容错UI/UX全宽分区布局 六色主题体系 玻璃态导航 响应式项目开源欢迎交流654671523

相关新闻