使用three.js实现3D地球

效果示例

引入 Three.js

首先,在 工程中中引入 Three.js 库:

css 复制代码
npm install three
npm i --save-dev @types/three

实现步骤

  1. 初始化场景、相机与渲染器
    • 创建场景 new THREE.Scene()
    • 添加雾化效果 new THREE.FogExp2
    • 创建透视相机 new THREE.PerspectiveCamera
    • 渲染器 new THREE.WebGLRenderer
    • 添加相机控件 OrbitControls
ts 复制代码
  scene = new THREE.Scene()
  scene.fog = new THREE.FogExp2(0x000033, 0.1)
  camera = new THREE.PerspectiveCamera(
    65,
    container.value.clientWidth / container.value.clientHeight,
    0.1,
    1000
  )
  renderer = new THREE.WebGLRenderer({ 
    antialias: true,
    alpha: true
  })
  renderer.setSize(container.value.clientWidth, container.value.clientHeight)
  renderer.setPixelRatio(window.devicePixelRatio)
  container.value.appendChild(renderer.domElement)

  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  controls.minDistance = 1.8
  controls.maxDistance = 5
  1. 创建地球模型
    • 创建三维球体 new THREE.SphereGeometry
    • 加载贴图地球 new THREE.TextureLoader
    • 添加高光 new THREE.MeshPhongMaterial
    • 球体外围添加一层云层贴图
ts 复制代码
  const geometry = new THREE.SphereGeometry(1, 128, 128)
  const textureLoader = new THREE.TextureLoader()
  const earthTexture = textureLoader.load(earth_atmos)

  const material = new THREE.MeshPhongMaterial({
    map: earthTexture,
    specular: new THREE.Color(0xaaaaaa),
    shininess: 15,
    emissive: 0x000000, // 确保地球不自发光
    emissiveIntensity: 0
  })

  earthMesh = new THREE.Mesh(geometry, material)
  earthMesh.position.y = yOffest
  scene.add(earthMesh)

  // 添加云层
  const cloudsTexture = textureLoader.load(earth_clouds)
  const cloudsMaterial = new THREE.MeshPhongMaterial({
    map: cloudsTexture,
    transparent: true,
    opacity: 0.6,
    depthWrite: false
  })
  const cloudsMesh = new THREE.Mesh(
    new THREE.SphereGeometry(1.01, 128, 128),
    cloudsMaterial
  )
  cloudsMesh.position.y = yOffest
  scene.add(cloudsMesh)
  1. 实现飞线
    • 创建曲线路径 new THREE.QuadraticBezierCurve3
    • 创建曲线材质 new THREE.ShaderMaterial
    • 添加箭头 new THREE.ConeGeometry
    • 添加文字 new THREE.CanvasTexture
ts 复制代码
  const curve = new THREE.QuadraticBezierCurve3(
    startVec,
    new THREE.Vector3().addVectors(startVec, endVec).multiplyScalar(0.5).normalize().multiplyScalar(1.2),
    endVec
  )
const createFlyLineMaterial = (
  startColor: THREE.Color,
  endColor: THREE.Color,
  lineWidth: number
) => {
  return new THREE.ShaderMaterial({
    uniforms: {
      time: { value: 0 },
      startColor: { value: startColor },
      endColor: { value: endColor },
      opacity: { value: 0.8 },
      lineWidth: { value: lineWidth }
    },
    vertexShader: `
      attribute float progress;
      varying float vProgress;
      
      void main() {
        vProgress = progress;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `,
    fragmentShader: `
      uniform float time;
      uniform vec3 startColor;
      uniform vec3 endColor;
      uniform float opacity;
      uniform float lineWidth;
      varying float vProgress;
      
      void main() {
        vec3 color = mix(startColor, endColor, vProgress);
        float glowIntensity = lineWidth * 0.5;
        float glow = sin(5.0 * (vProgress - time * 0.3)) * 0.3 + 0.7;
        glow = pow(glow, 2.0) * glowIntensity;
        float alpha = mix(0.1, 1.0, vProgress) * opacity * glow;
        gl_FragColor = vec4(color * (1.0 + glow * 0.3), alpha);
      }
    `,
    transparent: true,
    depthTest: false,
    blending: THREE.AdditiveBlending,
    linewidth: lineWidth
  })
}
const createArrow = (color: THREE.Color) => {
  const arrowGeometry = new THREE.ConeGeometry(0.004, 0.02, 8)
  const arrowMaterial = new THREE.MeshBasicMaterial({ 
    color: color,
    transparent: true,
    opacity: 0.9
  })
  const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial)
  arrow.rotation.x = Math.PI / 2
  arrow.position.set(0, -100, 0)
  
  return arrow
}
  const texture = new THREE.CanvasTexture(canvas)
  const spriteMaterial = new THREE.SpriteMaterial({ 
    map: texture,
    transparent: true
  })
  1. 动画优化
    • 添加云层自转 requestAnimationFrame
    • 添加箭头动画
ts 复制代码
  const animateEarth = () => {
    cloudsMesh.rotation.y += 0.0007
    animationId = requestAnimationFrame(animateEarth)
  }
const animateFlyLines = () => {
  const time = Date.now() * 0.0005
  
  flyLines.forEach(flyLine => {
    flyLine.material.uniforms.time.value = time
    if (flyLine.arrow) {
      flyLine.progress = (flyLine.progress + 0.0015) % 1
      const point = flyLine.curve.getPoint(flyLine.progress)
      const tangent = flyLine.curve.getTangent(flyLine.progress).normalize()
      flyLine.arrow.position.copy(point)
      flyLine.arrow.lookAt(point.clone().add(tangent))
      flyLine.arrow.rotateX(Math.PI / 2)
      const scale = 0.8 + Math.sin(flyLine.progress * Math.PI) * 0.5
      flyLine.arrow.scale.set(scale, scale, scale)
    }
  })
}

附上完整代码地址 gitee.com/TriF/3d_ear...

相关推荐
陈随易28 分钟前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·后端·程序员
SoaringHeart1 小时前
Flutter进阶:基于 EasyRefresh 的下拉刷新封装 n_easy_refresh_mixin.dart
前端·flutter
IT_陈寒3 小时前
Vite的热更新突然不香了,排查三小时差点砸键盘
前端·人工智能·后端
子兮曰4 小时前
Agency-Agents 深度解析:400+ AI 专家的"梦之队"如何重塑开发工作流
前端·后端·vibecoding
竹林8184 小时前
用 The Graph 查询链上数据实战:从手搓 RPC 到 Subgraph,我的 NFT 项目数据加载快了 10 倍
前端·javascript
妙码生花5 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
Awu12275 小时前
⚡从零开发 Agent CLI(五)实现一个可治理、可扩展的工具系统
前端·人工智能·claude
咪库咪库咪6 小时前
Vue3-生命周期
前端
莪_幻尘6 小时前
你的 AI Skill 越多越蠢?Token 上下文爆炸的求生指南
前端·ai编程
lichenyang4537 小时前
从 has.echo 到异步 API 注册表:一次 ASCF API 回调不触发的排查复盘
前端