基于three.js与vue3实现无人机3D飞行轨迹模拟

张开发
2026/4/14 14:01:22 15 分钟阅读

分享文章

基于three.js与vue3实现无人机3D飞行轨迹模拟
1. 从零搭建无人机3D飞行模拟环境第一次接触three.js和Vue3的组合时我完全没想到能用这么简单的代码实现酷炫的3D效果。记得当时为了模拟无人机飞行轨迹我整整折腾了两天现在把踩过的坑和最佳实践都分享给你。首先需要明确的是three.js负责3D渲染的核心逻辑而Vue3则用来管理应用状态和响应式更新。这种分工让代码结构变得非常清晰。下面这个基础模板是我经过多个项目验证后提炼出来的template div refcontainer classscene-container/div /template script setup import { ref, onMounted, onUnmounted } from vue import * as THREE from three const container ref(null) let scene, camera, renderer, drone /script这里有个关键细节一定要在Vue的script setup语法糖中声明变量否则响应式更新会出问题。我刚开始就因为这个细节调试了半天。2. 构建3D场景的核心要素2.1 场景初始化最佳实践创建3D场景就像搭积木需要几个核心组件协同工作。下面这段代码是我优化过多次的初始化方案function initScene() { // 场景设置 scene new THREE.Scene() scene.background new THREE.Color(0x87CEEB) // 天空蓝背景 // 相机配置 camera new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ) camera.position.set(0, 10, 20) // 渲染器配置 renderer new THREE.WebGLRenderer({ antialias: true }) renderer.setSize(window.innerWidth, window.innerHeight) container.value.appendChild(renderer.domElement) // 添加光源 const ambientLight new THREE.AmbientLight(0x404040) const directionalLight new THREE.DirectionalLight(0xffffff, 1) directionalLight.position.set(1, 1, 1).normalize() scene.add(ambientLight, directionalLight) }实测发现环境光(AmbientLight)和定向光(DirectionalLight)的组合最能模拟真实光照效果。记得调用normalize()方法规范化光源方向向量这是我早期经常忽略的细节。2.2 创建3D无人机模型原始文章中使用的是2D图片方案但真实项目中我们往往需要更逼真的3D模型。这里分享两种经过验证的方案方案一使用GLTF格式模型import { GLTFLoader } from three/examples/jsm/loaders/GLTFLoader const loader new GLTFLoader() loader.load(drone.gltf, (gltf) { drone gltf.scene scene.add(drone) })方案二用基本几何体组合function createDrone() { const bodyGeo new THREE.BoxGeometry(2, 0.5, 2) const rotorGeo new THREE.CylinderGeometry(0.2, 0.2, 0.05, 16) const material new THREE.MeshPhongMaterial({ color: 0x333333 }) const drone new THREE.Group() // 机身 const body new THREE.Mesh(bodyGeo, material) drone.add(body) // 四个螺旋桨 const positions [ [1, 0, 1], [1, 0, -1], [-1, 0, 1], [-1, 0, -1] ] positions.forEach(pos { const rotor new THREE.Mesh(rotorGeo, material) rotor.position.set(...pos) rotor.rotation.x Math.PI/2 drone.add(rotor) }) return drone }第二种方案虽然简单但已经能表现出无人机的基本特征。建议新手先用这个方案练手等熟悉three.js后再使用专业3D建模软件制作的GLTF模型。3. 实现复杂飞行轨迹3.1 基础运动控制原始文章展示了简单的上下运动实际项目中我们需要更复杂的轨迹控制。下面这个控制器是我在农业无人机项目中实际使用过的const flightController { position: { x: 0, y: 5, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, speed: 0.1, update(drone) { // 位置更新 drone.position.x this.position.x * this.speed drone.position.y this.position.y * this.speed drone.position.z this.position.z * this.speed // 旋转更新 drone.rotation.x this.rotation.x * this.speed drone.rotation.y this.rotation.y * this.speed drone.rotation.z this.rotation.z * this.speed } }使用时只需要在动画循环中调用function animate() { requestAnimationFrame(animate) flightController.update(drone) renderer.render(scene, camera) }3.2 路径飞行实现要让无人机沿着预定路径飞行可以使用样条曲线。这是我调试过的最佳参数组合function createFlightPath() { const curve new THREE.CatmullRomCurve3([ new THREE.Vector3(0, 5, 0), new THREE.Vector3(5, 8, -5), new THREE.Vector3(10, 5, 0), new THREE.Vector3(5, 3, 5), new THREE.Vector3(0, 5, 0) ]) const points curve.getPoints(50) const geometry new THREE.BufferGeometry().setFromPoints(points) const material new THREE.LineBasicMaterial({ color: 0xff0000 }) return new THREE.Line(geometry, material) }在动画循环中这样控制无人机沿路径移动let progress 0 function animate() { requestAnimationFrame(animate) progress 0.005 if (progress 1) progress 0 const point curve.getPointAt(progress) drone.position.copy(point) // 计算朝向 const tangent curve.getTangentAt(progress) drone.lookAt(tangent.add(point)) renderer.render(scene, camera) }这里有个关键技巧使用getPointAt而不是getPoint前者会根据曲线长度均匀分布点使运动速度更稳定。4. Vue3与Three.js的深度整合4.1 响应式控制面板Vue3的响应式特性可以让我们轻松创建飞行控制面板template div classcontrols label飞行速度: input typerange v-modelspeed min0.1 max1 step0.1 {{ speed }} /label button clicktakeOff起飞/button button clickland降落/button /div /template script setup import { ref } from vue const speed ref(0.3) function takeOff() { flightController.position.y 1 } function land() { flightController.position.y -1 } /script4.2 性能优化技巧在Vue3中使用three.js容易遇到性能问题这里分享几个实战经验使用防抖处理窗口resize事件import { debounce } from lodash-es window.addEventListener(resize, debounce(() { camera.aspect window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) }, 100))组件卸载时正确释放资源onUnmounted(() { cancelAnimationFrame(animationId) renderer.dispose() scene.traverse(obj { if (obj.isMesh) { obj.geometry.dispose() obj.material.dispose() } }) })使用Vue的shallowRef优化复杂对象import { shallowRef } from vue const droneModel shallowRef(null) loader.load(drone.gltf, (gltf) { droneModel.value gltf.scene })5. 高级效果实现5.1 添加飞行尾迹让飞行轨迹可视化可以大大提升体验下面是实现代码function createTrail() { const trailGeometry new THREE.BufferGeometry() const trailMaterial new THREE.LineBasicMaterial({ color: 0x00ffff, linewidth: 2 }) const positions new Float32Array(300) // 100个点 trailGeometry.setAttribute(position, new THREE.BufferAttribute(positions, 3)) const trail new THREE.Line(trailGeometry, trailMaterial) scene.add(trail) return { trail, positions, update(position) { // 移动所有点位置 for (let i this.positions.length - 1; i 3; i--) { this.positions[i] this.positions[i - 3] } // 添加新点 this.positions[0] position.x this.positions[1] position.y this.positions[2] position.z this.trail.geometry.attributes.position.needsUpdate true } } }在动画循环中调用trail.update(drone.position)即可看到漂亮的尾迹效果。5.2 环境效果增强真实的飞行模拟需要环境效果这里推荐几个必加的效果雾效scene.fog new THREE.FogExp2(0x87CEEB, 0.002)地面网格const gridHelper new THREE.GridHelper(50, 50, 0x555555, 0x333333) scene.add(gridHelper)天空盒const loader new THREE.CubeTextureLoader() const texture loader.load([ px.jpg, nx.jpg, py.jpg, ny.jpg, pz.jpg, nz.jpg ]) scene.background texture这些效果组合起来能让你的无人机模拟器瞬间提升几个档次。记得调整雾效的密度参数我测试过的最佳范围是0.001到0.003之间。

更多文章