Vue3 + Three.js + gltf-pipeline大型园区场景渲染与3D业务

在非使用unity作为3D渲染方案的前提下,对与目前web开发者比较友好的除了canvas场景需要的2D babylon.jsfabric.js, Three.js是目前针对于jsWeb用户最直接且比较友好的3D引擎方案了。
准备工作:
1.明确需要用的场景方案都有那些,模型需要的加载器是什么
2.模型的场景大小已经相关的交互业务
3.场景的工作环境(浏览器及硬件要求)
step1:
以.glb模型为例

import * as THREE from "three";
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";

以上就是一个场景绘制需要的基本3个要素
模型压缩
由于建模工程师因为场景规模的原因模型在建立时过多了使用了面,导致整个模型的体积很大,一个校区或者大园区为例,建筑加环境要素及周边地形都整体的模型体积已经达到了100M+,这个时候就需要我们在开发前就考虑模型的压缩问题了
DRACO压缩算法

npm install -g gltf-pipeline
--input, -i                   Path to the glTF or glb file.[string] [required]
--output, -o                  Output path of the glTF or glb file. Separate   resources will be saved to the same directory.  [string]
--binary, -b                  Convert the input glTF to glb.                                      //将输入的glTF转换为glb[boolean] [default: false]
--json, -j                    Convert the input glb to glTF.                                      //将输入的glb转换为glTF[boolean] [default: false]
--separate, -s                Write separate buffers, shaders, and textures                       //编写单独的缓冲区、着色器和纹理而不是把它们嵌入到glTF中instead of embedding them in the glTF.          [boolean] [default: false]
--separateTextures, -t        Write out separate textures only.                                   //只写出单独的纹理[boolean] [default: false]
--stats                       Print statistics to console for output glTF                         //将统计信息打印到控制台以输出glTF文件file.                 [boolean] [default: false]     
--keepUnusedElements          Keep unused materials, nodes and meshes.                            //保留未使用的材质、节点和网格[boolean] [default: false]
--draco.compressMeshes, -d    Compress the meshes using Draco. Adds the                            //使用Draco压缩网格。添加KHR_draco_mesh_压缩扩展KHR_draco_mesh_compression extension.[boolean] [default: false]
--draco.compressionLevel      Draco compression level [0-10], most is 10,                           //Draco压缩级别[0-10],大多数是10,最小值为0。值为0将会连续应用 编码并保留face顺序。least is 0. A value of 0 will apply sequentialencoding and preserve face order.[number] [default: 7]   
--draco.quantizePositionBits  Quantization bits for position attribute when                        //位置坐标属性的量化位使用Draco压缩。using Draco compression.  [number] [default: 11]--draco.quantizeNormalBits    Quantization bits for normal attribute when                           //法线属性的量化位使用Draco压缩using Draco compression.   [number] [default: 8]--draco.quantizeTexcoordBits  Quantization bits for texture coordinate                               //纹理坐标的量化位属性。attribute when using Draco compression.[number] [default: 10]--draco.quantizeColorBits     Quantization bits for color attribute when using                        //使用时颜色属性的量化位德拉科压缩Draco compression.         [number] [default: 8]--draco.quantizeGenericBits   Quantization bits for skinning attribute (joint                        //蒙皮属性(关节的量化位索引和关节权重)ad自定义属性使用Draco压缩时。indices and joint weights) ad custom attributeswhen using Draco compression. [number] [default: 8]--draco.uncompressedFallback  Adds uncompressed fallback versions of the                            //添加未压缩的回退版本压缩网格compressed meshes.    [boolean] [default: false]--draco.unifiedQuantization   Quantize positions of all primitives using the            //统一定义的量化网格所有基本体的边界框。 如果这个选项未设置,对每个应用量化原始的可能会导致差距出现在不同图元之间。same quantization grid defined by the unifiedbounding box of all primitives. If this optionis not set, quantization is applied on eachprimitive separately which can result in gapsappearing between different primitives.[boolean] [default: false]

在这里插入图片描述
gltf-pipeline的参数有很多这里我们只需要提炼出一个满足我们需要的就够了

