ThreeJs——引入和使用外部文件

407 阅读4分钟

引入外部文件

  • 引入需要的插件

    • 控制器(可以操作模型)
      • 对加载出来的物体进行鼠标的操作,放大、缩小、旋转、平移
    • 模型加载器
      • 可以用不同的模型加载器加载不同文件类型的模型
      • 这次使用的是GLTFLoader.js,用来加载gltf模型
        • 以扩展的形式,将加载的js代
        • 可以在模型的基础上,进行多其他效果的渲染(如分块着色等)
import * as THREE from 'three';
// 引入控制器
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
// 引入模型加载器
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';
  • 相机

    • 一般使用透视投影相机(PerspectiveCamera)
      • 基本等同于人的眼睛
      • 离的远的物体会显得比较小,近处的物体会比较大
    • 还有一种叫正交投影相机(OrthographicCamera)

image.png

image.png

const aspect = this.width / this.height;
// 创建一个透视投影相机
// 视景体竖直方向上的张角45度
// aspect等于width/height,是照相机水平方向和竖直方向长度的比值,通常设为canvas的横纵比例
// 照相机到视景体最近的距离 1
// 照相机到视景体最远的距离 1000
this.camera = new THREE.PerspectiveCamera(45, aspect, 1, 1000);
// 照相机放的位置
this.camera.position.z = 15;
  • 控制器

    • 轨道控制器可以使得相机围绕目标进行轨道运动
this.controls = new OrbitControls(this.camera, this.canvas);
// 你能够垂直旋转的角度的下限
// 范围是0到Math.PI,默认值为0,0的话可以看到顶部
this.controls.minPolarAngle = 0;
// 你能够垂直旋转的角度的上限,范围是0到Math.PI,其默认值为Math.PI
// Math.PI的话可以看到底部
this.controls.maxPolarAngle = Math.PI;
// 滑动效果是否流畅
this.controls.smooth = true;
// 平滑滑动的速度
this.controls.smoothspeed = 0.95;
// 相机以自动围绕目标旋转
this.controls.autoRotate = true;
// 围绕目标旋转的速度,默认值为2.0,相当于钟表每旋转一周需要30秒
this.controls.autoRotateSpeed = 1;
// 够将相机向外移动的最大距离(相机越远物体越小)
this.controls.maxDistance = 50;
// 够将相机向外移动的最小距离
this.controls.minDistance = 12;
// 如果.autoRotate被启用,你必须在你的动画循环里调用.update()
this.controls.update();

完整代码如下

/**
 * @version 1.0.1
 * @author yangf
 */
// 引入three
import * as THREE from 'three';
// 引入控制器
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
// 引入模型加载器
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';

