Three.js学习之旅--加载3D模型

今天继续学习Three.js,完成一个小案例,目标:学会导入GLTF(3D模型)

废话不多说,几行代码,现在开始!

初始化环境

新建html文件,并添加相机、场景、轨道控制器、渲染器,这个不会添加的,可以看看我前面的文章,很简单的。

往期回顾

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>案例-小火车</title>
    <script type="importmap">
        {
            "imports":{
                "three":"./node_modules/three/build/three.module.js",
                "three/addons/":"./node_modules/three/examples/jsm/"
            }
        }
    </script>
</head>

<body>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>

    <script type="module">
        import * as THREE from 'three';
        // 引入轨道控制器
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';


        // 创建场景
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0xbfe3dd);



        // 相机
        const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100);
        // 相机位置  
        camera.position.set(5, 2, 8);


        // 创建渲染器  
        const renderer = new THREE.WebGLRenderer({ antialias: true });  //抗锯齿
        // 设置渲染画布大小
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 将渲染画布插入到body中
        document.body.appendChild(renderer.domElement); //renderer.domElement为渲染画布元素

        // 创建轨道控制器
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.target.set(0, 0.5, 0);
        controls.update();
        // 添加阻尼效果(惯性)
        controls.enableDamping = true;


        // 渲染
        function animate() {
            renderer.render(scene, camera);
            controls.update();

            requestAnimationFrame(animate)
        }

        animate();

    </script>

</body>

</html>

添加3D模型

html 复制代码
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('./node_modules/three/examples/jsm/libs/draco/')
loader.setDRACOLoader(dracoLoader)
loader.load('./static/img/LittlestTokyo.glb', function (gltf) {
    const model = gltf.scene;
    model.position.set(1, 1, 0);
    model.scale.set(0.01, 0.01, 0.01);
    scene.add(model);
    animate();
});

此时的效果:

为什么会是一坨黑色的呢,因为我们没有给场景加上光源,就像是日常没有太阳,我们加上一个环境光:

html 复制代码
//创建光源
// 环境光
const light = new THREE.AmbientLight(0xffffff);
scene.add(light);

此时,这个3D模型已经可以被看见了:

使用 AnimationMixer动画混合器

使用 AnimationMixer动画混合器,让3D模型的小火车动起来,这里小火车运动,是模型上已经有的,所以说three.js很大部分,还是要靠建模的!建模太耗费精力,还是让专业的建模师去做吧。

model是3D模型,gltf.animations[0]是模型的组件(小火车),每个模型都可以由很多的组件。

html 复制代码
let mixer;
const clock = new THREE.Clock();


loader.load('./static/img/LittlestTokyo.glb', function (gltf) {
    ...
    mixer = new THREE.AnimationMixer(model);
    mixer.clipAction(gltf.animations[0]).play();
    ...
});


// 渲染
function animate() {
    ... 
    const delta = clock.getDelta();
    mixer.update(delta);
    ...
    
}

效果:

其实除了场景、相机外,也就几行代码。

完整代码

xml 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>案例-小火车</title>
    <script type="importmap">
        {
            "imports":{
                "three":"./node_modules/three/build/three.module.js",
                "three/addons/":"./node_modules/three/examples/jsm/"
            }
        }
    </script>
</head>

<body>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>

    <script type="module">
        import * as THREE from 'three';
        // 引入轨道控制器
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';


        // 创建场景
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0xbfe3dd);


        //创建光源
        // 环境光
        const light = new THREE.AmbientLight(0xffffff);
        scene.add(light);

        // 相机
        const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100);
        // 相机位置  
        camera.position.set(5, 2, 8);


        // 创建渲染器  
        const renderer = new THREE.WebGLRenderer({ antialias: true });  //抗锯齿
        // 设置渲染画布大小
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 将渲染画布插入到body中
        document.body.appendChild(renderer.domElement); //renderer.domElement为渲染画布元素

        // 创建轨道控制器
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.target.set(0, 0.5, 0);
        controls.update();
        // 添加阻尼效果(惯性)
        controls.enableDamping = true;


        // GLTFLoader 加载GLB模型
        const loader = new GLTFLoader();
        const dracoLoader = new DRACOLoader()
        dracoLoader.setDecoderPath('./node_modules/three/examples/jsm/libs/draco/')
        loader.setDRACOLoader(dracoLoader)
        loader.load('./static/img/LittlestTokyo.glb', 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);
            mixer.clipAction(gltf.animations[0]).play();
            animate();
        });

        // 让小火车动起来
        let mixer;

        const clock = new THREE.Clock();


        // 渲染
        function animate() {
            renderer.render(scene, camera);
            controls.update();

            const delta = clock.getDelta();
            mixer.update(delta);
            
            requestAnimationFrame(animate)
        }

        // animate();

    </script>

</body>

</html>
相关推荐
你挚爱的强哥22 分钟前
Vue2 实现 1.5s 十连击监听(连续点击若干次),封装通用可复用点击检测工具,不用 data!Vue 封装通用连击监听方法,支持自定义时长与点击次数
前端·javascript·vue.js
半个落月39 分钟前
用 LangChain.js 手写一个能读写文件和执行命令的 Mini Cursor
javascript·人工智能·后端
天外天-亮1 小时前
HBuilder X 使用 uview-plus 方式
开发语言·javascript·hbuilder x·uview-plus
多加点辣也没关系6 小时前
JavaScript|第9章:对象 — 基础
开发语言·javascript·ecmascript
后台开发者Ethan6 小时前
setState组件更新
前端·javascript·react
蜡台7 小时前
uniapp vue2 转 vue3
前端·javascript·uni-app·vue3·vue2
用户059540174467 小时前
把前端内存泄漏排查从2小时压到5分钟,我们把它集成进了CI
前端·css
WindfallSheng8 小时前
从Vibe Coding到Spec Coding:一套可落地的AI-SDD企业级研发实战工程
javascript·vue.js
Asize8 小时前
Agent 入门:从 LLM 与 Agent 的区别到 Function Calling
javascript·人工智能
我有满天星辰8 小时前
Vue 指令完全指南:从 Vue 2 到 Vue 3 的演进与实战
前端·javascript·vue.js