gltf-pipeline -i .\public\tep\23.glb -o .\public\tep\23-main.glb  -d --draco.compressionLevel 9 --draco.quantizePositionBits 10 --draco.quantizeColorBits 10
-i .\public\cascl\caa4.glb   //输入路径-o .\public\cascl\caa4-main.glb  //输出路径及名称-d --draco.compressionLevel 10 //压缩等级--draco.quantizePositionBits 20  //量化  0 标识无损压缩 

需要注意的是安装好之后是不可以直接运行的我们需要一个three.js为我们提供的基本依赖draco_decoder.js 这个文件一般就放在node_module/three/examples/js/libs/draco目录下cpoy出来与模型文件一起放在public文件夹下即可

完成上述这些准备工作之后我们开始渲染我们的第一个大园区场景,因为我们使用了压缩算法所以我们需要额外再引入一个解压加载器,并将我们copy出来的draco_decoder.js文件与我们压缩好的模型都放在public下

import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader"

step2
初始一个加载模型的方法

export const initMod=(id,filePath,fun)=>{container=document.getElementById(id);scene = new THREE.Scene();camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);renderer = new THREE.WebGLRenderer({ antialias: true,alpha: true });renderer.setSize(container.clientWidth, container.clientHeight);container.appendChild(renderer.domElement);renderer.setClearColor('#6fc0ec', 1.0);renderer.outputEncoding = THREE.sRGBEncoding;const loader = new GLTFLoader();let dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath("/cascl/"); // 设置public下的解码路径,注意最后面的/dracoLoader.setDecoderConfig({ type: "js" });dracoLoader.preload();loader.setDRACOLoader(dracoLoader);loader.load(filePath,gltf => {// 将模型放到中间const box = new THREE.Box3().setFromObject(gltf.scene);const size = box.getSize(new THREE.Vector3()).length();const center = box.getCenter(new THREE.Vector3());gltf.scene.position.x -= center.x;gltf.scene.position.y -= center.y;gltf.scene.position.z -= center.z;camera.near = size / 100;camera.far = size * 100;camera.updateProjectionMatrix();camera.position.copy(center);camera.position.x += size / 2;camera.position.y += size / 2;camera.position.z += size / 2;camera.lookAt(center);scene.add(gltf.scene);console.log('---加载的模型',gltf.scene)const ambient = new THREE.AmbientLight(0xffffff, 0.4);scene.add(ambient);//添加在模型的右上角高三倍设置一个光源 太阳const light = new THREE.DirectionalLight(0xffffff, 1);// 模型宽度const width = box.max.x - box.min.x;// 模型高度const height = box.max.y - box.min.y;// 模型深度const depth = box.max.z - box.min.z;light.position.set(width * 3, height * 3, depth * 3);scene.add(light);
// 点光源let point = new THREE.PointLight('#74beee',1);point.position.set(-width * 3, -height * 3, depth * 3); // 点光源位置scene.add(point); // 点光源添加到场景中//多设置几个光源const light3 = new THREE.DirectionalLight('#8dccee', 1);light3.position.set(-width * 3, -height * 3, depth * 3);scene.add(light3);const light4 = new THREE.HemisphereLight('#8dccee', 0.3);scene.add(light4);//包含关键帧动画的模型作为参数创建一个播放器mixer = new THREE.AnimationMixer(gltf.scene);//  获取gltf.animations[0]的第一个clip动画对象clipAction = mixer.clipAction(gltf.animations[0]); //创建动画clipAction对象clipAction.play(); //播放动画
//不循环播放clipAction.loop = THREE.LoopOnce;
// 物体状态停留在动画结束的时候clipAction.clampWhenFinished = true// 如果想播放动画,需要周期性执行`mixer.update()`更新AnimationMixer时间数据clock = new THREE.Clock();},undefined,error => {console.error(error);});camera.position.z = 5;// 添加OrbitControls控制器controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.05;controls.screenSpacePanning = false;controls.minDistance = 1;controls.maxDistance = 1000;const tag = labelTag({x: -580,y:50,z: -705});tagList.push(tag)scene.add(tag);//添加到指定的场景里renderLabel()animate();runLoop()renderer.domElement.addEventListener('click', handleModClick, false)
}

上方的初始方案中包含了一个基本的动画加载器由此我们可以完成一个基本的模型加载的场景创建
在这里插入图片描述
这是一个可以执行楼层分层爆炸的模型内置的动画由:

   //包含关键帧动画的模型作为参数创建一个播放器mixer = new THREE.AnimationMixer(gltf.scene);

