如何在Next.js 14+Three.js的3D地球模型上固定显示大洲名称
解决Next.js 14中Three.js地球模型固定大洲名称的问题
要实现大洲名称随地球旋转保持固定位置,核心是将文本作为地球Mesh的子元素,继承地球的旋转属性,同时通过坐标计算将文本定位到对应大洲区域。以下是具体实现方案:
步骤1:修改Earth.jsx组件
在地球组件中引入Text组件和THREE,添加大洲数据并将文本作为地球Mesh的子元素:
"use client" import React, { useRef } from "react"; import { useGLTF, Text } from "@react-three/drei"; import { useFrame } from "@react-three/fiber"; import * as THREE from "three"; // 大洲名称及大致经纬度(可根据实际需求调整) const continents = [ { name: "亚洲", lat: 30, lon: 100 }, { name: "欧洲", lat: 50, lon: 10 }, { name: "非洲", lat: 0, lon: 20 }, { name: "北美洲", lat: 40, lon: -100 }, { name: "南美洲", lat: -20, lon: -60 }, { name: "大洋洲", lat: -25, lon: 135 }, { name: "南极洲", lat: -90, lon: 0 }, ]; // 将经纬度转换为三维坐标(半径设为比地球大0.1,避免文本被模型遮挡) const latLonToVector3 = (lat, lon, radius = 3.1) => { const phi = (90 - lat) * (Math.PI / 180); const theta = (lon + 180) * (Math.PI / 180); const x = -(radius * Math.sin(phi) * Math.cos(theta)); const y = radius * Math.cos(phi); const z = radius * Math.sin(phi) * Math.sin(theta); return new THREE.Vector3(x, y, z); }; export default function Earth(props) { const { nodes, materials } = useGLTF("/globe/earth.gltf"); const earthRef = useRef(); useFrame(() => { if (earthRef.current) { earthRef.current.rotation.y -= 0.002; } }); return ( <group {...props} dispose={null}> <mesh ref={earthRef} geometry={nodes.Object_4.geometry} material={materials["Scene_-_Root"]} scale={3} > {/* 渲染大洲名称文本 */} {continents.map((continent, idx) => { const position = latLonToVector3(continent.lat, continent.lon); return ( <Text key={idx} position={position} fontSize={0.2} color="#ffffff" anchorX="center" anchorY="middle" rotation-y={Math.PI} // 调整文本朝向,确保正面面向相机 outlineWidth={0.02} outlineColor="#000000" > {continent.name} </Text> ); })} </mesh> </group> ); } useGLTF.preload("/globe/earth.gltf");
关键说明
- 子元素继承旋转:将
Text组件放在地球Mesh内部,地球旋转时文本会自动跟随旋转,保持与大洲的相对位置不变。 - 经纬度转三维坐标:通过公式将地理经纬度转换为Three.js的三维坐标,半径设为比地球模型稍大,避免文本被地球表面遮挡。
- 文本样式优化:添加描边、调整字体大小和朝向,确保文本在旋转过程中始终清晰可见。
可选优化:使用GLTF内置大洲节点定位
如果你的GLTF模型中已经包含各个大洲的独立Mesh节点(例如nodes.Asia),可以直接使用节点的位置来定位文本,精度更高:
// 替换上述的Text渲染部分 {[ { name: "亚洲", node: nodes.Asia }, { name: "欧洲", node: nodes.Europe }, // 其他大洲... ].map((item, idx) => ( <Text key={idx} position={item.node.position} scale={0.15} color="#ffffff" anchorX="center" anchorY="middle" > {item.name} </Text> ))}
内容的提问来源于stack exchange,提问作者Pavitar Sharma
相关产品推荐
相关产品推荐

