• 作者:老汪软件技巧
  • 发表时间:2024-09-13 10:02
  • 浏览量:

引言

本技术文档将详细介绍如何使用 Three.js 在 Web 环境中加载和控制 GLTF 3D 模型,并为模型设置多个动画状态。本文将包括从初始化 Three.js 场景、加载 GLTF 模型、处理动画混合器和切换动画状态到实现用户界面的步骤。该文档特别适合开发交互式 Web 应用、3D 模型查看器或简单的游戏等场景。

关键概念概述1.Three.js 简介

Three.js 是一个基于 JavaScript 的 3D 图形库,提供了一系列强大且易用的 API 来创建和操控 3D 场景。Three.js 封装了 WebGL 的复杂性,使开发者能够更轻松地实现 3D 渲染。它支持各种几何体、材质、光源、阴影效果等,并且与 GLTF 模型兼容。

2.GLTF(GL 传输格式)简介

GLTF 是一种开放、通用的 3D 文件格式,用于高效传输和加载 3D 模型及其相关资源(如纹理、动画等)。Three.js 提供了对 GLTF 的原生支持,能够方便地加载和渲染这些 3D 模型,并支持它们的动画播放。

实现步骤1.准备工作与环境搭建

在项目中使用 Three.js 进行 3D 渲染时,首先需要确保引入了相关的脚本库。你可以直接使用 CDN 引入,也可以通过 npm 安装。为了简单起见,我们将直接通过 CDN 引入库文件。

html
复制代码
<script src="https://cdn.jsdelivr.net/npm/three@0.137.0/build/three.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/three@0.137.0/examples/js/loaders/GLTFLoader.js">script>

在项目中还需要准备一个简单的 HTML 页面,在这个页面中,Three.js 将加载并渲染 3D 模型。

html
复制代码
html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js GLTF 模型与动画title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    style>
head>
<body>
    <div id="controls">div>
    <script src="three.min.js">script>
    <script src="GLTFLoader.js">script>
body>
html>

2.初始化 Three.js 场景

在 Web 页面中,首先要初始化 Three.js 场景,包括创建场景、相机和渲染器。以下代码演示了如何初始化一个基本的 Three.js 场景。

javascript
复制代码
let scene, camera, renderer;
function init() {
    // 创建场景
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xe0e0e0);  // 设置场景背景颜色
    // 创建相机
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 100);
    camera.position.set(-5, 3, 10);  // 设置相机位置
    camera.lookAt(new THREE.Vector3(0, 2, 0));  // 设置相机目标位置
    // 创建 WebGL 渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    // 添加光源
    const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
    hemiLight.position.set(0, 20, 0);
    scene.add(hemiLight);
    const dirLight = new THREE.DirectionalLight(0xffffff);
    dirLight.position.set(0, 20, 10);
    scene.add(dirLight);
}

3.加载 GLTF 模型

接下来,我们使用GLTFLoader来加载一个 GLTF 模型文件。Three.js 提供了GLTFLoader来解析和加载.gltf或.glb格式的 3D 模型文件。

javascript
复制代码
const loader = new THREE.GLTFLoader();
loader.load('模型文件URL', function (gltf) {
    const model = gltf.scene;
    scene.add(model);  // 将模型添加到场景中
    const mixer = new THREE.AnimationMixer(model);  // 创建动画混合器
    const animations = gltf.animations;  // 获取模型的所有动画
    const actions = {};
    // 遍历动画并创建动作
    animations.forEach(clip => {
        const action = mixer.clipAction(clip);
        actions[clip.name] = action;  // 存储每个动作
    });
    // 设置初始状态并播放动画
    actions['Idle'].play();
});

4.设置动画状态切换

为了能够切换动画状态,我们需要创建一个函数,用来控制模型的不同动画状态的播放。通过调用AnimationMixer的clipAction方法,我们可以获取模型的动画动作,并通过fadeIn和fadeOut来实现平滑切换。

javascript
复制代码
let activeAction, previousAction;
function setState(state) {
    previousAction = activeAction;
    activeAction = actions[state];
    if (previousAction !== activeAction) {
        if (previousAction) {
            previousAction.fadeOut(0.5);  // 旧动作淡出
        }
        activeAction.reset().fadeIn(0.5).play();  // 新动作淡入
    }
}

我们可以通过按钮或其他交互方式来调用setState函数。例如,可以在页面中添加几个按钮,让用户选择不同的动画状态:

html
复制代码
class="controls">

5.响应窗口大小调整

为了确保页面在窗口大小调整时保持正确的比例和布局,我们可以添加一个事件监听器,当窗口大小发生变化时自动调整相机的纵横比并重新设置渲染器的尺寸。

javascript
复制代码
window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});

6.动画更新循环

最后,为了确保动画正常运行,我们需要在requestAnimationFrame中更新场景和动画混合器。这个函数将被循环调用以更新动画帧。

javascript
复制代码
function animate() {
    requestAnimationFrame(animate);
    const delta = clock.getDelta();  // 获取时间间隔
    if (mixer) mixer.update(delta);  // 更新动画
    renderer.render(scene, camera);  // 渲染场景
}

7.完整代码示例

html
复制代码
html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js GLTF Model with Animation Statestitle>
    <style>
        body { margin: 0; }
        canvas { display: block; }
        .controls {
            position: absolute;
            top: 10px;
            left: 10px;
            z-index: 10;
        }
        .controls button {
            margin: 5px;
            padding: 10px;
            background-color: #ccc;
            border: none;
            cursor: pointer;
        }
    style>
head>
<body>
    <div class="controls">
        <button onclick="setState('Idle')">Idlebutton>
        <button onclick="setState('Walking')">Walkingbutton>
        <button onclick="setState('Running')">Runningbutton>
    div>
    <script src="https://cdn.jsdelivr.net/npm/three@0.137.0/build/three.min.js">script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.137.0/examples/js/loaders/GLTFLoader.js">script>
    <script>
        let scene, camera, renderer, model, mixer, actions = {}, activeAction, previousAction;
        const clock = new THREE.Clock();
        function init() {
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xe0e0e0);
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 100);
            camera.position.set(-5, 3, 10);
            camera.lookAt(new THREE.Vector3(0, 2, 0));
            const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
            hemiLight.position.set(0, 20, 0);
            scene.add(hemiLight);
            const dirLight = new THREE.DirectionalLight(0xffffff);
            dirLight.position.set(0, 20, 10);
            scene.add(dirLight);
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);
            const loader = new THREE.GLTFLoader();
            loader.load('https://threejs.org/examples/models/gltf/RobotExpressive/RobotExpressive.glb', function (gltf) {
                model = gltf.scene;
                scene.add(model);
                mixer = new THREE.AnimationMixer(model);
                gltf.animations.forEach((clip) => {
                    const action = mixer.clipAction(clip);
                    actions[clip.name] = action;
                });
                setState('Idle');
                animate();
            });
        }
        function setState(state) {
            if (actions[state]) {
                previousAction = activeAction;
                activeAction = actions[state];
                if (previousAction !== activeAction) {
                    if (previousAction) {
                        previousAction.fadeOut(0.5);
                    }
                    activeAction.reset().fadeIn(0.5).play();
                }
            }
        }
        function animate() {
            requestAnimationFrame(animate);
            const delta = clock.getDelta();
            if (mixer) mixer.update(delta);
            renderer.render(scene, camera);
        }
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
        init();
    script>
body>
html>

结论

通过这篇文档,你可以了解到如何使用 Three.js 加载并控制一个带有多个动画状态的 GLTF 模型。我们详细讲解了如何初始化场景、加载模型、创建动画混合器并切换动画状态。