You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在@react-three/drei地球模型上绘制地理坐标连接曲线?

问题描述

我基于一个3D地球模型项目搭建了自己的版本,已经为地理坐标添加了标记。现在想要创建曲线连接部分标记,实现类似飞行轨迹的效果。查阅drei文档后,我选择使用CatmullRomLine组件,编写了如下CurvedLine组件:

import React, { useRef } from 'react';
import { CatmullRomLine } from '@react-three/drei';

export interface CurvedLineProps {
  start: [number, number, number];
  end: [number, number, number];
}

export const CurvedLine = ({ start, end }: CurvedLineProps) => {
  const lineRef = useRef(null);

  return (
    <CatmullRomLine
      ref={lineRef}
      points={[start, end]}
      closed={false}
      color={0xffe600}
      lineWidth={6}
      curveType='catmullrom'
      tension={0.03}
    />
  );
};

随后在App.tsx中调用该组件:

import { CurvedLine} from './components/CurvedLine';

const getLinePoint = (item): [number, number, number] => {
  return [item.lat as number, item.lon as number, 0];
};

// ...其余代码

<Suspense fallback={null}>
    <CurvedLine
      start={getLinePoint(coords[0])}
      end={getLinePoint(coords[3])}
    />
    <Earth coords={coords} />
</Suspense>

目前线条已成功绘制,但存在两个问题:一是线条不在地球模型附近,二是没有预期的弯曲效果,实际显示如下:
绘制的线条位于地球上方

我尝试移除硬编码的0,但结果没有变化。请问问题出在哪里?如何正确绘制连接两点的弧形飞行轨迹?


解决方案

1. 地理坐标未转换为3D球面坐标

当前直接将经纬度[lat, lon, 0]作为3D坐标传入曲线组件,但这是二维地理坐标,并未映射到地球模型的3D球面空间中。地球模型基于球面构建,需要将经纬度转换为Three.js可识别的笛卡尔坐标(x, y, z)。

可以编写如下转换函数:

// 地球半径根据你的模型缩放比例调整,设为1.01是让曲线略高于地球表面,避免被遮挡
const latLonTo3D = (lat: number, lon: number, radius = 1.01): [number, number, number] => {
  const phi = (90 - lat) * (Math.PI / 180);
  const theta = (lon + 180) * (Math.PI / 180);
  return [
    -(radius * Math.sin(phi) * Math.cos(theta)),
    radius * Math.cos(phi),
    radius * Math.sin(phi) * Math.sin(theta)
  ];
};

然后修改getLinePoint函数,使用转换后的坐标:

const getLinePoint = (item): [number, number, number] => {
  return latLonTo3D(item.lat as number, item.lon as number);
};

2. 仅两点无法生成CatmullRom曲线

CatmullRomLine需要至少3个点才能生成平滑的曲线,仅传入起点和终点时,组件只会绘制一条直线。要实现飞行轨迹的弧形效果,需要在两点之间添加中间控制点,让曲线向外凸起。

可以通过计算两点中点并向外拉伸的方式生成控制点:

// 生成包含起点、凸起中点、终点的曲线点数组
const getCurvePoints = (start: [number, number, number], end: [number, number, number]) => {
  // 计算两点的笛卡尔中点
  const midX = (start[0] + end[0]) / 2;
  const midY = (start[1] + end[1]) / 2;
  const midZ = (start[2] + end[2]) / 2;
  // 将中点沿径向向外拉伸,形成凸起效果
  const midPoint = [midX * 1.2, midY * 1.2, midZ * 1.2] as [number, number, number];
  return [start, midPoint, end];
};

接着修改CurvedLine组件,使用包含控制点的数组:

export const CurvedLine = ({ start, end }: CurvedLineProps) => {
  const lineRef = useRef(null);
  const curvePoints = getCurvePoints(start, end);

  return (
    <CatmullRomLine
      ref={lineRef}
      points={curvePoints}
      closed={false}
      color={0xffe600}
      lineWidth={6}
      curveType='catmullrom'
      tension={0.5} // 提高张力值,让曲线过渡更平滑
    />
  );
};

3. 调整渲染层级避免遮挡

如果曲线被地球模型遮挡,可以调整渲染顺序(将CurvedLine放在Earth组件之后渲染),或者修改曲线材质的深度测试属性:

<CatmullRomLine
  // ...其他属性
  material={{ depthTest: false }}
/>

内容的提问来源于stack exchange,提问作者Igal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 12:55:14