export default {
    name: 'page-demo',
    data() {
        return {
            canvas: null,
            scene: null,
            camera: null,
            renderer: null,
            width: 0,
            height: 0,
            activeIndex: 0,
            meshList: [],
            meshColorList: []
        };
    },
    mounted() {
        // 初始化
        this.init();
        // 加载模型
        this.loadModel();
        // 更新模型
        this.update();
    },
    methods: {
        // 初始化
        init() {
            // 获取要加载模型的容器,并且创建canvas
            this.initEL();
            this.initSence();
            this.initLight();
            this.initCamera();
            this.initRenderer();
            this.render();
            this.initControls();
            this.root.appendChild(this.canvas);
        },
        // 获取渲染dom节点
        initEL() {
            this.root = document.getElementById('content');
            this.canvas = document.createElement('canvas');
            this.width = this.root.offsetWidth;
            this.height = this.root.offsetHeight;
        },
        // 初始化场景
        initSence() {
            this.scene = new THREE.Scene();
        },
        // 初始化光源
        initLight() {
            // 环境光(颜色会添加到整个场景和所有对象的当前颜色上)
            // 参数为颜色和光照强度
            const ambient = new THREE.AmbientLight(0xffffff, 0.9);
            this.scene.add(ambient);
            // 点光源(光线来自同一点,且辐射方向四面八方,如蜡烛发出的光)
            // const point = new THREE.PointLight(0x0655fd, 0.1, 100);
            // 平行光
            // const directional = new THREE.DirectionalLight(0xffffff, 0.5);
            // this.scene.add(point);
            // this.scene.add(directional);
        },
        // 初始化相机
        initCamera() {
            const aspect = this.width / this.height;
            // 创建一个透视投影相机
            // 视景体竖直方向上的张角45度
            // aspect等于width/height,是照相机水平方向和竖直方向长度的比值,通常设为canvas的横纵比例
            // 照相机到视景体最近的距离 1
            // 照相机到视景体最远的距离 1000
            this.camera = new THREE.PerspectiveCamera(45, aspect, 1, 1000);
            // 照相机放的位置
            this.camera.position.z = 15;
        },
        // 初始化渲染
        initRenderer() {
            // 创建渲染器
            this.renderer = new THREE.WebGLRenderer({antialias: true});
            // 设置渲染器为容器高宽大小
            this.renderer.setSize(this.width, this.height);
            // 设置渲染器的容器颜色
            this.renderer.setClearColor(0xeeeeee, 1);
            // 渲染器给canvas,为了在 HTML 创建的容器中添加渲染器的 DOM 元素
            this.canvas = this.renderer.domElement;
        },
        // 初始化控制器
        initControls() {
            this.controls = new OrbitControls(this.camera, this.canvas);
            // 你能够垂直旋转的角度的下限
            // 范围是0到Math.PI,默认值为0,0的话可以看到顶部
            this.controls.minPolarAngle = 0;
            // 你能够垂直旋转的角度的上限,范围是0到Math.PI,其默认值为Math.PI
            // Math.PI的话可以看到底部
            this.controls.maxPolarAngle = Math.PI;
            // 滑动效果是否流畅
            this.controls.smooth = true;
            // 平滑滑动的速度
            this.controls.smoothspeed = 0.95;
            // 相机以自动围绕目标旋转
            this.controls.autoRotate = true;
            // 围绕目标旋转的速度,默认值为2.0,相当于钟表每旋转一周需要30秒
            this.controls.autoRotateSpeed = 60;
            // 够将相机向外移动的最大距离(相机越远物体越小)
            this.controls.maxDistance = 30;
            // 够将相机向外移动的最小距离
            this.controls.minDistance = 10;
        },
        // 加载模型
        loadModel() {
            this.loader = new GLTFLoader();
            this.loader.load('static/cat_dispenser/scene.gltf', (model) => {
                let index = 1;
                // 将每个模块存起来
                model.scene.traverse((child) => {
                    if (child instanceof THREE.Mesh) {
                        child.name = `模块${index}`;
                        index++;
                        this.meshList.push(child);
                        this.meshColorList.push(child.material);
                        // child.material.map = null;
                    }
                });
                this.scene.add(model.scene);
                this.renderer.render(this.scene, this.camera);
            });
        },
        // 渲染
        render() {
            this.renderer.render(this.scene, this.camera);
        },
        // 更新模型
        update() {
            // 帧循环
            requestAnimationFrame(() => this.update());
            // 如果.autoRotate被启用,你必须在你的动画循环里调用.update()
            // 自动动
            // this.controls.update();
            this.render();
        },
        // 点击添加和去除材质
        chickChangeMaterial(item, index) {
            this.$set(this.meshList[index], 'active', !item.active);
            // 选择出模型
            const rgb = 'rgb(255,0,0)'.replace(/[rgb]|[(]|[)]|\s/g, '').split(',');
            this.meshList[index].material = this.setTexture(rgb);
        },
        // 设置材质
        setTexture(rgb) {
            // 定义一张纹理(可以理解为布)
            const size = 200 * 200;
            const data = new Uint8Array(3 * size);
            for (let i = 0; i < size; i++) {
                const stride = i * 3;
                data[stride] = rgb[0];
                data[stride + 1] = rgb[1];
                data[stride + 2] = rgb[2];
            }
            // 创建一个纹理
            const texture = new THREE.DataTexture(data, 100, 100, THREE.RGBFormat);
            // 将纹理作为材质返回
            return new THREE.MeshPhongMaterial({map: texture});
        }
    }
};

// html代码
<!--
 * @name:
 * @author: yangf
 * @description:
 * @createTime: Do not edit
 * @UpdateTime: Do not edit
-->
<template>
    <div class="fd-page-content">
        <button class="fd-mb20" @click="clickGoto('/index')">返回</button>
        <div id="content" class="canvas"></div>
        <button @click="chickChangeMaterial(item,index)"
            :class="['fd-ctrl-btn',{active:item.active}]"
            v-for="(item, index) in meshList" :key="index"
            >{{item.name}}</button>
    </div>
</template>
<script src="./index.js"></script>
<style scoped>
.fd-ctrl-btn {
    margin: 50px 10px;
    background: #5cadff !important;
    border-color: #5cadff !important;
}

.fd-ctrl-btn.active {
    background: #a1a7ad !important;
    border-color: #a1a7ad !important;
}
</style>

里面使用的3D模型地址:sketchfab.com/3d-models/c…