GLTF动画

在本课中,我们将创建与 FBX 动画课程中创建的项目等效的 GLTF模型

我们使用Blender将主要的FBX模型及其相关的动画文件转换为GLB文件。

资源

如果您不想使用Blender将文件转换为视频中所示的文件,则可以直接下载它们并保存到fbxglb./dist/client/models

可使用GLTF编辑器预览、编辑这几个模型。

启动脚本

./src/client/client.ts

import * as THREE from 'three'

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

import Stats from 'three/examples/jsm/libs/stats.module'

import { GUI } from 'dat.gui'

const scene = new THREE.Scene()

scene.add(new THREE.AxesHelper(5))

const light1 = new THREE.PointLight(0xffffff, 100)

light1.position.set(2.5, 2.5, 2.5)

scene.add(light1)

const light2 = new THREE.PointLight(0xffffff, 100)

light2.position.set(-2.5, 2.5, 2.5)

scene.add(light2)

const camera = new THREE.PerspectiveCamera(

75,

window.innerWidth / window.innerHeight,

0.1,

1000

)

camera.position.set(0.8, 1.4, 1.0)

const renderer = new THREE.WebGLRenderer()

renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)

controls.enableDamping = true

controls.target.set(0, 1, 0)

let mixer: THREE.AnimationMixer

let modelReady = false

const animationActions: THREE.AnimationAction[] = []

let activeAction: THREE.AnimationAction

let lastAction: THREE.AnimationAction

const gltfLoader = new GLTFLoader()

gltfLoader.load(

'models/vanguard.glb',

(gltf) => {

// gltf.scene.scale.set(.01, .01, .01)

复制代码
    mixer = new THREE.AnimationMixer(gltf.scene)

    const animationAction = mixer.clipAction((gltf as any).animations[0])
    animationActions.push(animationAction)
    animationsFolder.add(animations, 'default')
    activeAction = animationActions[0]

    scene.add(gltf.scene)

    // //add an animation from another file
    // gltfLoader.load(
    //     'models/vanguard@samba.glb',
    //     (gltf) => {
    //         console.log('loaded samba')
    //         const animationAction = mixer.clipAction(
    //             (gltf as any).animations[0]
    //         )
    //         animationActions.push(animationAction)
    //         animationsFolder.add(animations, 'samba')

    //         //add an animation from another file
    //         gltfLoader.load(
    //             'models/vanguard@bellydance.glb',
    //             (gltf) => {
    //                 console.log('loaded bellydance')
    //                 const animationAction = mixer.clipAction(
    //                     (gltf as any).animations[0]
    //                 )
    //                 animationActions.push(animationAction)
    //                 animationsFolder.add(animations, 'bellydance')

    //                 //add an animation from another file
    //                 gltfLoader.load(
    //                     'models/vanguard@goofyrunning.glb',
    //                     (gltf) => {
    //                         console.log('loaded goofyrunning');
    //                         (gltf as any).animations[0].tracks.shift() //delete the specific track that moves the object forward while running
    //                         const animationAction = mixer.clipAction(
    //                             (gltf as any).animations[0]
    //                         )
    //                         animationActions.push(animationAction)
    //                         animationsFolder.add(animations, 'goofyrunning')

    //                         modelReady = true
    //                     },
    //                     (xhr) => {
    //                         console.log(
    //                             (xhr.loaded / xhr.total) * 100 + '% loaded'
    //                         )
    //                     },
    //                     (error) => {
    //                         console.log(error)
    //                     }
    //                 )
    //             },
    //             (xhr) => {
    //                 console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
    //             },
    //             (error) => {
    //                 console.log(error)
    //             }
    //         )
    //     },
    //     (xhr) => {
    //         console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
    //     },
    //     (error) => {
    //         console.log(error)
    //     }
    // )
},
(xhr) => {
    console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
    console.log(error)
}

)

window.addEventListener('resize', onWindowResize, false)

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight

camera.updateProjectionMatrix()

renderer.setSize(window.innerWidth, window.innerHeight)

render()

}

const stats = new Stats()

document.body.appendChild(stats.dom)

const animations = {

default: function () {

setAction(animationActions[0])

},

samba: function () {

setAction(animationActions[1])

},

bellydance: function () {

setAction(animationActions[2])

},

goofyrunning: function () {

setAction(animationActions[3])

},

}

const setAction = (toAction: THREE.AnimationAction) => {

if (toAction != activeAction) {

lastAction = activeAction

activeAction = toAction
//lastAction.stop()

lastAction.fadeOut(1)

activeAction.reset()

activeAction.fadeIn(1)

activeAction.play()

}

}

const gui = new GUI()

const animationsFolder = gui.addFolder('Animations')

animationsFolder.open()

const clock = new THREE.Clock()

function animate() {

requestAnimationFrame(animate)

复制代码
controls.update()

if (modelReady) mixer.update(clock.getDelta())

render()

stats.update()

}

function render() {

renderer.render(scene, camera)

}

animate()

最终脚本

./src/client/client.ts

import * as THREE from 'three'

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

import Stats from 'three/examples/jsm/libs/stats.module'

import { GUI } from 'dat.gui'

const scene = new THREE.Scene()

scene.add(new THREE.AxesHelper(5))

const light1 = new THREE.PointLight(0xffffff, 1000)

