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

相关推荐
合作小小程序员小小店14 小时前
web网页开发,在线%考试,教资,题库%系统demo,基于vue,html,css,python,flask,随机分配,多角色,前后端分离,mysql数据库
前端·vue.js·后端·前端框架·flask
慧一居士14 小时前
ES6(ECMAScript 2015)功能介绍,使用场景,对应功能点完整使用示例
前端
吃饺子不吃馅14 小时前
了解微前端:为何 Web Component 是绕不开的关键技术?
前端·javascript·架构
恋猫de小郭14 小时前
第一台 Andriod XR 设备发布,Jetpack Compose XR 有什么不同?对原生开发有何影响?
android·前端·flutter
muyouking1114 小时前
Tailwind CSS 小白快速入门速查手册
前端·css·css3
社恐的下水道蟑螂14 小时前
用 n8n 打造 AI 科技新闻速览工作流:自动化获取、总结,每天高效充电
前端
陈随易14 小时前
PaddleOCR-VL可太强了,图片识别转文字的巅峰之作
前端·后端·程序员
盗德14 小时前
紧急项目下,前端是“先乱炖”还是“慢火煲汤”?我选第三条路
前端·程序员
zmirror14 小时前
Monorepo单仓多包&独立部署
前端
Linsk14 小时前
为什么BigInt无法通过Babel降级?
前端·typescript·前端工程化