Three.js 单模型,组合模型加载

8,405次阅读
没有评论

共计 10255 个字符,预计需要花费 26 分钟才能阅读完成。

前言 :

 

        three.js  基础模型加载可查看  https://threejs.org/  上面涵盖各种模型的加载器及加载方式,在此不再赘述。

        本文主要介绍如何使用 vue 配合 three.js 库实现各种格式 3d 模型文件的加载,组合式模型文件的加载,以及如何去更新相机的设置,调整模型的大小缩放去达到最适合观察的角度与大小。

一,模型加载器引入

   three.js 官网之中提供了各种模型文件所对应的加载器,在此处只是简单列举常用模型加载器,其余加载器的使用方式于此大致无二。

    import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader.js";
    import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
    import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
    import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js';
    import {OBJLoader} from 'three/examples/jsm/loaders/OBJLoader.js';
    import {TDSLoader} from 'three/examples/jsm/loaders/TDSLoader.js';
    import {ThreeMFLoader} from 'three/examples/jsm/loaders/3MFLoader.js';
    import {AMFLoader} from 'three/examples/jsm/loaders/AMFLoader.js';
    import {GCodeLoader} from 'three/examples/jsm/loaders/GCodeLoader.js';
    import {KMZLoader} from 'three/examples/jsm/loaders/KMZLoader.js';
    import {PCDLoader} from 'three/examples/jsm/loaders/PCDLoader.js';
    import {PLYLoader} from 'three/examples/jsm/loaders/PLYLoader.js';
    import {STLLoader} from 'three/examples/jsm/loaders/STLLoader.js';
    import {ColladaLoader} from 'three/examples/jsm/loaders/ColladaLoader.js';
    import {MTLLoader} from 'three/examples/jsm/loaders/MTLLoader.js';
    
    this.modeLoader = {'gltf': () => {return new GLTFLoader() },
        'gcode': () => {return new GCodeLoader() },
        'glb': () => {return new GLTFLoader() },
        'fbx': () => {return new FBXLoader() },
        '3ds': () => {return new TDSLoader() },
        '3mf': () => {return new ThreeMFLoader() },
        'amf': () => {return new AMFLoader() },
        'kmz': () => {return new KMZLoader() },
        'obj': () => {return new OBJLoader() },
        'pcd': () => {return new PCDLoader() },
        'ply': () => {return new PLYLoader() },
        'stl': () => {return new STLLoader() },
        'dae': () => {return new ColladaLoader() },
        'mtl': () => {return new MTLLoader() },
    } 

以上便是常用加载器的列举及其初始化,在此处将各种模型放在一个对象中为了便于后面可以更加方便去通过对象属性的方式拿到此加载器的实例。

二,场景初始化

在此,准备工作便已经全部完成,接下来便开始场景,相机,渲染器,控制器等必不可少的元素的初始化及其属性配置。

init() {this.camera = new Three.PerspectiveCamera(45, this.container.clientWidth /     this.container.clientHeight, 0.1, 1000)
    this.camera.position.set(0, 0, 100)
    this.camera.updateProjectionMatrix();
    this.scene = new Three.Scene()
    const color = new Three.Color(0x800080);
    color.convertSRGBToLinear();
    this.renderer = new Three.WebGLRenderer({antialias: true,})
    this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
    this.renderer.gammaFactor = 2.2;
    this.renderer.outputEncoding = Three.sRGBEncoding
    this.renderer.setPixelRatio(window.devicePixelRatio);  // 设置设备像素比
    this.container.appendChild(this.renderer.domElement)
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    this.controls.enableDamping = true
    this.animate()}

舞台已经打好了,接下来该有我们的主人公出场了,接下来便是在场景中加载模型文件。

三,单个模型加载

Three.js 单模型,组合模型加载

loadModel() {const { type, url} = this.config
    const loader = this.modeLoader[type]()
    if(['gltf','glb'].includes(type)){const dracoloader = this.getDracoLoader()
        loader.setDRACOLoader(dracoloader)
    }
    loader.load(url, (object) => {
        let obj = object
        if (['stl', 'ply'].includes(type)) {
            let material = new Three.MeshPhongMaterial({
                color: 0x4eacc2,
                specular: 0x111111,
                shininess: 200
            })
            obj = new Three.Mesh(object, material);
        } else if (['glb', 'gltf', 'kmz', 'dae'].includes(type)) {obj = object.scene}
        this.scene.add(obj)
        this.initSize(obj)
    })
}