light1.position.set(2.5, 2.5, 2.5)

scene.add(light1)

const light2 = new THREE.PointLight(0xffffff, 1000)

light2.position.set(-2.5, 2.5, 2.5)

scene.add(light2)

const camera = new THREE.PerspectiveCamera(

75,

window.innerWidth / window.innerHeight,

0.1,

1000

)

camera.position.set(0.8, 1.4, 1.0)

const renderer = new THREE.WebGLRenderer()

renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)

controls.enableDamping = true

controls.target.set(0, 1, 0)

let mixer: THREE.AnimationMixer

let modelReady = false

const animationActions: THREE.AnimationAction[] = []

let activeAction: THREE.AnimationAction

let lastAction: THREE.AnimationAction

const gltfLoader = new GLTFLoader()

gltfLoader.load(

'models/vanguard.glb',

(gltf) => {

// gltf.scene.scale.set(.01, .01, .01)

复制代码
    mixer = new THREE.AnimationMixer(gltf.scene)

    const animationAction = mixer.clipAction((gltf as any).animations[0])
    animationActions.push(animationAction)
    animationsFolder.add(animations, 'default')
    activeAction = animationActions[0]

    scene.add(gltf.scene)

    //add an animation from another file
    gltfLoader.load(
        'models/vanguard@samba.glb',
        (gltf) => {
            console.log('loaded samba')
            const animationAction = mixer.clipAction(
                (gltf as any).animations[0]
            )
            animationActions.push(animationAction)
            animationsFolder.add(animations, 'samba')

            //add an animation from another file
            gltfLoader.load(
                'models/vanguard@bellydance.glb',
                (gltf) => {
                    console.log('loaded bellydance')
                    const animationAction = mixer.clipAction(
                        (gltf as any).animations[0]
                    )
                    animationActions.push(animationAction)
                    animationsFolder.add(animations, 'bellydance')

                    //add an animation from another file
                    gltfLoader.load(
                        'models/vanguard@goofyrunning.glb',
                        (gltf) => {
                            console.log('loaded goofyrunning')
                            ;(gltf as any).animations[0].tracks.shift() //delete the specific track that moves the object forward while running
                            const animationAction = mixer.clipAction(
                                (gltf as any).animations[0]
                            )
                            animationActions.push(animationAction)
                            animationsFolder.add(animations, 'goofyrunning')

                            modelReady = true
                        },
                        (xhr) => {
                            console.log(
                                (xhr.loaded / xhr.total) * 100 + '% loaded'
                            )
                        },
                        (error) => {
                            console.log(error)
                        }
                    )
                },
                (xhr) => {
                    console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
                },
                (error) => {
                    console.log(error)
                }
            )
        },
        (xhr) => {
            console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
        },
        (error) => {
            console.log(error)
        }
    )
},
(xhr) => {
    console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
    console.log(error)
}

)

window.addEventListener('resize', onWindowResize, false)

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight

camera.updateProjectionMatrix()

renderer.setSize(window.innerWidth, window.innerHeight)

render()

}

const stats = new Stats()

document.body.appendChild(stats.dom)

const animations = {

default: function () {

setAction(animationActions[0])

},

samba: function () {

setAction(animationActions[1])

},

bellydance: function () {

setAction(animationActions[2])

},

goofyrunning: function () {

setAction(animationActions[3])

},

}

const setAction = (toAction: THREE.AnimationAction) => {

if (toAction != activeAction) {

lastAction = activeAction

activeAction = toAction
//lastAction.stop()

lastAction.fadeOut(1)

activeAction.reset()

activeAction.fadeIn(1)

activeAction.play()

}

}

const gui = new GUI()

const animationsFolder = gui.addFolder('Animations')

animationsFolder.open()

const clock = new THREE.Clock()

function animate() {

requestAnimationFrame(animate)

复制代码
controls.update()

if (modelReady) mixer.update(clock.getDelta())

render()

stats.update()

}

function render() {

renderer.render(scene, camera)

}

animate()

原文链接:GLTF动画 (mvrlink.com)

相关推荐
C_心欲无痕6 小时前
nginx - 实现域名跳转的几种方式
运维·前端·nginx
花哥码天下6 小时前
恢复网站console.log的脚本
前端·javascript·vue.js
LawrenceLan6 小时前
Flutter 零基础入门(十二):枚举(enum)与状态管理的第一步
开发语言·前端·flutter·dart
Zoey的笔记本6 小时前
「支持ISO27001的GTD协作平台」数据生命周期管理方案与加密通信协议
java·前端·数据库
奔跑的呱呱牛7 小时前
geojson-to-wkt 坐标格式转换
javascript·arcgis
C_心欲无痕7 小时前
Docker 本地部署 CSR 前端项目完整指南
前端·docker·容器
charlie1145141917 小时前
从 0 开始的机器学习——NumPy 线性代数部分
开发语言·人工智能·学习·线性代数·算法·机器学习·numpy
catchadmin8 小时前
Laravel12 + Vue3 的免费可商用商业级管理后台 CatchAdmin V5 正式发布
开发语言·php
康一夏8 小时前
React面试题,封装useEffect
前端·javascript·react.js
袁气满满~_~8 小时前
Python数据分析学习
开发语言·笔记·python·学习