Three.js 实现高分辨率地球边界可视化

在 WebGL 可视化项目中,Three.js 常常被用来实现三维地球的渲染和交互。但仅仅显示地球贴图还不够,很多时候我们需要在球面上叠加地理边界(比如国家、省份轮廓),以增强可读性和交互性。本文将介绍如何在 Three.js 中利用 Canvas 叠加高分辨率边界线,并将其作为纹理映射到三维地球上。


一、实现目标

  1. 使用一张 8K 高清地球纹理 作为底图。
  2. GeoJSON 数据 中解析边界信息。
  3. Canvas 上绘制边界线,并与底图合成一张新纹理。
  4. 将合成后的纹理映射到球体几何体上,实现 高分辨率边界显示

最终效果:

  • 既保留了高清地球纹理的细节。
  • 又在其上叠加了清晰的地理边界。

二、核心思路

  1. 加载底图与高程图

    • 使用 TextureLoader 加载地球基础纹理(8K 分辨率)。
    • 加载地形高度贴图,用于球体表面微弱的位移效果。
  2. 解析 GeoJSON 边界数据

    • 遍历 PolygonMultiPolygon 类型,逐点计算对应的纹理坐标。

    • 采用 等经纬度展开映射

      ini 复制代码
      x = ((180 + lon) / 360) * width;
      y = ((90 - lat) / 180) * height;
  3. Canvas 绘制边界

    • 将底图绘制到 Canvas 上。
    • 使用 stroke 方法绘制边界线。
    • 线条颜色、粗细可调节(高分辨率下线宽建议 >1.5)。
  4. 合成新纹理

    • 将 Canvas 转换为 THREE.CanvasTexture
    • 作为着色器材质的 earthTexture 使用。
  5. Shader 实现表面位移 + 纹理采样

    • 顶点着色器:根据高程贴图位移顶点。
    • 片元着色器:采样合成后的纹理,并输出到屏幕。

三、关键代码

1. GeoJSON 转 Canvas 边界绘制

ini 复制代码
function createBorderTexture(geojson, baseImage) {
  const canvas = document.createElement("canvas");
  canvas.width = baseImage.width;
  canvas.height = baseImage.height;
  const ctx = canvas.getContext("2d");

  // 绘制底图
  ctx.drawImage(baseImage, 0, 0, canvas.width, canvas.height);

  // 设置边界线样式
  ctx.strokeStyle = "red";
  ctx.lineWidth = 2.0;

  // 遍历边界
  geojson.features.forEach(feature => {
    const type = feature.geometry.type;

    if (type === "Polygon") {
      feature.geometry.coordinates.forEach(ring => {
        ctx.beginPath();
        ring.forEach(([lon, lat], idx) => {
          const x = ((180 + lon) / 360) * canvas.width;
          const y = ((90 - lat) / 180) * canvas.height;
          if (idx === 0) ctx.moveTo(x, y);
          else ctx.lineTo(x, y);
        });
        ctx.closePath();
        ctx.stroke();
      });
    }

    if (type === "MultiPolygon") {
      feature.geometry.coordinates.forEach(polygon => {
        polygon.forEach(ring => {
          ctx.beginPath();
          ring.forEach(([lon, lat], idx) => {
            const x = ((180 + lon) / 360) * canvas.width;
            const y = ((90 - lat) / 180) * canvas.height;
            if (idx === 0) ctx.moveTo(x, y);
            else ctx.lineTo(x, y);
          });
          ctx.closePath();
          ctx.stroke();
        });
      });
    }
  });

  return new THREE.CanvasTexture(canvas);
}

2. Shader 材质

ini 复制代码
const material = new THREE.ShaderMaterial({
  uniforms: {
    earthTexture: { value: borderTexture },
    heightTexture: { value: heightTexture },
    displacementScale: { value: 0.1 }
  },
  vertexShader: `
    uniform sampler2D heightTexture;
    uniform float displacementScale;
    varying vec2 vUv;
    void main() {
    // 将uv 帖图传给 片元着色器使用
      vUv = uv;
      // 将灰度图获取r的值为 0-1 
      float height = texture2D(heightTexture, uv).r;
      // normal * height 计算示例 vec3(0,0.5,0) * 0.2  => vec3(0,1,0)
      // position + normal 计算示例  vec3(0,0.5,0) + vec3(0,0.2,0) => vec3(0,0.7,0)
      // 在计算完成之后会在球体中将 顶点 生成对应的高度
      vec3 newPosition = position + normal * height * displacementScale;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
    }
  `,
  fragmentShader: `
  // 获取地图 8K
    uniform sampler2D earthTexture;
    // 获取顶点着色器中的uv
    varying vec2 vUv;
    void main() {
    // 计算顶点对应的rgb值 
      vec3 color = texture2D(earthTexture, vUv).rgb;
      // 将 rgb 返回给显示做颜色显示
      gl_FragColor = vec4(color, 1.0);
    }
  `
});

3. 地球几何体

ini 复制代码
const geometry = new THREE.SphereGeometry(1, 128, 128);
earth = new THREE.Mesh(geometry, material);

四、交互与控制

在场景中使用 OrbitControls

  • 支持旋转、缩放。
  • 限制相机缩放距离,保证交互舒适性。
ini 复制代码
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.minDistance = 1.2;
controls.maxDistance = 10;

同时,可以通过按钮控制地球的自动旋转:

csharp 复制代码
if (rotateStatus.value) {
  group.rotation.y += 0.002;
}

五、总结与拓展

本文通过 Canvas 动态合成纹理 的方式,实现了在 8K 高清地球贴图上叠加边界线 的效果。这样不仅保证了纹理的清晰度,还能灵活调整边界的样式。

该文章由AI生成后调整

要代码的请留言,我会将源码发私信发出 示例地址

未来的拓展方向:

  1. 使用 Raycaster 增加鼠标交互(点击高亮区域)。
  2. 将边界独立为 LineLoopMesh,实现更复杂的交互。
  3. 支持多层纹理(如云层、大气层)以增强真实感。
相关推荐
LaoZhangAI2 小时前
Google Gemini AI图片编辑完全指南:50+中英对照提示词与批量处理教程(2025年9月)
前端·后端
用户11481867894842 小时前
从零搭建 Vue3 + Nest.js 实时通信项目:4 种方案(短轮询 / 长轮询 / SSE/WebSocket)
前端·后端
LaoZhangAI2 小时前
Google Gemini Nano与Banana AI完整部署指南:2025年轻量级AI解决方案
前端·后端
用户11481867894842 小时前
基于 Webpack Module Federation 的 Vue 微前端实践
前端
怪可爱的地球人2 小时前
Pinia状态管理有哪些常用API?
前端
小高0072 小时前
🤔函数柯里化:化繁为简的艺术与实践
前端·javascript·面试
却尘2 小时前
React useMemo 依赖陷阱:组件重挂载,状态无限复原
前端·javascript·react.js
Asort2 小时前
JavaScript 从零开始(三):浏览器控制台与VS Code协同工作环境搭建详解
前端·javascript
跟橙姐学代码2 小时前
自动化邮件发送的终极秘籍:Python库smtplib与email的完整玩法
前端·python·ipython