其中根据模型的类型做了处理,对与压缩模型进行了单独处理

const dracoloader = this.getDracoLoader()

loader.setDRACOLoader(dracoloader)

解决压缩模型

const getDracoLoader = () => {const dracoloader = new DRACOLoader()
    dracoloader.setDecoderPath("/draco/")
    dracoloader.setDecoderConfig({type: "js"})
    dracoloader.preload()
    return dracoloader
}

        到此,模型就已经出现在场景中了,但是在此还是有很多问题,比如说,模型的大小,有些很大,有些很小甚至在场景中只能看见一个小点只能通过鼠标滚轮去放大,才能让他展示在我们面前,那有没有好的办法让他直接已最好的姿态展示在我们面前呢,当然可以,接下来我们要请出灯光师去给我们的舞台加上灯光,让摄影师去调整相机的角度去让模型更好展示。

灯光加载

环境光(AmbientLight):光没有特定方向,只是整体改变场景的光照明暗。

平行光(DirectionalLight):光就是沿着特定方向发射。这种光的表现像是无限远,从它发出的光线都是平行的。常常用平行光来模拟太阳光的效果。

setLight() {const light1 = new Three.DirectionalLight(0xffffff, 1)
    light1.position.set(0, 0, 1)
    this.scene.add(light1)
    const light2 = new Three.DirectionalLight(0xffffff, 1)
    light2.position.set(0, 2, 100)
    this.scene.add(light2)
    const light3 = new Three.DirectionalLight(0xffffff, 1)
    light3.position.set(0, 0, -10)
    this.scene.add(light3)
    const ambientLight = new Three.AmbientLight(0xffffff, 1)
    this.scene.add(ambientLight)
}

模型大小设置:

 initSize(obj) {
    let group = obj;
    group.updateMatrixWorld()
    const box = new Three.Box3().setFromObject(group);
    const size = box.getSize(new Three.Vector3());
    const center = box.getCenter(new Three.Vector3());
    const maxSize = Math.max(size.x, size.y, size.z);
    const targetSize = 2.5; // 目标大小
    const scale = targetSize / (maxSize> 1 ? maxSize : .5);
    group.scale.set(scale, scale, scale)
    this.controls.maxDistance = size.length() * 10
    this.camera.lookAt(center)
    this.camera.updateProjectionMatrix();}

到此,模型就以完美的姿态展现在您的面前了。。。

四,组合模型加载(以 obj 和 mtl 为例)

loadGroupModel() {const { type, url} = this.config
    const typeList = type.split(',')
    let t1 = typeList[0]
    let t2 = typeList[1]
    const loader1 = modeLoader[t1]()
    const loader2 = modeLoader[t2]()
    loader1.load(url[0], (materials) => {materials.preload()
        loader2.setMaterials(materials)
        loader2.load(url[1], (object) => {scene.add(object)
            initSize(object)
        })
    })
}

完整代码 :

import * as Three from 'three'
import {onMounted, reactive, defineComponent, h} from 'vue';
import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader.js";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js';
import {OBJLoader} from 'three/examples/jsm/loaders/OBJLoader.js';
import {TDSLoader} from 'three/examples/jsm/loaders/TDSLoader.js';
import {ThreeMFLoader} from 'three/examples/jsm/loaders/3MFLoader.js';
import {AMFLoader} from 'three/examples/jsm/loaders/AMFLoader.js';
import {GCodeLoader} from 'three/examples/jsm/loaders/GCodeLoader.js';
import {KMZLoader} from 'three/examples/jsm/loaders/KMZLoader.js';
import {PCDLoader} from 'three/examples/jsm/loaders/PCDLoader.js';
import {PLYLoader} from 'three/examples/jsm/loaders/PLYLoader.js';
import {STLLoader} from 'three/examples/jsm/loaders/STLLoader.js';
import {ColladaLoader} from 'three/examples/jsm/loaders/ColladaLoader.js';
import {MTLLoader} from 'three/examples/jsm/loaders/MTLLoader.js';

class LoadModel {constructor(config, elementId) {
        this.config = config
        this.container = document.getElementById(elementId)
        // 相机
        this.camera
        // 场景
        this.scene
        // 渲染器
        this.renderer
        // 控制器
        this.controls
        // 模型
        this.model

