uniapp vue2 vue3 使用three.js

该文展示了如何在Vue3应用中使用Three.js库加载3D模型(支持GLTF和FBX格式),并应用OrbitControls进行视角控制。文章详细阐述了初始化场景、设置相机、渲染器以及光源的过程,并提供了加载模型、调整模型大小以适应视口的计算方法。
摘要由CSDN通过智能技术生成
<template>
    <!-- vue3 -->
  <view class="">
    <view id="threeView">456</view>
    <view class="">
      22222
    </view>
  </view>
</template>
<script module="three" lang="renderjs" setup>
  import * as THREE from 'three'
  //引入轨道控制器(用来通过鼠标事件控制模型旋转、缩放、移动)
  import {
    OrbitControls
  } from '../../static/threeTwo/examples/jsm/controls/OrbitControls.js'
  import {
    GLTFLoader
  } from '../../static/threeTwo/examples/jsm/loaders/GLTFLoader.js'
  import {
    FBXLoader
  } from '../../static/threeTwo/examples/jsm/loaders/FBXLoader.js'
  import {
    computed,
    onMounted,
    ref,
    watch
  } from 'vue'
  // 如果模型没有显示出来, 可能是cameraZ的值不够, 请注意
  let scene = '',
    camera = '',
    renderer = '',
    cube = '',
    sphere = '',
    step = '',
    stats = '',
    group = '',
    cameraZ = 500,
    url = '../../static/texst.glb',
    width = 800,
    height = 560,
    orbitcontrols = '';
  const init = () => {
    console.log(cameraZ, 'cameraZ');
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
    camera.position.set(0, 0, cameraZ);
    renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setClearColor(0xffffff, 0);
    renderer.setSize(width / 1.2, height / 1.2);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.shadowMapEnabled = true;
    document.getElementById("threeView").appendChild(renderer.domElement);
    orbitcontrols = new OrbitControls(camera, renderer.domElement); //移动控件
    orbitcontrols.enabled = true;
    orbitcontrols.enableRotate = true;
    orbitcontrols.enableZoom = false;
    orbitcontrols.autoRotate = true;
    orbitcontrols.minPolarAngle = Math.PI / 4;
    orbitcontrols.maxPolarAngle = 3 - (Math.PI / 4);
    console.log('78', url.endsWith('gltf'), url.endsWith('glb'))
    console.log(url)
    if (url.endsWith('gltf') || url.endsWith('glb')) {
      //设置了六个平行光  有些材质不接受环境光会很暗
      const directionLight1 = new THREE.DirectionalLight(0xffffff, 1);
      directionLight1.position.set(-300, 0, 0)
      scene.add(directionLight1);
 
      const directionLight2 = new THREE.DirectionalLight(0xffffff, 1);
      directionLight2.position.set(300, 0, 0)
      scene.add(directionLight2);
 
      const directionLight3 = new THREE.DirectionalLight(0xffffff, 1);
      directionLight3.position.set(0, 300, 0)
      scene.add(directionLight3);
 
      const directionLight4 = new THREE.DirectionalLight(0xffffff, 1);
      directionLight4.position.set(0, 300, 0)
      scene.add(directionLight4);
 
      const directionLight5 = new THREE.DirectionalLight(0xffffff, 1);
      directionLight5.position.set(0, 0, -300)
      scene.add(directionLight5);
 
      const directionLight6 = new THREE.DirectionalLight(0xffffff, 1);
      directionLight6.position.set(0, 0, 300)
      scene.add(directionLight6);
    }
    let Sun = new THREE.DirectionalLight(0xffffff, 1);
    Sun.position.set(0, 300, 0);
    Sun.castShadow = true;
    let Ambient = new THREE.AmbientLight(0xffffff, 1);
    scene.add(Ambient);
    scene.add(Sun);
  }
  const loadModel = () => {
    let loader1 = new GLTFLoader();
    let FBXloader = new FBXLoader();
    let rotateObj = [];
    const loadLoader = url.endsWith('fbx') ? FBXloader : loader1;
    loadLoader.load(url, function(gltf) {
      const loadscene = gltf.scene || gltf;
 
      loadscene.scale.set(1, 1, 1);
 
      let group = new THREE.Group();
      group.add(loadscene);
 
      let bbox = new THREE.Box3().setFromObject(group);
      // console.log(bbox,'bbox---');
      let mdlen = bbox.max.x - bbox.min.x; //边界的最小坐标值 边界的最大坐标值
      let mdhei = bbox.max.y - bbox.min.y;
      let mdwid = bbox.max.z - bbox.min.z;
      group.position.set(0, 0, 0);
      // console.log(camera,'相机的信息',group,'组的信息');
      let dist = Math.abs(camera.position.z - group.position.z - (mdwid / 2));
      //console.log('dist值为:' + dist );
      let vFov = camera.fov * Math.PI / 180; //弧度=角度*Math.PI/180
      //console.log('vFov值为:' + vFov );
      let vheight = 2 * Math.tan(vFov * 0.5) * dist;
      //console.log('vheight值为:' + vheight );
      let fraction = mdhei / vheight;
      // console.log('fraction值为:' + fraction );
      let finalHeight = height * fraction;
      //console.log('finalHeight值为:' + finalHeight);
      let finalWidth = (finalHeight * mdlen) / mdhei;
      //console.log('finalWidth值为:' + finalWidth );                
 
      let value1 = width / finalWidth;
      // console.log('value1缩放比例值为:' + value1);
      let value2 = height / finalHeight;
      // console.log('value2缩放比例值为:' + value2);
 
      if (value1 >= value2) {
        group.scale.set(value2, value2, value2);
      } else {
        group.scale.set(value1 / 2, value1 / 2, value1 / 2);
        // group.scale.set(value1,value1,value1);
      }
      let bbox2 = new THREE.Box3().setFromObject(group)
      // console.log(bbox2,'bbox2');
      let mdlen2 = bbox2.max.x - bbox2.min.x;
      let mdhei2 = bbox2.max.y - bbox2.min.y;
      let mdwid2 = bbox2.max.z - bbox2.min.z;
      group.position.set(-(bbox2.max.x + bbox2.min.x) / 2,
        -(bbox2.max.y + bbox2.min.y) / 2,
        -(bbox2.max.z + bbox2.min.z) / 2);
      document.getElementById("threeView").click(); //去掉加载效果
      scene.add(group);
      // let boxhelper = new THREE.BoxHelper(group,0xbe1915); //辅助线外面红色框
      // scene.add(boxhelper);  
    })
  }
  const animate = () => {
    requestAnimationFrame(animate);
    orbitcontrols.update(); //自动旋转
    renderer.render(scene, camera);
  }
  setTimeout(() => {
    init();
    animate();
    loadModel();
  }, 100)
