Vue3 + Three.js + gltf-pipeline大型园区场景渲染与3D业务
在非使用unity作为3D渲染方案的前提下,对与目前web开发者比较友好的除了canvas场景需要的2D babylon.js,fabric.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.场景具有漫游功能
基于以上我们开始增加需要的工具
- 场景漫游动画处理库
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();}
}
由于业务延展很多不再过的的赘述,解决方案包含了动态标签切换,标记交互,灯光动态,场景灯光随相机,标签随相机,场景模型过滤,场景模型设备等状态动态更新,单楼层模型,室内模型控制切换即各类物联网设备交互等等。
相关文章:
Vue3 + Three.js + gltf-pipeline大型园区场景渲染与3D业务
在非使用unity作为3D渲染方案的前提下,对与目前web开发者比较友好的除了canvas场景需要的2D babylon.js,fabric.js, Three.js是目前针对于jsWeb用户最直接且比较友好的3D引擎方案了。 准备工作: 1.明确需要用的场景方案都有那些,模…...
基于FPGA的PS端的Si5340的控制
1、功能 Si5340/41-D可以输出任意频率,当然有范围,100Hz1GHz。外部输入为24M或者4854M的XTAL,VCO在13500~14256Mhz之间,控制接口采用IIC或者SPI。 芯片架构图 2、IIC控制方式 3、直接上控制代码 使用米联客ZU3EG,将…...
安装 Lua 的 HTTP 库
首先,你需要安装 Lua 的 HTTP 库。可以使用 LuaRocks 来安装。以下是安装命令: luarocks install http然后,你可以使用以下代码来爬取网页内容: local http require http-- 设置代理信息 http.set_proxy(jshk.com.cn)-- 网页UR…...
Redis解决缓存问题
目录 一、引言二、缓存三、Redis缓存四、缓存一致性1.缓存更新策略2.主动更新 五、缓存穿透六、缓存雪崩七、缓存击穿1.基于互斥锁解决具体业务2.基于逻辑过期解决具体业务 一、引言 在一些大型的网站中会有十分庞大的用户访问流量,而过多的用户访问对我们的MySQL数…...
七个合法学习黑客技术的网站,让你从萌新成为大佬
大家好我是若风,一个8年网络安全攻防经验的白帽黑客。 合法的学习网站,以下这些网站,虽说不上全方位的满足你的需求,但是大部分也都能。能带你了解到黑客有关的技术,视频,电子书,实践࿰…...
【数据结构】面试OJ题——带环链表(数学推论)
目录 1.环形链表Ⅰ 编辑 思路 : 思路拓展 问题一: 问题二: 总结: 问题三: 证明总结第三点 总结: 2. 环形链表Ⅱ 思路一 思路二 3.相交链表 思路: 1.环形链表Ⅰ 141. 环形链…...
PostgreSQL中pg_ctl工具的使用
pg_ctl工具有以下功能: (1)初始化postgresql数据库实例 (2)启动、终止或重启postgresql数据库服务 (3)查看postgresql数据库服务的状态 (4)让数据库实例重新读取配置…...
深入理解Kafka3.6.0的核心概念,搭建与使用
Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…...
【python】编程题小代码
空心题(平行四边形) layer int(input("请输入你要打印的行数:")) for i in range(1,layer // 2 2): space_num layer - i for j in range(0,space_num): print(" ",end "") star_num 2 * i - 1 for j in range(0,sta…...
抖音小程序开发全攻略:如何规划项目和选择合适的开发团队
在数字化时代,抖音小程序成为企业推广和服务的重要渠道。本文将为您提供抖音小程序开发的全面攻略,重点介绍如何规划项目和选择合适的开发团队,并附有一些关键的技术代码示例。 1. 项目规划 在开始抖音小程序开发之前,详细的项…...
PSP - 蛋白质复合物结构预测 模版配对(Template Pair) 逻辑的特征分析
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/134328447 在 蛋白质复合物结构预测 的过程中,模版 (Template) 起到重要作用,提供预测结果的关于三维结构的先验信息&…...
喜报不断!箱讯平台获评2023年上海市促进现代航运服务业创新示范项目
近期,可谓捷报频传!在箱讯科技子公司苏州箱讯获评苏州市软件和信息服务业 “头雁”培育企业没过多久,就又迎来好消息! 日前,上海市交通委发布“2023年上海市促进现代航运服务业创新项目”评选结果,箱讯An…...
SOME/IP学习笔记3
目录 1.SOMEIP Transformer 1.1 SOME/IP on-wire format 1.2 协议指定 2. SOMEIP TP 2.1 SOME/IP TP Header 3.小结 1.SOMEIP Transformer 根据autosar CP 相关规范,SOME/IP Transformer主要用于将SOME/IP格式的数据序列化,相当于一个转换器。总体…...
【ATTCK】ATTCK开源项目Caldera学习笔记
CALDERA是一个由python语言编写的红蓝对抗工具(攻击模拟工具)。它是MITRE公司发起的一个研究项目,该工具的攻击流程是建立在ATT&CK攻击行为模型和知识库之上的,能够较真实地APT攻击行为模式。 通过CALDERA工具,安全…...
黑窗口连接远程服务
ssh root192.168.x.x 回车输入密码 查看docker docker ps 停止正在运行的服务 docker stop xxxxx 删除服务 docker rm xxxxx 查看镜像 docker images 删除镜像 docker rmi xxxxx 删除镜像 启动并运行整个服务 docker compose up -d jar包名称 idea 使用tcp方式连接docker 配置d…...
好消息!2023年汉字小达人市级比赛在线模拟题大更新:4个组卷+11个专项,助力孩子更便捷、有效、有趣地备赛
自从《中文自修》杂志社昨天发通知,官宣了2023年第十届汉字小达人市级比赛的日期和安排后,各路学霸们闻风而动,在自己本就繁忙的日程中又加了一项:备赛汉字小达人市级比赛,11月30日,16点-18点。 根据这几年…...
SAP 70策略测试简介
在前面的文章中我们已经测试了10、11、20、40、50、52、60、62策略的测试,接下来我们需要对70策略进行测试,很多的项目中也都会用到70策略。 70策略是一种比较常见的、基于按库存且主要用于半成品或者原材料的计划策略。 我们还是按照之前的惯例,先看下70策略的后台配置 我…...
uniapp+vue3+ts+vite+echarts开发图表类小程序,将echarts导入项目使用的详细步骤,耗时一天终于弄好了
想在uniapp和vue3环境中使用echarts是一件相当前卫的事情,官方适配的还不是很好,echarts的使用插件写的是有些不太清晰的,这里我花费了一天的时间,终于将这个使用步骤搞清楚了,并且建了一个仓库,大家可以直…...
分布式服务器架构的优点有哪些?
在当今数字化时代,随着互联网的普及和技术的不断进步,企业和组织面临着处理越来越多的数据和用户请求的挑战。为了应对这些挑战,分布式服务器架构应运而生。分布式服务器架构通过将任务和数据分散到多个服务器上,提供了许多优点&a…...
Zephyr-7B论文解析及全量训练、Lora训练
文章目录 一、Zephyr:Direct Distillation of LM Alignment1.1 开发经过1.1.1 Zephyr-7B-alpha1.1.2 Zephyr-7B-beta 1.2 摘要1.3 相关工作1.4 算法1.4.1 蒸馏监督微调(dSFT)1.4.2 基于偏好的AI反馈 (AIF)1.4.3 直接蒸馏偏好优化&…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...