        this.modeLoader = {'gltf': () => {return new GLTFLoader() },
            'gcode': () => {return new GCodeLoader() },
            'glb': () => {return new GLTFLoader() },
            'fbx': () => {return new FBXLoader() },
            '3ds': () => {return new TDSLoader() },
            '3mf': () => {return new ThreeMFLoader() },
            'amf': () => {return new AMFLoader() },
            'kmz': () => {return new KMZLoader() },
            'obj': () => {return new OBJLoader() },
            'pcd': () => {return new PCDLoader() },
            'ply': () => {return new PLYLoader() },
            'stl': () => {return new STLLoader() },
            'dae': () => {return new ColladaLoader() },
            'mtl': () => {return new MTLLoader() },
        }

        this.init()
        this.setLight()
        this.loadModel()}

    animate() {requestAnimationFrame(() => this.animate())
        this.renderer.render(this.scene, this.camera)
        this.controls.update()}

    init() {this.camera = new Three.PerspectiveCamera(45, this.container.clientWidth / this.container.clientHeight, 0.1, 1000)
        this.camera.position.set(0, 0, 100)
        this.camera.updateProjectionMatrix();
        this.scene = new Three.Scene()
        const color = new Three.Color(0x800080);
        color.convertSRGBToLinear();
        this.renderer = new Three.WebGLRenderer({antialias: true,})
        this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
        this.renderer.gammaFactor = 2.2;
        this.renderer.outputEncoding = Three.sRGBEncoding
        this.renderer.setPixelRatio(window.devicePixelRatio);  // 设置设备像素比
        this.container.appendChild(this.renderer.domElement)
        this.controls = new OrbitControls(this.camera, this.renderer.domElement)
        this.controls.enableDamping = true
        this.animate()}

    setLight() {const light1 = new Three.DirectionalLight(0xffffff, 1)
        light1.position.set(0, 0, 1)
        this.scene.add(light1)
        const light2 = new Three.DirectionalLight(0xffffff, 1)
        light2.position.set(0, 2, 100)
        this.scene.add(light2)
        const light3 = new Three.DirectionalLight(0xffffff, 1)
        light3.position.set(0, 0, -10)
        this.scene.add(light3)
        const ambientLight = new Three.AmbientLight(0xffffff, 1)
        this.scene.add(ambientLight)
    }

    // 设置模型大小
    initSize(obj) {
        let group = obj;
        group.updateMatrixWorld()
        const box = new Three.Box3().setFromObject(group);
        const size = box.getSize(new Three.Vector3());
        const center = box.getCenter(new Three.Vector3());
        const maxSize = Math.max(size.x, size.y, size.z);
        const targetSize = 2.5; // 目标大小
        const scale = targetSize / (maxSize> 1 ? maxSize : .5);
        group.scale.set(scale, scale, scale)
        this.controls.maxDistance = size.length() * 10
        this.camera.lookAt(center)
        this.camera.updateProjectionMatrix();}


    getDracoLoader() {const dracoloader = new DRACOLoader()
        dracoloader.setDecoderPath("/draco/")
        dracoloader.setDecoderConfig({type: "js"})
        dracoloader.preload()
        return dracoloader
    }

    loadModel() {const { type, url} = this.config
        const loader = this.modeLoader[type]()
        if(['gltf','glb'].includes(type)){const dracoloader = this.getDracoLoader()
            loader.setDRACOLoader(dracoloader)
        }
        loader.load(url, (object) => {
            let obj = object
            if (['stl', 'ply'].includes(type)) {
                let material = new Three.MeshPhongMaterial({
                    color: 0x4eacc2,
                    specular: 0x111111,
                    shininess: 200
                })
                obj = new Three.Mesh(object, material);
            } else if (['glb', 'gltf', 'kmz', 'dae'].includes(type)) {obj = object.scene}
            this.scene.add(obj)
            this.initSize(obj)
        })
    }

    loadGroupModel() {const { type, url} = this.config
        const typeList = type.split(',')
        let t1 = typeList[0]
        let t2 = typeList[1]
        const loader1 = modeLoader[t1]()
        const loader2 = modeLoader[t2]()
        loader1.load(url[0], (materials) => {materials.preload()
            loader2.setMaterials(materials)
            loader2.load(url[1], (object) => {scene.add(object)
                initSize(object)
            })
        })
    }
}

export default LoadModel

结语 :

         既然选择了远方,便只顾风雨兼程。

原文地址: Three.js 单模型,组合模型加载

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-07发表,共计10255字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)