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

相关推荐
像风一样自由202030 分钟前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
aiprtem1 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊1 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术1 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing1 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止2 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall2 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴2 小时前
简单入门Python装饰器
前端·python
袁煦丞3 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作
天天扭码3 小时前
从图片到语音:我是如何用两大模型API打造沉浸式英语学习工具的
前端·人工智能·github