• 【重要threeJS 渲染性能上的优化方案】 加载读取渲染压缩包中的模型


    需要准备的插件和包(threeJS等包省略,这个三是实现标题功能核心的包):

    1. jszip  Docs:JSZip (stuk.github.io)
    2. jszip-utils
    3. FileSaver

            package1&2 是 从 压缩包中读取 模型的关键!!!, package1&3是模型打包压缩必须的!

     【代码有很多地方可以自行精简】

    首先实现上传模型,自动打包成zip文件并自动下载zip包

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jszip/3.7.1/jszip.min.js"
        type="application/javascript"></script>
        <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/FileSaver.js/2014-08-29/FileSaver.min.js" type="application/javascript"></script>  <title>Document</title>
    </head>
    
    <body>
      
        <input type="file" name="file" id="fileID">
        <button type="submit" onclick="toZip()">sure</button>
      
    </body>
    <script>
      function toZip() {
        var file = document.getElementById("fileID");
      // 文件上传后 点击submit, 获取到上传文件
    var zip = new JSZip(); zip.file(file.files[0].name, file.files[0]); //console.log(file.files[0]); zip.generateAsync({ // 这里可以看jszip官方文档 type: "blob", compression: "DEFLATE", compressionOptions: { level: 9 } }).then(function (content) { // console.log(2); saveAs(content, file.files[0].name.split('.')[0]+'.zip');
        //到这里 就可以上传模型,自动会压缩并打包。 });
    // if (false) { // var content = "这里可以拿到接口返回的压缩包二进制数据,还原后解压"; // zip.loadAsync(content).then(function (zip) { // new_zip.file("getContent.txt").async("string"); // }); // } } </script> </html>

    再实现结合vue3+threeJS+jszip 读取渲染压缩包里模型功能[threeJS 基本操作,例如打灯光,设置相机等操作就不注释了。网上很多例子。]

    <script setup>
    import JSZip from "jszip";
    import * as THREE from "three";
    import { OrbitControls } from "@/js/Controls";
    import { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter";
    import { STLLoader } from "three/examples/jsm/loaders/STLLoader.js";
    import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
    import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
    import {
      CSS2DRenderer,
      CSS2DObject,
    } from "three/examples/jsm/renderers/CSS2DRenderer.js";
    import { ref, onMounted, getCurrentInstance, nextTick, onUpdated } from "vue";
    
    const { proxy } = getCurrentInstance();
    // let scene, mesh;base
    let scene, raycaster, mouse, sphere103, lineGeometry, line, base;
    raycaster = new THREE.Raycaster();
    mouse = new THREE.Vector2();
    const num = ref(0);
    const isShow = ref(0);
    const info = ref("model loading...");
    const camera = ref(null);
    const renderer = ref(null);
    const labelRenderer = ref(null);
    const controls = ref(null);
    const path = "/model/408.fbx";
    
    const createScene = () => {
      scene = new THREE.Scene();
      scene.position.y = 0;
    };
    const createLight = () => {
      scene.add(new THREE.AmbientLight(0x444444));
    
      const light = new THREE.PointLight(0xffffff);
    
      light.position.set(0, 50, 50);
    
      //告诉平行光需要开启阴影投射
    
      light.castShadow = true;
    
      scene.add(light);
    };
    const createCamera = () => {
      camera.value = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      camera.value.position.set(100, 35, 0); // 设置相机位置
    
      camera.value.lookAt(new THREE.Vector3(0, 0, 0)); // 设置相机方向
      scene.add(camera.value);
    };
    const createRender = async () => {
      const element = document.getElementById("container");
      renderer.value = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      renderer.value.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
      renderer.value.shadowMap.enabled = true; // 显示阴影
      renderer.value.shadowMap.type = THREE.PCFSoftShadowMap;
      renderer.value.setClearColor(0x3f3f3f, 1); // 设置背景颜色
      element.appendChild(renderer.value.domElement); // 这里是canvas
    
      labelRenderer.value = new CSS2DRenderer();
      labelRenderer.value.setSize(element.clientWidth, element.clientHeight);
      labelRenderer.value.domElement.style.position = "absolute";
      labelRenderer.value.domElement.style.top = "0px";
      // console.dir(labelRenderer.value.domElement);
      element.appendChild(labelRenderer.value.domElement); // 这里domElement是 divlabel的父级
    };
    const createControls = () => {
      controls.value = new OrbitControls(
        camera.value,
        labelRenderer.value.domElement
      );
      controls.value.minDistance = 5;
      controls.value.maxDistance = 100;
    };
    
    function setScaleToFitSize(obj) { // 这里是将模型适配到一个合适的比例
      const boxHelper = new THREE.BoxHelper(obj);
      boxHelper.geometry.computeBoundingBox();
      const box = boxHelper.geometry.boundingBox;
      const maxDiameter = Math.max(
        box.max.x - box.min.x,
        box.max.y - box.min.y,
        box.max.z - box.min.z
      );
      const scaleValue = camera.value.position.x / maxDiameter;
      obj.scale.set(scaleValue, scaleValue, scaleValue);
    }
    
    const loadSTL = (scale = 0.02) => {
      const new_zip = new JSZip(); // 实例化jszip
      const loader = new GLTFLoader();
    // 这里的 工具类是配合jszip使用的,工具类读取zip数据,得到二进制数据流,存到 blob中,之后,threeJS 加载器loader去加载这个二进制数据就好了 JSZipUtils.getBinaryContent(
    "/model/825B.zip", function (err, data) { if (err) { throw err; // or handle err } new_zip.loadAsync(data).then(function (res) { let fileList = res.files; for (let key in fileList) { // 读取模型文件内容 new_zip .file(key) .async("arraybuffer") .then((content) => { // Blob构造文件地址,通过url加载模型 let blob = new Blob([content]); let modelUrl = URL.createObjectURL(blob); console.log(modelUrl); loader.load(modelUrl, (gltf) => { gltf.scene.traverse(function (child) { if (child.isMesh) { //模型自发光 child.material.emissive = child.material.color; child.material.emissiveMap = child.material.map; } }); setScaleToFitSize(gltf.scene); scene.add(gltf.scene); }); }); } }); }); }; const onProgress = (xhr) => { // console.log("加载完成的百分比" + (xhr.loaded / xhr.total) * 100 + "%"); num.value = Math.floor((xhr.loaded / xhr.total) * 100); }; const render = () => { requestAnimationFrame(render); renderer.value.render(scene, camera.value); labelRenderer.value.render(scene, camera.value); controls.value.update(); // console.log(scene) }; const loadLabel = async (base) => { // 自定义 标签的加载 const { data } = await proxy.$axios.post("/getPoints", { belongTo: path, }); var radius = 0.4, segemnt = 16, rings = 16; var sphereMaterial = new THREE.MeshLambertMaterial({ color: "#ECF0F1" }); var material = new THREE.LineBasicMaterial({ color: 0x26a7f2 }); data.forEach((v, index) => { sphere103 = new THREE.Mesh( new THREE.SphereGeometry(radius, segemnt, rings), sphereMaterial ); sphere103.name = "mesPoint"; lineGeometry = new THREE.BufferGeometry(); //three.js 125 版本以上就废弃掉 目前支持的是 bufferGeometry const div = document.createElement("div"); div.className = "tag"; div.innerHTML = `${v.id}`; div.addEventListener("click", (e) => { console.log(e.target); }); const divLabel = new CSS2DObject(div); divLabel.addEventListener("click", (e) => { consolelog(e.target); }); // divLabel.visible = false; divLabel.position.set(v.positionX * 2, v.positionY * 2, v.positionZ * 2); const begin = new THREE.Vector3(v.positionX, v.positionY, v.positionZ); const end = new THREE.Vector3( v.positionX * 2, v.positionY * 2, v.positionZ * 2 ); lineGeometry.setFromPoints([begin, end]); line = new THREE.Line(lineGeometry, material, THREE.LineSegments); sphere103.position.set(v.positionX, v.positionY, v.positionZ); line.add(divLabel); scene.add(sphere103); scene.add(line); }); }; const init = async () => { createScene(); // 创建场景 createLight(); // 创建光源 createCamera(); // 创建相机 createRender(); // 创建渲染器 createControls(); // 创建控件对象 render(); // 渲染 loadSTL(); // 加载模型 }; function raycastMeshes(callback, raycaster) { let intersects = []; let meshes = []; // 获取整个场景 let theScene = scene || new THREE.Scene(); console.log(theScene); // 获取鼠标点击点的射线 let theRaycaster = raycaster || new THREE.Raycaster(); // 对场景及其子节点遍历 for (let i in theScene.children) { // 如果场景的子节点是Group或者Scene对象 if ( theScene.children[i] instanceof THREE.Group || theScene.children[i] instanceof THREE.Scene ) { // 场景子节点及其后代,被射线穿过的模型的数组集合 // intersects = theRaycaster.intersectObjects(theScene.children[i].children, true) let rayArr = theRaycaster.intersectObjects( theScene.children[i].children, true ); intersects.push(...rayArr); console.log(intersects); } else if (theScene.children[i] instanceof THREE.Mesh) { let rayArr = theRaycaster.intersectObjects( theScene.children[i].children, true ); meshes.push(rayArr); console.log(meshes); // 如果场景的子节点是Mesh网格对象,场景子节点被射线穿过的模型的数组集合 // intersects.push(theRaycaster.intersectObject(theScene.children[i])) } } intersects = filtersVisibleFalse(intersects); // 过滤掉不可见的 // 被射线穿过的模型的数组集合 if (intersects && intersects.length > 0) { return callback(intersects); } else { // this.hiddenDetailDiv() return null; } } function filtersVisibleFalse(arr) { let arrList = arr; if (arr && arr.length > 0) { arrList = []; for (let i = 0; i < arr.length; i++) { if (arr[i].object.visible) { arrList.push(arr[i]); } } } return arrList; } function clickApp(intersects) { if (intersects[0].object !== undefined) { // console.log(intersects[0].object, '这就是成功点击到的对象了~') console.log(intersects); } } onMounted(() => { init(); }); </script> <template> <div> <div class="progress" v-show="isShow"> <el-progress :text-inside="true" :stroke-width="24" :percentage="num" status="success" /> <div class="progressInfo">{{ info }}</div> </div> <div class="main" id="container"></div> </div> </template>

    最终效果:

  • 相关阅读:
    javascript数据结构
    uni-app — 一套前端开发跨平台应用的终极解决方案
    从函数式编程到Ramda函数库(二)
    从函数式编程到Ramda函数库(一)
    node.js爬取数据并定时发送HTML邮件
    vue cli3.0 结合echarts3.0和地图的使用方法
    vue加载优化策略
    C#时间格式化
    wpf 调用线程无法访问此对象,因为另一个线程拥有该对象。
    使用oracle数据库开发,异常总结
  • 原文地址:https://www.cnblogs.com/Hijacku/p/16243254.html
Copyright © 2020-2023  润新知