完成捕捉及后续播放
step3
接下来我们完成园区的业务需求建设
1.场景需要有天空背景
2.场景需要有关键建筑的标注
3.场景的交互具有高亮环绕
4.场景具有漫游功能
基于以上我们开始增加需要的工具

  1. 场景漫游动画处理库
import TWEEN from '@tweenjs/tween.js';

2.场景天空环境即天空盒

    const urls = ['../sky/Above Day B_Cam_3_Right-X.png',//x正方形'../sky/Above Day B_Cam_2_Left+X.png',//x负方向'../sky/Above Day B_Cam_4_Up+Y.png',//y正方形'../sky/Above Day B_Cam_5_Down-Y.png',//y负方向'../sky/Above Day B_Cam_0_Front+Z.png',//z正方形'../sky/Above Day B_Cam_1_Back-Z.png'//z负方形]const textureCube = new THREE.CubeTextureLoader().load(urls)scene.background = textureCube

3.场景后处理器

import {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer';
import {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass';
import {OutlinePass} from 'three/examples/jsm/postprocessing/OutlinePass';

在使用了后处理器后因为模型抗拒齿原因我们需要在额外补充一个

import {SMAAPass} from 'three/examples/jsm/postprocessing/SMAAPass';//抗锯齿后处理const smaaPass = new SMAAPass(container.clientWidth * pixelRatio, container.clientHeight * pixelRatio);

4.一个漫游动画控制的方法

export const createCameraTween = (pos2, pos) => {tween = new TWEEN.Tween({// 相机开始坐标x: camera.position.x,y: camera.position.y,z: camera.position.z,// 相机开始指向的目标观察点tx:  current.x,ty:   current.y,tz:   current.z,}).to({// 相机结束坐标x: pos.x,y: pos.y,z: pos.z,// 相机结束指向的目标观察点tx: pos.x,ty: pos.y,tz: pos.z,}, 2000).onUpdate(function (obj) {// 动态改变相机位置camera.position.set(obj.x, obj.y, obj.z);// 动态计算相机视线camera.lookAt(pos.x, pos.y, -pos.z);}).start();animates();
}
const animates = (time) => {TWEEN.update(time);requestAnimationFrame(animates);
}

在使用glb/gltf模型中我们也常常会需要处理模型加载发暗,材质渲染失真的情况这里我们也一并加入到初始化的方案内

import {GammaCorrectionShader} from'three/examples/jsm/shaders/GammaCorrectionShader';
import {ShaderPass} from 'three/examples/jsm/postprocessing/ShaderPass';
import {RoomEnvironment} from 'three/examples/jsm/environments/RoomEnvironment';

由此我们以及基本完成了所有的加载需要的必备条件即要求,我们渲染一个大园区场景
在这里插入图片描述
在这里插入图片描述
场景后处理的效果业务由:

const renderOutline = (mod) => {if (buildIds.includes(mod.name)) {// 创建后处理对象EffectComposer,WebGL渲染器作为参数composer = new EffectComposer(renderer);renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);
// 创建OutlinePass通道container = document.getElementById('mod') ? document.getElementById('mod') : document.getElementById('mod2');const v2 = new THREE.Vector2(container.clientWidth, container.clientHeight);const outlinePass = new OutlinePass(v2, scene, camera);// 创建伽马校正通道const gammaPass = new ShaderPass(GammaCorrectionShader);composer.addPass(gammaPass);const pixelRatio = renderer.getPixelRatio()//抗锯齿后处理const smaaPass = new SMAAPass(container.clientWidth * pixelRatio, container.clientHeight * pixelRatio);composer.addPass(smaaPass);outlinePass.selectedObjects = [mod];outlinePass.visibleEdgeColor.set('#a838ef');outlinePass.edgeThickness = 4;outlinePass.edgeStrength = 15;outlinePass.pulsePeriod = 3;composer.addPass(outlinePass);animateOutline()bus.$emit('showMod', mod)} else {clearOutline();}
}

由于业务延展很多不再过的的赘述,解决方案包含了动态标签切换,标记交互,灯光动态,场景灯光随相机,标签随相机,场景模型过滤,场景模型设备等状态动态更新,单楼层模型,室内模型控制切换即各类物联网设备交互等等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/171319.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm+vue协同过滤算法的电影推荐系统

基于ssmvue协同过滤算法的电影推荐系统 摘要 电影推荐系统在信息技术发展的背景下日益成为研究的焦点,本研究基于SSM(Spring SpringMVC MyBatis)框架与Vue.js技术,以协同过滤算法为核心,旨在构建一种高效、准确的电影…

rabbitMq创建交换机,以及路由键绑定队列教程

创建交换机: 创建队列: 创建路由,绑定到交换机: 补充: 创建新用户后,记得点进用户中,那两个set都点击一下; 还有配置代码连接的时候,连的端口为5672,可不…

C语言之文件操作(剩余部分)

上篇博客字数到极限了,给大家把内容补充在这一篇,我们还剩下文件读取结束的判定和文件缓冲区的内容没有介绍,让我们开始下面的学习吧! 目录 1.文件读取结束的判定 1.1feof函数 1.2ferror函数 代码示例 2.文件缓冲区 2.1fflu…

构建Docker基础镜像(ubuntu20.04+python3.9.10+pytorch-gpu-cuda11.8)

文章目录 一、前置条件1.创建 ubuntu 镜像源文件【sources.list】2.下载 python 安装包【Python-3.9.10.tgz】 二、构建方法1.构建目录2.创建DockerFile3.打包镜像 一、前置条件 配置一下 ubuntu 的镜像源下载 python 安装包 1.创建 ubuntu 镜像源文件【sources.list】 内容…

Linux socket编程(2):socket函数介绍及C/S模型代码实现

上一节简单介绍了一下套接字、字节序和地址结构体的概念,算是对socket有一个入门的了解。这一节就实现一个客户端-服务端的代码,从这个例子中来学习socket函数的使用。 文章目录 1 客户端/服务端模型2 套接字函数2.1 socket:创建套接字2.2 bind:绑定套接…

数据结构-堆排序及其复杂度计算

目录 1.堆排序 1.1 向上调整建堆 1.2 向下调整建堆 2. 两种建堆方式的时间复杂度比较 2.1 向下调整建堆的时间复杂度 2.2 向上调整建堆的时间复杂度 Topk问题 上节内容,我们讲了堆的实现,同时还包含了向上调整法和向下调整法,最后我们…

C++字典树算法:找出强数对的最大异或值 II

涉及知识点 数学 字典树 题目 给你一个下标从 0 开始的整数数组 nums 。如果一对整数 x 和 y 满足以下条件&#xff0c;则称其为 强数对 &#xff1a; |x - y| < min(x, y) 你需要从 nums 中选出两个整数&#xff0c;且满足&#xff1a;这两个整数可以形成一个强数对&…

二十四、W5100S/W5500+RP2040树莓派Pico<PHY的状态模式控制>

文章目录 1. 前言2. 相关简介2.1 简述2.2 原理2.3 优点&应用 3. WIZnet以太网芯片4. PHY模式配置测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 W5100S/W5500不仅支持自动PHY自动协商&#xff0c;而且支持用户自定义…

vue3 ref 与shallowRef reactive与shallowReactive

ref 给数据添加响应式&#xff0c;基本类型采用object.defineProperty进行数据劫持&#xff0c;对象类型是借助reactive 实现响应式&#xff0c;采用proxy 实现数据劫持&#xff0c;利用reflect进行源数据的操作 let country ref({count:20,names:[河南,山东,陕西],objs:{key…

C/C++交换输出 2021年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C交换输出 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C交换输出 2021年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入两个整数a,b&#xff0c;将它们交换输出 2、输入输…

概率论和数理统计(三)数理统计基本概念

前言 “概率论”是给定一个随机变量X的分布F(x),然后求某事件A概率 P ( x ∈ A ) P(x \in A) P(x∈A)或者随机变量X的数字特征.“统计”是已知一组样本数据 { x 1 , x 2 , . . . x n } \{x_1,x_2,...x_n\} {x1​,x2​,...xn​},去求分布F(x) 统计的基本概念 在统计中&#x…

Android framework添加自定义的Product项目,lunch目标项目

文章目录 Android framework添加自定义的Product项目1.什么是Product&#xff1f;2.定义自己的Product玩一玩 Android framework添加自定义的Product项目 1.什么是Product&#xff1f; 源码目录下输入lunch命令之后&#xff0c;简单理解下面这些列表就是product。用于把系统编…