</script>
<template>
    <!-- vue2 -->
    <div id="threeView" ref="threeView" class="threeView" :prop="opt" :change:prop="three.updatethree"
        style="width:100%; height:500rpx"></div>
</template>

<script>
    export default {
        props: {

        },
        data() {
            return {
                loading:false,
                opt:{}
            }
        },
        methods: {
            start(url,mtl) {
                this.loading = true
                this.opt = {
                    url,
                    mtl,    
                }
            },
        
        }
    }
</script>


<script module="three" lang="renderjs">
    // const THREE = require('static/threeTwo/build/three.min.js')//vue2
    
    import * as THREE from 'three' //vue3
    import {
        OrbitControls
    } from '@/static/threeTwo/examples/jsm/controls/OrbitControls.js'
    import {
        GLTFLoader
    } from '@/static/threeTwo/examples/jsm/loaders/GLTFLoader.js'
    import {
        RGBELoader
    } from '@/static/threeTwo/examples/jsm/loaders/RGBELoader.js';
    import {
        OBJLoader
    } from '@/static/threeTwo/examples/jsm/loaders/OBJLoader.js'
    import {
        MTLLoader
    } from '@/static/threeTwo/examples/jsm/loaders/MTLLoader.js'
    // const ThreeBSP = require('static/three/build/ThreeBSP.js')(THREE)
    var renderer;
    var scene, GLTFloader;
    var camera;
    var controls;
    let pmremGenerator, envMap;
    let renderEnabled = false;
    let timeOut = null;
    export default {
        mounted() {
            // this.initThree();
        },
        methods: {
            updatethree(val) {
                if (val) {
                    this.initThree(val)
                }
            },
            initThree({url,mtl}) {
                let that = this

                /* 创建场景对象Scene */
                scene = new THREE.Scene();
                /*
                    相机设置
                 */
                const element = document.getElementById('threeView')
                // const width = window.innerWidth; // 窗口宽度
                // const height = window.innerHeight; // 高度
                
                const width = element.clientWidth; // 窗口宽度
                const height = element.clientHeight; // 窗口高度
                
                console.log(width, 'width');
                console.log(height, 'height');
                // const width = 100; // 窗口宽度
                // const height = 100; // 窗口高度
                camera = new THREE.PerspectiveCamera(20, width / height, 0.1, 1000);
                camera.position.set(0, 30, 100);
                scene.add(camera);
                
                /*
                    创建渲染器对象
                 */
                
                renderer = new THREE.WebGLRenderer({
                    antialias: true,
                    alpha: true
                });
                console.log(window.innerWidth, window.innerHeight,'window.innerWidth, window.innerHeight')
                renderer.physicallyCorrectLights = true;
                renderer.outputEncoding = THREE.sRGBEncoding;
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(element.clientWidth, element.clientHeight);
                pmremGenerator = new THREE.PMREMGenerator(renderer);
                pmremGenerator.compileEquirectangularShader();
                console.log(renderer.domElement, 'renderer.domElement')
                element.appendChild(renderer.domElement); // body元素中插入canvas对象

                /*
                    光源设置
                 */
                // 半球光
                const hemiLight = new THREE.HemisphereLight();
                hemiLight.intensity = 0.3
                scene.add(hemiLight);
                // 环境光
                const ambientLight = new THREE.AmbientLight(0xffffff, 1);
                scene.add(ambientLight);
                //平行光
                const light = new THREE.DirectionalLight(0xffffff, 0.3);
                light.position.set(-10, -1, 100);
                scene.add(light);
                /*
                    控制器
                 */
                controls = new OrbitControls(camera, renderer.domElement);
                controls.enablePan = false; //滑动
                controls.enableDamping = true;
                controls.dampingFactor = 0.05;
                controls.screenSpacePanning = false;
                controls.autoRotate = true; //场景自动旋转
                controls.autoRotateSpeed = 4; //旋转的速度
                // controls.minDistance = 60;
                // controls.maxDistance = 100;
                // controls.maxPolarAngle = Math.PI /2;//2.8 1.8 Math.PI / 1
                // controls.minPolarAngle = Math.PI /2;//相同的角度禁止垂直旋转
                controls.addEventListener('change', function() {
                    that.timeRender();
                });
                //设置鼠标,触摸的操作方式
                // controls.mouseButtons = {
                //     LEFT: THREE.MOUSE.PAN,
                //     MIDDLE: THREE.MOUSE.DOLLY,
                //     RIGHT: THREE.MOUSE.ROTATE
                // }
                // controls.touches = {
                //     ONE: THREE.TOUCH.PAN,
                //     TWO: THREE.TOUCH.DOLLY_ROTATE
                // }
                /*
                    环境纹理
                 */

                // new RGBELoader()
                // .setDataType( THREE.UnsignedByteType )
                // .load( 'http://qiniu.3fwallet.com/venice_sunset_1k.hdr', ( texture ) => {

                //     envMap = pmremGenerator.fromEquirectangular( texture ).texture;
                //     pmremGenerator.dispose();
                //     scene.environment = envMap;
                //     // scene.background = envMap;
                // }, );

                that.animate();

                const objLoader = new OBJLoader()
                let MTLLoader1 = new MTLLoader()

                // let obj = 'static/3mode/LUZI1.obj'
                // let mtl = 'static/3mode/LUZI1.mtl'
                MTLLoader1.load(mtl, (object) => {
                    object.preload();

                    objLoader.setMaterials(object)
                    objLoader.load(url, (root) => {

                        const model1 = root;
                        console.log(model1, 'model1')
                        model1.position.set(0, 0, 0);

                        // model1.name = 'model1';
                        scene.add(model1);
                        that.timeRender();
                    })
                }, null, (error) => {

                });

            

                renderer.render(scene, camera);

            },
        
            animate() {
                if (renderEnabled) {
                    controls.update();
                    this.render();
                }
                requestAnimationFrame(() => {
                    this.animate()
                });
            },
            // 动画
            render() {
                renderer.render(scene, camera); //执行渲染操作
            },
            /*
                渲染判断
                在没有动画的情况下,可以在有需要的时候渲染,提高性能
             */
            timeRender() {
                //设置为可渲染状态
                renderEnabled = true;
                //清除上次的延迟器
                if (timeOut) {
                    clearTimeout(timeOut);
                }
                //开启动画时需要一直渲染,不能暂停渲染操作
                // timeOut = setTimeout(function () {
                //     renderEnabled = false;
                // }, 3000);
            },
    
    }
}
</script>
<style>
    .content {
        margin: 0;
        overflow: hidden;
    }

    .three {
        width: 100%;
    }

    #threeView {
        width: 100%;

    }
</style>


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值