uniapp通过renderjs加载3D模型,支持FBX、GLB和GLTF模型,模型可自动适应。

模型组件

组件模板

  1. n-text-loading是我的自定义loading组件,可以自行替换
  2. id是threeView是模型显示的位置,
  3. props里面的url是模型链接,cameraZ是相机位置,默认100,一般不需要改,有些z轴很长的模型旋转的时候会有一部分相机看不到这个时候就需要调整这个值了,这两个要从后台上传。
  4. :prop=“url”,url就是你传给renderjs的值,不传的话renderjs中拿不到,
  5. :change:prop=“thress.updataModelUrl”,这个表示prop改变了会执行模块thress中的updataModelUrl方法,这个方法接收四个参数newValue, oldValue, ownerInstance, instance
<template>
    <view class="collection-bg">
		<n-text-loading v-if="loading"></n-text-loading>
        <view id="threeView" 
            @click="thress.onClick" 
            :prop="url" 
            :change:prop="thress.updataModelUrl"
            :propz="cameraZ" 
            :change:propz="thress.updataModelZ"
            class="media-wrap" 
            ></view>
    </view>
</template>
<script>
	export default{
		props:{
			url: String,
            cameraZ: Number
		},
		data(){
			return{
				loading: true,
			}
		},
        methods:{
            completed(option){
                this.loading = option.loading || false;
            }
        }
	}
</script>

renderjs

  1. script里面的module="thress"和上面template里面的thress是对应的,别写错了
  2. three自己npm下载就可以了
  3. template里面是用不了renderjs中的data的值的,所以控制加载中的属性loading我是放在上面的,加载完成之后调用id为threeView的点击事件,然后会执行renderjs中onClick方法,onClick会收到两个参数event, ownerInstance,再使用ownerInstance.callMethod()方法,可以传两个参数,第一个是上面方法名,第二个是你要传递的参数,比如这样: ownerInstance.callMethod(‘completed’, { loading: false });
  4. 好像也没啥了,其他直接复制就OK了。
<script module="thress" lang="renderjs">
	// import THREE from 'three/build/three.min.js'
	const THREE = require('three')
    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
    import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
    import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
	export default {
    name:"ModelScale",
    data(){
        return{
            scene:null,
            camera:null,
            renderer:null,  
            cube:null,
            sphere:null,
            step:0,
            stats:null, 
            group:null,
			orbitcontrols: null,
        }
    },
    props:{
        width:{type:Number,default: 375},
        height:{type:Number,default: 380},
    },
    methods:{
        updataModelUrl(newValue, oldValue, ownerInstance, instance) {
            // 监听 service 层数据变更
            console.log(newValue,'层数据变更');
        },
        updataModelZ(newValue, oldValue, ownerInstance, instance){
           console.log(newValue,'层数据变更2');
        },
        onClick(event, ownerInstance){
            ownerInstance.callMethod('completed', {
                loading: false
            })
        },
        init(){
            const cameraZ = this.cameraZ <= 0 ? 100 : this.cameraZ;
            console.log(cameraZ,'cameraZ');
            this.scene = new THREE.Scene();
            this.camera = new THREE.PerspectiveCamera(45,this.width/this.height,0.1,1000);
            this.camera.position.set(0,0,cameraZ);
            this.renderer = new THREE.WebGLRenderer({antialias: true });           
			this.renderer.setClearColor(0xffffff,0);
            this.renderer.setSize(this.width/1.2,this.height/1.2);
            this.renderer.setPixelRatio(window.devicePixelRatio);
            this.renderer.shadowMapEnabled = true; 
            document.getElementById("threeView").appendChild(this.renderer.domElement);

            this.orbitcontrols = new OrbitControls(this.camera, this.renderer.domElement);  //移动控件
			this.orbitcontrols.enabled = true;
			this.orbitcontrols.enableRotate =true;
			this.orbitcontrols.enableZoom = false;
			this.orbitcontrols.autoRotate = true;
            this.orbitcontrols.minPolarAngle = Math.PI / 4;  
            this.orbitcontrols.maxPolarAngle = 3 - (Math.PI / 4);  

            // let axes = new THREE.AxesHelper(100);//辅助线
            // this.scene.add(axes);

			if(this.url.endsWith('gltf') || this.url.endsWith('glb')){
				//设置了六个平行光  有些材质不接受环境光会很暗
				const directionLight1 = new THREE.DirectionalLight(0xffffff, 1);
				directionLight1.position.set(-300,0,0)
				this.scene.add(directionLight1);
		
				const directionLight2 = new THREE.DirectionalLight(0xffffff, 1);
				directionLight2.position.set(300,0,0)
				this.scene.add(directionLight2);
				
				const directionLight3 = new THREE.DirectionalLight(0xffffff, 1);
				directionLight3.position.set(0,300,0)
				this.scene.add(directionLight3);
				
				const directionLight4 = new THREE.DirectionalLight(0xffffff, 1);
				directionLight4.position.set(0,300,0)
				this.scene.add(directionLight4);
		
				const directionLight5 = new THREE.DirectionalLight(0xffffff, 1);
				directionLight5.position.set(0,0,-300)
				this.scene.add(directionLight5);
		
				const directionLight6 = new THREE.DirectionalLight(0xffffff, 1);
				directionLight6.position.set(0,0,300)
				this.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);
			this.scene.add(Ambient);
			this.scene.add(Sun);
        },
        loadModel(){
            let self = this; //这一点很重要。。
            let loader1 = new GLTFLoader();
			let FBXloader = new FBXLoader();
           	let rotateObj = [];
			const loadLoader = this.url.endsWith('fbx') ? FBXloader : loader1;
            loadLoader.load(this.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(self.camera,'相机的信息',group,'组的信息');
                let dist =Math.abs(self.camera.position.z - group.position.z- (mdwid/2));
                //console.log('dist值为:' + dist );
                let vFov = self.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 = self.height * fraction ;                
                //console.log('finalHeight值为:' + finalHeight);
                let finalWidth = (finalHeight*mdlen) /mdhei;             
                //console.log('finalWidth值为:' + finalWidth );                

                let value1 = self.width/finalWidth;
                // console.log('value1缩放比例值为:' + value1);
                let value2 = self.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(); //去掉加载效果
                self.scene.add(group);
                // let boxhelper = new THREE.BoxHelper(group,0xbe1915); //辅助线外面红色框
                // self.scene.add(boxhelper);  
            });                       
        },
        animate(){
            requestAnimationFrame(this.animate);
			this.orbitcontrols.update();  //自动旋转
            this.renderer.render(this.scene,this.camera);  
        }
    },
    mounted(){
        window.ob = this;
        this.init();
        this.animate(); 
        this.loadModel();      
    }
}
</script>
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值