利用 Three.js 实现汽车模型的自动躲避功能

举报
不惑 发表于 2025/01/08 19:10:57 2025/01/08
【摘要】 本文展示了如何使用 Three.js 实现汽车模型的自动躲避功能。从基本场景的搭建到模型的加载,再到碰撞检测与躲避逻辑的实现,涵盖了许多 Three.js 的核心功能。通过这个项目,可以获得关于 3D 图形编程的深刻理解,并能够将这些知识应用于更复杂的项目中。希望本文对你在 Three.js 的学习和开发中有所帮助!

引言

在现代计算机图形学中,Three.js作为一个强大的WebGL库,为开发者提供了创建复杂3D场景的能力。本文将详细介绍如何利用Three.js加载GLTF模型,并实现一个简单的汽车自动躲避功能。我们将逐步解析代码实现的每一个环节,并深入探讨其中的关键概念与技术。
image.png

Three.js 概述

Three.js 是一个开源的 JavaScript 库,旨在简化 WebGL 的使用。通过 Three.js,开发者可以轻松创建复杂的 3D 场景,并通过简洁的 API 进行交互。库提供了丰富的几何体、材质、光照和阴影等功能,使得开发者能够构建出高度逼真的图形。

项目准备

首先,确保您已经设置好开发环境,并安装了Three.js库。可以使用npm工具进行安装:

环境搭建

在开始之前,我们需要搭建好开发环境。确保你有以下工具:

  • Node.js 和 npm
  • 一个现代浏览器(如 Chrome 或 Firefox)
  • 一个代码编辑器(如 Visual Studio Code)

依赖安装

首先,使用 npm 安装 Three.js 和其他依赖库:

npm install three
npm install --save three/examples/jsm/loaders/GLTFLoader
npm install --save three/examples/jsm/loaders/FontLoader
npm install --save three/examples/jsm/geometries/TextGeometry

模型准备

我们需要准备两款汽车的 GLB 模型,可以从网上下载或自己创建。确保模型的路径正确,以便在项目中加载。

实现汽车自动躲避功能

在现代计算机图形学中,Three.js是一个强大的WebGL库,能够帮助开发者创建复杂的3D场景。本文将介绍如何使用Three.js加载GLTF模型,并实现一个简单的汽车自动躲避功能。我们将详细探讨代码实现的每一步,并讨论关键概念。

创建基本场景

为了开始构建我们的3D应用,我们需要创建一个Three.js场景、相机和渲染器。场景是所有3D对象的容器,相机用于观察场景,而渲染器则负责将场景中的对象绘制到屏幕上。

我们首先创建了一个新的场景对象,然后定义了一个透视相机,设置视野范围和比例,并最后将渲染器添加到文档的主体中。相机的位置设定为(1, 8, 9),以便能从高处俯瞰场景。

import * as THREE from 'three';
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 设置相机位置
camera.position.set(1, 8, 9);
camera.lookAt(0, 0, 0);

添加道路

为了让汽车看起来像是在行驶,我们需要在场景中创建一条道路。道路的表示可以使用PlaneGeometry,这将创建一个平面,并可以在其上绘制纹理来模拟实际道路的外观。

在这里,我们定义了一个createRoad函数,该函数加载指定路径的纹理,并创建一个平面几何体作为道路。然后将其添加到场景中,并通过旋转使其水平放置。

// 创建道路的函数
function createRoad(texturePath) {
    const roadTexture = new THREE.TextureLoader().load(texturePath);
    const roadGeometry = new THREE.PlaneGeometry(10, 100);
    const roadMaterial = new THREE.MeshBasicMaterial({ map: roadTexture });
    const roadMesh = new THREE.Mesh(roadGeometry, roadMaterial);
    roadMesh.rotation.x = -Math.PI / 2;
    return roadMesh;
}

// 添加道路到场景
const road = createRoad('./your/image.png');
scene.add(road);

加载汽车模型

我们将使用GLTFLoader来加载汽车的GLTF模型。GLTF(GL Transmission Format)是一种开放标准,用于传输和加载3D模型。我们将异步加载两个汽车模型,并将它们添加到场景中。

我们定义了一个loadCarModel函数,该函数接收加载器、模型路径、位置、旋转角度和缩放比例作为参数,返回一个Promise,以便我们可以在模型加载完成后进行后续处理。我们使用Promise.all来并行加载两个汽车模型,确保它们都加载完成后再进行下一步。

// 加载汽车模型的函数
function loadCarModel(loader, path, position, rotationY, scale) {
    return new Promise((resolve, reject) => {
        loader.load(path, (gltf) => {
            const car = gltf.scene;
            car.position.copy(position);
            car.rotation.y = rotationY;
            car.scale.set(scale, scale, scale);
            scene.add(car);
            resolve(car);
        }, undefined, (error) => {
            console.error('模型加载失败:', error);
            reject(error);
        });
    });
}

