three3d切换显示轮播模型

three3d切换显示轮播模型

这里提供两个工具一个是fastStone Capture 录视频的 https://ezgif.com/video-to-gif将录制的wmv格式转换gif格式

实现3d切换显示不同模型
这里单单是vue的写法 自己后期可以封装

//首先引入这些文件,当然你得先下载three, npm i three -S
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
const dataList = [
  {
    path: "/3d/小苗-水稻.fbx",
    position: { y: -15 },
    offset: -100,
  },
  {
    path: "/3d/水稻-拔节.fbx",
    position: { y: -25 },//调解上下
    offset: 60,//调解大小近大远小
  },...这里我就不写过多的为了方便观看  首先path 是模型路径放在public/3d下面,这里用的是fbx模型
];
const zoom = 100;//层级
var timer = null;//timerid
var d3fbxRef = null;//dom元素
var my_three = {
  scene: null,
  camera: null,
  renderer: null,
  controls: null,
  file: null
};//three的属性,场景相机渲染器等等
var modelList = [];//缓存数据
var list = [];//数据
var oldIndex = null;//切换前的索引
//执行函数初始化
mounted() {
  this.init({ models: dataList, targetDom: this.$refs.d3Ref })
},




//下面是封装的
  methods: {
  //初始化传入2个参数,数据必须是数组,dom
    init({ models, targetDom }) {
      d3fbxRef = targetDom;//dom元素
      list = [...models];//数据
      let _this = this;
      (async function init() {
        await _this.initScene();//初始化场景
        await _this.initCamera();//初始化相机
        await _this.initModel();//初始化模型
        await _this.initRenderer()//初始化渲染器
        await _this.initControls();//初始化控制器
      })();
    },
    initScene() {
      return new Promise((resolve, reject) => {
        my_three.scene = new THREE.Scene();//创建3d场景
        resolve();
      });
    },
    initCamera(index = 0) {
    //添加各种光照
      const { scene, camera } = my_three;
      // 环境光
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
      // 平行光
      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
      // 半球光
      const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x00ff00);
      //聚光灯
      const spotLight = new THREE.SpotLight(0xffffff, 1);
      spotLight.position.set(1000, 1010, 1000);
      // 点光源
      const pointLight = new THREE.PointLight(0xffffff, 0.5, 10, 2);
      //场景的内容
      if (scene.children.length === 0) {
        scene.add(spotLight);
        scene.add(hemisphereLight);
        scene.add(directionalLight);
        scene.add(ambientLight);
      }

      const { offset = 0 } = list[index];
      return new Promise((resolve, reject) => {
        // THREE.PerspectiveCamera  透视摄像机
        // fov — 摄像机视锥体垂直视野角度
        // aspect — 摄像机视锥体长宽比
        // near — 摄像机视锥体近端面
        // far — 摄像机视锥体远端面
        //创建相机
        if (camera) {
          my_three.camera.position.set(1000, 1000, 1050 + offset);
        } else {
          my_three.camera = new THREE.PerspectiveCamera(45, 1, 1, 2000);
          my_three.camera.position.set(1000, 1000, 1050 + offset);
          scene.add(my_three.camera);
        }
        resolve();
      });
    },
    //初始化模型
    initModel(index = 0) {
      const { scene } = my_three;
      const { path, position } = list[index] || {};
      //如果是gltf模型
      if (path.includes(".gltf")) {
        const loader = new GLTFLoader();
        return new Promise((resolve, reject) => {
          let oldFile = modelList[index];
          oldIndex = index;
          if (oldFile) {
            my_three.file = oldFile;
            scene.add(oldFile);
            resolve();
          } else {
            loader.load(path, ({ scene: objectScene }) => {

              const { y } = position || { y: 0 };
              objectScene.name = path;
              objectScene.position.set(1000, 1000 + y, 1000);
              objectScene.traverse((child) => {
                // 模型转到一定角度消失 DoubleSide 解决
                if (child instanceof THREE.Mesh) {
                  child.material.side = THREE.DoubleSide;
                }
              });

              modelList[index] = objectScene;
              my_three.file = objectScene;
              scene.add(objectScene);
              resolve();
            });
          }
        });
      } else {
      	//fbx模型
        const loader = new FBXLoader();
        return new Promise((resolve, reject) => {
        	//这里将显示过得文件缓存起来了,防止2次渲染卡
          let oldFile = modelList[index];
          oldIndex = index;
          if (oldFile) {
            my_three.file = oldFile;
            scene.add(oldFile);
            resolve();
          } else {
            loader.load(path, (object) => {
              const { y } = position || { y: 0 };
              object.name = path;
              object.position.set(1000, 1000 + y, 1000);
              object.traverse((child) => {
                // 模型转到一定角度消失 DoubleSide 解决
                if (child instanceof THREE.Mesh) {
                  child.material.side = THREE.DoubleSide;//显示2个面默认背面不显示
                  // child.material.side = 2;
                }
              });
              modelList[index] = object;
              my_three.file = object;
              scene.add(object);
              resolve();
            });
          }
        });
      }
    },
    initRenderer() {
    //下面都是些渲染配置项
      const { scene, camera, file } = my_three;
      return new Promise((resolve, reject) => {
        my_three.renderer = new THREE.WebGLRenderer({
          alpha: true, // 画布是否包含alpha(透明度)缓冲区。默认值为false。
          antialias: true, // 是否执行抗锯齿。默认值为false。
        });
        // 定义渲染器的输出编码。默认为THREE.LinearEncoding
        my_three.renderer.outputEncoding = THREE.sRGBEncoding;
        // 设置设备像素比。通常用于避免HiDPI设备上绘图模糊
        my_three.renderer.setPixelRatio(window.devicePixelRatio);
        // 设置alpha(背景透明度)。合法参数是一个0.0到 1.0之间的浮点数
        my_three.renderer.setClearAlpha(0);
        // 将输出canvas的大小调整为(width, height)并考虑设备像素比,且将视口从(0, 0)开始调整到适合大小
        my_three.renderer.setSize(d3fbxRef.clientWidth, d3fbxRef.clientHeight);
        // my_three.renderer.domElement: canvas,渲染器在其上绘制输出。
        d3fbxRef.appendChild(my_three.renderer.domElement);
        // 开始渲染
        my_three.renderer.render(scene, camera);
        resolve();
      });
    },
    initControls() {
      const { scene, camera, renderer, file } = my_three;

      return new Promise((resolve, reject) => {
        my_three.controls = new OrbitControls(camera, renderer.domElement);
        my_three.controls.target.set(1000, 1000, 1000);
        my_three.controls.maxZoom = 2;
        my_three.controls.minZoom = 0.5;
        // my_three.controls.enableZoom = false;
        // console.log(my_three.controls);
        // 开启自动旋转
        my_three.controls.autoRotate = true;
        my_three.controls.enableDamping = true//阻尼效果
        // 旋转速度,默认为20
        my_three.controls.autoRotateSpeed = 10;
        // 更新控制器
        my_three.controls.update();

        // my_three.controls.addEventListener('change', changeControls)
        // function changeControls(event) {
        //     console.log(camera.position)
        // }
        const render = function () {
          cancelAnimationFrame(timer);
          my_three.controls.enableZoom = Math.abs(camera.position.z - file.position.z) < zoom;
          my_three.renderer.render(scene, camera);
          my_three.controls.update();
          // 请求动画帧。
          // 与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。
          // 具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,
          // 换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。
          // 它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
          timer = requestAnimationFrame(render);
        };
        render();
        resolve();
      });
    },
    clearModel(index) {
      const { scene, renderer } = my_three;
      return new Promise((resolve, reject) => {
        // 通过模型名称找到对应的模型变量
        let obj = scene.getObjectByName(list[index]["path"]);
        // 删除场景中的模型
        obj && scene.remove(obj);
        resolve();
      });
    },
    //切换模型 参数索引
    async changeModel(index) {
      await this.clearModel(oldIndex);
      await this.initCamera(index);
      await this.initModel(index);
      await this.initControls();
    },
    //模型清除
    clearAll() {
      const { scene, renderer, controls } = my_three;
      cancelAnimationFrame(timer);
      renderer.forceContextLoss();
      renderer.dispose();
      controls.dispose();
      scene.clear();

      my_three = {
        scene: null,
        camera: null,
        renderer: null,
        controls: null,
        file: null,
      };
    }
  }

使用 首先引入该方法,让后 创建静态数据,init初始化就可以 ,changeModal切换
three3d切换显示轮播模型_第1张图片

你可能感兴趣的:(three.js,3d,数码相机)