使用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...

相关推荐
艾小码16 分钟前
手把手教你实现一个EventEmitter,彻底告别复杂事件管理!
前端·javascript·node.js
幸福摩天轮25 分钟前
npm发布包
前端
前端AK君28 分钟前
Gitlab 线上合并冲突的坑
前端
ze_juejin28 分钟前
ES6 Module 深入学习
前端
章丸丸31 分钟前
Tube - Studio Videos
前端·后端
因吹斯汀1 小时前
一饭封神:当AI厨神遇上你的冰箱,八大菜系大师在线battle!
前端·vue.js·ai编程
再学一点就睡1 小时前
NATAPP 内网穿透指南:让本地项目轻松 “走出去”
前端
拜无忧1 小时前
2025最新React项目架构指南:从零到一,为前端小白打造
前端·react.js·typescript
稻草人不怕疼1 小时前
记一次从“按钮点不动”到“窗口派发缺失”的排查过程
前端
irving同学462382 小时前
TypeORM 列装饰器完整总结
前端·后端·nestjs