// 使用 Promise.all 加载两个汽车模型
Promise.all([
    loadCarModel(gltfLoader, './modles/su7.glb', new THREE.Vector3(0, 0, 0), Math.PI / 2, 0.2),
    loadCarModel(gltfLoader, './modles/su7.glb', new THREE.Vector3(0, 0, 2), Math.PI / 2, 0.2)
]).then(models => {
    cars = models;
    if (font) {
        createDistanceTextAndStartAnimation();
    }
});

加载字体

为了能够在场景中显示汽车之间的距离信息,我们需要加载字体。这可以通过FontLoader实现,以便我们能够创建文本几何体并在场景中展示。

我们通过FontLoader加载指定的字体文件,并在加载完成后将其存储在变量font中。加载完成后,我们检查汽车是否已加载完毕,如果是,则调用创建距离文本的函数并启动动画。

// 加载字体
const fontLoader = new FontLoader();
fontLoader.load('/json/STKaiti_Regular.json', (loadedFont) => {
    font = loadedFont;
    if (cars.length === 2) {
        createDistanceTextAndStartAnimation();
    }
}, undefined, (error) => {
    console.error('字体加载失败:', error);
});

创建和更新距离文本

我们需要创建一个文本对象来显示两辆车之间的距离,并在动画循环中动态更新该文本。createDistanceText函数创建一个文本对象,显示当前两辆车的距离,并放置在指定的位置。updateDistanceText函数负责更新文本内容,以反映实时的距离信息。

// 创建距离文本的函数
function createDistanceText(font, car1, car2, x, y, z) {
    const distanceMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
    const initialDistance = Math.abs(car1.position.z - car2.position.z).toFixed(2);
    const distanceTextGeometry = new TextGeometry(`距离: ${initialDistance}m`, {
        font,
        size: 0.5,
        height: 0.1,
    });

    const distanceText = new THREE.Mesh(distanceTextGeometry, distanceMaterial);
    distanceText.position.set(x, y, z);
    return distanceText;
}

// 更新距离文本的函数
function updateDistanceText(distanceText, car1, car2) {
    const distance = Math.abs(car1.position.z - car2.position.z).toFixed(2);
    distanceText.geometry.dispose();
    distanceText.geometry = new TextGeometry(`距离: ${distance}m`, {
        font,
        size: 0.5,
        height: 0.1,
    });
}

实现动画循环

我们需要实现一个动画循环,以持续更新汽车的位置和距离文本。这是实现动态交互和动画的关键部分,在animate函数中,我们创建了一个名为loop的内部函数,该函数使用requestAnimationFrame来实现动画效果。我们在每一帧中更新汽车的位置,并根据实时距离更新距离文本。在updateCarPositions函数中,我们使汽车根据预设速度向前移动,并实现自动躲避逻辑。

// 动画循环
function animate() {
    function loop() {
        requestAnimationFrame(loop);
        updateCarPositions();
        if (distanceText && cars.length === 2) {
            updateDistanceText(distanceText, cars[0], cars[1]);
        }
        renderer.render(scene, camera);
    }
    loop();
}

// 更新汽车位置的函数
function updateCarPositions() {
    cars.forEach((car, index) => {
        car.position.z -= index === 0 ? 0.01 : 0.018;

        if (car.position.z < -50) car.position.z = 50;

        if (index === 1 && cars[0]) {
            const distance = cars[0].position.distanceTo(car.position);
            if (distance < 1) {
                car.position.x += 0.1;
                if (car.position.x > 5) car.position.x = 5;
            }
        }
    });
}

结论

通过上述步骤,我们使用Three.js创建了一个简单的3D场景,其中包含加载的汽车模型和动态更新的距离文本。这个示例展示了如何处理3D模型、文本显示以及简单的动画逻辑。希望通过这篇文章,您能对Three.js的使用有更深入的理解,并能够根据自己的需求进行扩展和修改。

小车.gif

后续工作

在实现基础功能之后,可以进一步扩展此项目:

  • 添加更多汽车模型:尝试加载不同的汽车模型,并实现不同的行为。
  • 引入物理引擎:可以利用如Cannon.js等物理引擎,实现更真实的碰撞检测和反应。
  • 优化性能:确保在更多对象的情况下仍然能够流畅运行,您可以考虑使用合并几何体和减少

通过不断尝试和实践,能更好地掌握Three.js的强大功能,并创造出更加丰富的3D应用。为了让场景更加生动,我们可以添加一些动态效果,比如汽车的移动速度、背景音乐等。可以使用 Tween.js 来实现平滑的动画效果,或者加入简单的物理引擎来模拟真实的汽车行为。

性能优化

在实现了基本功能后,我们需要关注性能优化。Three.js 提供了许多优化技巧,例如:

  • 使用 InstancedMesh 渲染多个相同的对象。
  • 合并几何体以减少 Draw Call。
  • 使用 Level of Detail (LOD) 技术。

结论

本文展示了如何使用 Three.js 实现汽车模型的自动躲避功能。从基本场景的搭建到模型的加载,再到碰撞检测与躲避逻辑的实现,涵盖了许多 Three.js 的核心功能。通过这个项目,可以获得关于 3D 图形编程的深刻理解,并能够将这些知识应用于更复杂的项目中。希望本文对你在 Three.js 的学习和开发中有所帮助!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。