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

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


一、实现目标

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

最终效果:

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

二、核心思路

  1. 加载底图与高程图:

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

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

    • 采用 等经纬度展开映射:

      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. 将边界独立为 LineLoop 或 Mesh,实现更复杂的交互。
  3. 支持多层纹理(如云层、大气层)以增强真实感。
相关推荐
jay神24 分钟前
基于 YOLOv8 的轴承缺陷检测(Web 版)
前端·人工智能·yolo
jay神32 分钟前
基于 YOLOv8 的森林火灾检测系统(Web 版)
前端·yolo
code2cat36 分钟前
HTML基础篇之第一张网页:从index.html到浏览器里的任务清单
前端·html
miss1 小时前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan2 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真2 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment2 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan2 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么2 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技3 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue