Model 模型引入

外界引入

1
https://www.gstatic.com/draco/versioned/decoders/1.5.6/

代码组织

HTML

1
<div ref="container" class="contain"></div>

CSS

1
2
3
4
5
    .contain {
        width: 100%;
        height: 100vh;
        display: block;
    }

JS

依赖

1
2
3
4
5
6
7
8
    import * as THREE from 'three'
    import { ref, onMounted, onBeforeUnmount } from 'vue'

    import Stats from 'three/addons/libs/stats.module.js' // 性能监视
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js' // 相机控制
    import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js' // 创建房间环境
    import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js' // 加载GLTF模型  
    import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js' // 加载DRACO压缩的模型
全局变量
1
2
3
4
5
    const container = ref(null) // 容器引用

    let scene, camera, renderer, controls, stats, clock, mixer // 3D场景相关变量

    const MODEL_PATH = '/models/LittlestTokyo.glb' // 导入模型路径

生命周期

1
2
3
4
5
6
7
8
    onMounted(() => {
        init() // 初始化场景
        animate() // 开始动画循环
    })

    onBeforeUnmount(() => {
        dispose() // 清理资源
    })
init 初始化 (核心)

  • clock

  • 用于动画循环

  • renderer

  • .setPixelRatio(window.devicePixelRatio) 像素比,适配高分辨率屏幕

  • .setSize(window.innerWidth, window.innerHeight) 渲染器大小,填满窗口

  • 将 renderer 加入到DOM容器

  • stats 性能监视器

  • 将 stats 加入到DOM容器

  • Scene

    • 背景色

    • 环境贴图

  • camera

  • 相机位置

  • controls

  • 模型中心

  • 阻尼 + 阻尼系数

  • loader

  • .setDRACOLoader(dracoLoader) 加载器

  • .load(…) 加载模型

代码👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    function init() {
        clock = new THREE.Clock()

        renderer = new THREE.WebGLRenderer({ antialias: true })
        renderer.setPixelRatio(window.devicePixelRatio)
        renderer.setSize(window.innerWidth, window.innerHeight)

        container.value.appendChild(renderer.domElement)

        stats = new Stats()
        container.value.appendChild(stats.dom)

        scene = new THREE.Scene()
        scene.background = new THREE.Color(0xbfe3dd)

        const roomEnv = new RoomEnvironment()
        const pmremGenerator = new THREE.PMREMGenerator(renderer)
        scene.environment = pmremGenerator.fromScene(roomEnv, 0.04).texture

        camera = new THREE.PerspectiveCamera(
            40,
            window.innerWidth / window.innerHeight,
            0.1,
            100
        )
        camera.position.set(5, 2, 8)

        controls = new OrbitControls(camera, renderer.domElement)
        controls.target.set(0, 0.5, 0)
        controls.enableDamping = true
        controls.dampingFactor = 0.05

        const dracoLoader = new DRACOLoader()
        dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')

        const loader = new GLTFLoader()
        loader.setDRACOLoader(dracoLoader)
        loader.load(
            MODEL_PATH,
            function (gltf) {
                const model = gltf.scene
                model.position.set(1, 1, 0)
                model.scale.set(0.01, 0.01, 0.01)
                scene.add(model)

                mixer = new THREE.AnimationMixer(model)
                if (gltf.animations.length) mixer.clipAction(gltf.animations[0]).play()
                renderer.setAnimationLoop(animate)
            },
            undefined,
            function (e) {
                console.error('模型加载出错:', e)
            }
        )
        window.addEventListener('resize', onWindowResize)
    }
animate 动画循环 (核心)
1
2
3
4
5
6
7
    function animate() {
        const delta = clock.getDelta() // 获取时间增量
        if (mixer) mixer.update(delta) // 更新动画混合
        controls.update() // 更新控制器
        renderer.render(scene, camera) // 渲染场景
        stats.update() // 更新性能监视器
    }
  • delta 时间增量

  • controls.update() 控制器更新

  • renderer.render(scene, camera) 渲染场景

  • stats.update() 更新性能监视器

dispose 卸载
1
2
3
4
5
6
    function dispose() {
        renderer.setAnimationLoop(null) // 停止动画循环
        renderer.dispose() // 清理渲染器资源
        stats.dom.remove() // 移除性能监视器DOM
        window.removeEventListener('resize', onWindowResize) // 移除窗口大小变化监听
    }