ThreeJs绘制仓库场景

news2024/11/16 11:42:15

        之前有做过关于threejs开发的一些小功能项,最近正好在做一个仓库相关的3D场景,这里贴出核心代码和大家分享下。

        首先要做3D场景,除了基础的组件要引入生成外,要先加入地板,在地板上添加一些3D模型达到各种各样的效果,所以首先插入地板,这里用的是vue框架,所以和原始的html使用方法上有一些区别,地板我这里也是创建了一个立体,只不过长和宽很大,高度只有1,所以看起来像一个地板,其次就是给地板添加一些纹理贴图,使地板看起来更真实一些,然后设置地板位置,最终加入到场景scene中,为了方便扩展性更好,这里把地面的宽高厚度设置为变量,方便后续修改

initFloor(){
      let floorGeometry = new THREE.BoxGeometry(         
      this.floor.floorLength,this.floor.floorWidth,this.floor.floorDepth);
      let texture = new THREE.TextureLoader().load( '/static/images/floor.jpg' )
      texture.wrapS = THREE.RepeatWrapping
      texture.wrapT = THREE.RepeatWrapping
      texture.repeat.set(5,5)
      let cubeMaterial = new THREE.MeshLambertMaterial( {
        map:texture
      } );
      let floor = new THREE.Mesh( floorGeometry, cubeMaterial );
      floor.name = '地板';
      floor.position.set(this.floor.floorLength/2,this.floor.floorWidth/2,0)
      scene.add(floor)
},

效果如下:

添加完地面后,开始添加墙面,没有墙面的话会显得物体比较突兀,而且场地没有边界感,如果是模拟的墙可以直接添加四面墙,不用考虑窗户和门,这里从简开发,就只添加四面墙,先设置好每面墙的高度和宽度,同样这里将墙体的相关参数设置为变量,方便后期修改。

//创建墙面 
createCubeWall() {
      let materialTie = [];
      materialTie.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //前  0xafc0ca :灰色
      materialTie.push(new THREE.MeshPhongMaterial({color: 0x9cb2d1}));  //后  0x9cb2d1:淡紫
      materialTie.push(new THREE.MeshPhongMaterial({color: 0xd6e4ec}));  //上  0xd6e4ec: 偏白色
      materialTie.push(new THREE.MeshPhongMaterial({color: 0xd6e4ec}));  //下
      materialTie.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //左   0xafc0ca :灰色
      materialTie.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //右

      let wallList = []
      let wall1 = {width:this.floor.floorLength, height:3, depth:20, angle:0, matArrayB:materialTie, x:this.floor.floorLength/2, y:0, z:10, name:"墙面"};
      let wall2 = {width:this.floor.floorLength, height:3, depth:20, angle:1, matArrayB:materialTie, x:this.floor.floorLength/2, y:this.floor.floorWidth, z:10, name:"墙面"};
      let wall3 = {width:this.floor.floorWidth, height:3, depth:20, angle:1.5, matArrayB:materialTie, x:0, y:(this.floor.floorWidth/2), z:10, name:"墙面"};
      let wall4 = {width:this.floor.floorWidth, height:3, depth:20, angle:1.5, matArrayB:materialTie, x:this.floor.floorLength, y:(this.floor.floorWidth/2), z:10, name:"墙面"};
      wallList.push(wall1);wallList.push(wall2);wallList.push(wall3);wallList.push(wall4);
      for(let i=0;i<wallList.length;i++){
        let cubeGeometry = new THREE.BoxGeometry(wallList[i].width, wallList[i].height, wallList[i].depth);
        let cube = new THREE.Mesh(cubeGeometry, wallList[i].matArrayB);
        cube.position.x = wallList[i].x;
        cube.position.y = wallList[i].y;
        cube.position.z = wallList[i].z;
        cube.rotation.z += wallList[i].angle * Math.PI; //-逆时针旋转,+顺时针
        cube.name = wallList[i].name;
        scene.add(cube);
      }
    },

效果如下:

再接着就是添加一些货架,货架不推荐使用建模的方式开发,因为后续一但货架的尺寸发生了改变,那么就需要对模型重新改动再引入进来,太麻烦了,尽量使用threejs提供的建模方式进行绘制,因为需求的关系这里需要两两货架并拢,所以代码会显得有些乱,货架包括了四条腿,层板,五个模型,这里对五个模型的相关参数都设置为变量的方式,方便后面快速调整货架的模型,仓库也不会只有一个货架,这里用了循环,实现12排货架,每排两组货架,每组三个货架,

//初始化货架    
initShelf(){
      for(let n=0;n<2;n++) {
        let offsetX = 60;
        for (let i = 0; i < 12; i++) {
          if(i%2 === 1){
            offsetX  = offsetX + 45;
          }else{
            offsetX = offsetX + 25;
            this.addPaTrack(offsetX, this.indistanceY+n*180, 0,true,"car_"+n+"_"+i)
          }
          for (let j = 0; j < 3; j++) {
            let shelfName = '货架' + j+"上"
            this.shelfList.push({
              shelfName: shelfName,
              planeWidth: this.plane.planeWidth,
              planeHeight: this.plane.planeHeight,
              planeLength: this.plane.planeLength,
              holderLength: this.holder.holderLength,
              holderHeight: this.holder.holderHeight,
              holderWidth: this.holder.holderWidth,
              positionX: offsetX,
              positionY: this.indistanceY + (j * 60)+n*180,
              positionZ: this.holder.holderHeight + 2,
              layerNum: this.layerNum,
              columnNum: this.columnNum
            });
          }
        }
      }
      for(let i = 0;i < this.shelfList.length; i++){
        for(let j = 0; j < this.shelfList[i].layerNum; j++){
          this.addShelf(
              this.shelfList[i].positionX,
              this.shelfList[i].positionY,
              this.shelfList[i].positionZ*(j+1),
              this.shelfList[i].planeWidth,
              this.shelfList[i].planeLength,
              this.shelfList[i].planeHeight,
              this.shelfList[i].holderLength,
              this.shelfList[i].holderWidth,
              this.shelfList[i].holderHeight,
              scene,
              this.shelfList[i].shelfName+"$"+j,
              this.shelfList[i].columnNum);
        }
      }
    },
    addShelf(x,y,z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name,num){
      let RackMat2 = new THREE.MeshPhongMaterial({color:0xFFFFFF});
      let plane = new THREE.BoxGeometry( plane_x, plane_y/num,plane_z, );
      let gz = [];
      for(let i = 0; i < num; i++){
        gz.push( y + plane_y/num/2 + (plane_y/num)*i );
        let obj = new THREE.Mesh( plane, RackMat2 );
        obj.position.set(x, gz[i], z) ;
        scene.add(obj);
      }
      let holder = new THREE.BoxGeometry( holder_x, holder_y, holder_z );
      let obj2 = new THREE.Mesh( holder, RackMat2, 0 );
      let obj3 = new THREE.Mesh( holder, RackMat2, 0 );
      let obj4 = new THREE.Mesh( holder, RackMat2, 0 );
      let obj5 = new THREE.Mesh( holder, RackMat2, 0 );
      obj2.position.set(x-plane_x/2+holder_x/2,y+holder_y/2,z-holder_z/2-plane_z/2,);
      obj3.position.set(x+plane_x/2-holder_x/2,y+holder_y/2, z-holder_z/2-plane_z/2, );
      obj4.position.set(x-plane_x/2+holder_x/2,y+plane_y-holder_y/2,z-holder_z/2-plane_z/2 );
      obj5.position.set(x+plane_x/2-holder_x/2,y+plane_y-holder_y/2, z-holder_z/2-plane_z/2 );
      scene.add(obj2);scene.add(obj3);scene.add(obj4);scene.add(obj5);
    },

效果图如下:

有了货架还需要在货架上防止货物,货物可以根据货架的位置动态计算货物放置的位置,先做一个专门添加货物的功能,在做一个初始化货架的方法,在初始化货架中根据需求添加货物,再循环调用生成货物的方法添加多个货物。

//初始化货架
initCube(){
      for(let q=0; q<this.shelfList.length;q++){
        for(let i=0;i<this.layerNum;i++){
          for(let j=0;j<this.columnNum;j++){
            let shorageName = this.shelfList[q].shelfName+"_"+i+"层_"+j+"列"
            let x = this.shelfList[q].positionX;
            let y = this.shelfList[q].positionY + (this.box.boxDepth) + j*(this.plane.planeLength/3)
            let z = this.shelfList[q].positionZ + (this.box.boxDepth/2) + i*(this.holder.holderHeight+this.plane.planeHeight)
            this.addCube(x-6,y,z,"货物在"+"_"+shorageName+"_1")
            this.addCube(x+5,y,z,"货物在"+"_"+shorageName+"_2")
          }
        }
      }
    },
    //新增货架
    addCube(x,y,z,name){
      const loader = new GLTFLoader()
      loader.load("/static/model/box.glb", (gltf) => {
        gltf.scene.position.set(x, y, z+1)   // 模型位置
        gltf.scene.rotation.x = Math.PI / 2    // x轴旋转
        gltf.scene.scale.set(0.6, 0.4, 0.5)   // 模型位置
        gltf.scene.name = name
        scene.add(gltf.scene)
      })
    },

效果如下:

这边是添加的glb格式的货物模型,是一个蓝色的料箱,颜色比较深且没加贴图显得有点失真,大家可以根据需要添加贴图或者换成适合的货物模型,也可以自己做立方体添加贴图来代替。到这里一个厂区的模型大概成型了,但是一般还会添加AGV车,或者叉车模型,这里再做个AGV的模型,在添加AGV小车前先添加地上的二维码用于给下小车导航,最终是要对接到真实的二维码,这里就大概绘制一个二维码的区域,依然是和地板类似的方式添加一个个小方块,上面使用二维码的贴图,并循环咋区域内绘制出来。

initQRCode() {
      for (let i = 0; i < 18; i++) {
        for (let j = 0; j < 3; j++) {
          this.addQRCode(50 + (i * 30), 40 + (j * 30), 0, 'qrcode')
        }
      }
    },
    addQRCode(x,y,z,name){
      let qrCodeMat = new THREE.MeshLambertMaterial();
      new THREE.TextureLoader().load( "/static/images/qrcode.png", function( map ) {
        qrCodeMat.map = map;
        qrCodeMat.needsUpdate = true;
      } );
      let geometry = new THREE.BoxGeometry( 5, 5, 1 );
      let qrCode = new THREE.Mesh(geometry, qrCodeMat);
      qrCode.position.set(x,y,1)
      qrCode.name = name
      scene.add(qrCode);
    },

效果如下:

因为距离远会有些失真,不过二维码能起到表达这个意思就好了,添加完二维码后就是添加AGV车了,这里可以把AGV车放在第一个二维码的位置,最终AGV还是要对接到真实的AGV为止的,可以通过MQTT或者webscoket来实时获取AGV的位置来动态改变AGV模型在场景中的位置,下面添加AGV小车的模型:

initAGV() {
      this.addAGV(50,40,2,true)
    },
    addAGV(x,y,z,box){
      const loader = new GLTFLoader()
      loader.load("/static/model/agv.gltf", (gltf) => {
        gltf.scene.position.set(x,y,z)   // 模型位置
        gltf.scene.rotation.x = Math.PI / 2    // x轴旋转
        gltf.scene.rotation.y = Math.PI / 2   // z轴旋转
        gltf.scene.scale.set(0.1, 0.1 , 0.1)   // 模型位置
        gltf.scene.name ='1'
        if(box){
          loader.load("/static/model/box.glb", (gltf) => {
            gltf.scene.position.set(x, y, z+10)   // 模型位置
            gltf.scene.rotation.x = Math.PI / 2    // x轴旋转
            gltf.scene.scale.set(0.6, 0.4, 0.5)   // 模型位置
            gltf.scene.name = name
            scene.add(gltf.scene)
          })
        }
        this.agvList.push(gltf.scene);
        scene.add(gltf.scene)   // 加入场景
      })
    },

效果如下:

为了真实性我还给小车上放了一个货物的托盘,至此,一个简易的工厂模型就搭建好了,不过如果货物太多的话可能会导致浏览器卡顿,因此在开始绘制的初期就要考虑这个问题,如果是建模的方式来制作模型可以考虑做成低模的方式,在使用的时候再对模型文件进行压缩,如果是ThreeJs代码的方式,可以考虑做一些代码上的优化。

最终为了提高效果,我们可以再添加一个动画效果,飞入的方式展现,原理上就是控制相机做一个飞入的动作来实现好看的动画效果

效果如下:

仓库的3D效果

好了这里一个简单的工厂模型就好了,可以根据具体需求定制化开发一些其他功能,实现更好的效果。我只是刚开始做ThreeJs,这里做个记录,勿喷

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

wiondow系统-python中缺少JDK安装(超详解)!!!

因为学习python中&#xff0c;用到Pysaprk,但因缺少JDK而报错&#xff0c;解决方法如下 下载新款且稳定的17版本&#xff08;21不推荐&#xff09;官网下载有限速设置&#xff0c;压缩包我已经放在下面了&#xff0c;注意提取 百度网盘链接&#xff1a;https://pan.baidu.com/…

Mobaxterm 使用lrzsz传输文件(rz/sz)

Mobaxterm 使用lrzsz传输文件报错 1. 现象 最近从xshell切换到Mobaxterm其他一切正常,就是使用rz传输文件时会出现错误,比较苦恼. 会出现以下错误 [rootcentos7 rpmbuild]# rz ▒CCCCCCCCCCC23be50ive.**B0100000023be502. 解决方法 去官网(https://mobaxterm.mobatek.net…

【技术分享】RK3399 Ubuntu通过Python实现录音和播放功能

​本文基于IDO-SBC3968 Ubuntu 系统通过Python脚本实现录音和播放功能。 IDO-SBC3968采用RK3399国产六核64位CPU高性能处理器&#xff0c;支持4K HDMI2.0显示&#xff0c;接口丰富&#xff0c;拥有千兆以太网&#xff0c;全协议TypeC接口&#xff0c;USB3.0 &#xff0c;eDP 和…

Android : ListView + SimpleAdapter(简单适配器)-简单应用

示例图&#xff1a; MainActivity.java package com.example.mylistviewsimpleadapter;import androidx.appcompat.app.AppCompatActivity;import android.content.Context; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import …

LangChain(0.0.339)官方文档一:快速入门

LangChain官网、LangChain官方文档 、langchain Github、langchain API文档、llm-universe 文章目录 一、LangChain简介&#xff08;v0.0.338&#xff09;1.1 整体框架1.2 主要组件1.2.1 Model I/O1.2.2 Retrieval1.2.2.1 RAG1.2.2.2 Retrieval 1.2.3 Chain 1.3 其它组件1.3.1 …

windbg双机调试

1&#xff1a;虚拟机增加串行端口 2&#xff1a;操作步骤&#xff1a;编辑虚拟机设置 -> 添加 -> 串行端口 -> 完成 参数配置&#xff1a;使用命名管道 -> \\.\pipe\com_1 -> 该端是服务器&#xff0c;另一端是应用程序 -> 轮询时主动放弃CPU->确定 3 -b…

App Inventor 2 什么情况下需要使用字典?

介绍 字典在其他语言中称为映射、关联数组或列表&#xff0c;是一种将一个值&#xff08;通常称为键&#xff09;与另一个值关联的数据结构。 Q&#xff1a;App Inventor 2 什么情况下需要使用字典&#xff1f; A&#xff1a;列表能完成字典的绝大部分功能&#xff0c;不过字…

C++之unordered_map/set的使用

前面我们已经学习了STL中底层为红黑树结构的一系列关联式容器——set/multiset 和 map/multimap(C98). unordered系列关联式容器 在C98中, STL提供了底层为红黑树结构的一系列关联式容器, 在查询时效率可达到log2N,即最差情况下需要比较红黑树的高度次, 当树中的节点非常多时,…

前端入门(三)Vue生命周期、组件技术、事件总线、

文章目录 Vue生命周期Vue 组件化编程 - .vue文件非单文件组件组件的注意点组件嵌套Vue实例对象和VueComponent实例对象Js对象原型与原型链Vue与VueComponent的重要内置关系 应用单文件组件构建 Vue脚手架 - vue.cli项目文件结构refpropsmixin插件scoped样式 Vue生命周期 1、bef…

企业远程访问业务系统:对比MPLS专线,贝锐蒲公英为何更优优势?

如今&#xff0c;企业大多都会采用OA、ERP、CRM等各种数字化业务系统。 私有云、公有云混合架构也变得越来越常见。 比如&#xff1a;研发系统部署在公司本地私有云、确保数据安全&#xff0c;OA采用公有云方案、满足随时随地访问需求。 如此一来&#xff0c;也产生了远程访问…

OSS+CDN的资费和安全

文章目录 花费OSSCDNOSS CDN 安全OSS防盗链跨域设置CORS数据加密 CDN防盗链URL鉴权Cookie鉴权远程鉴权IP黑白名单UA黑白名单 回源服务自定义私有参数IP黑白名单数据加密 花费 OSS 存储费用 &#xff1a;0.12元/GB/月下行流量费用 &#xff1a;0.5元/GB请求费用 &#xff1a;…

PyQt6简介

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计12条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

threejs创建一个旋转的正方体【完整代码】

效果&#xff1a; 中文网three.js docs 1.搭建环境 安装three 首先我们需要新建一个项目 vue/react都可 这里以vue为演示 npm i three 找到一个新的页面 在页面script的地方导入three import * as THREE from "three" 或者自己逐个导入 import {PerspectiveC…

Tiktok小店如何入驻?注册流程与资料全解

作为国内成功的出海App之一&#xff0c;Tiktok的特色就是社交平台兴趣电商&#xff0c;已然成为当前跨境电商的一大趋势。数据显示&#xff0c;目前Tiktok全球月活跃用户已接近16亿&#xff0c;正是红海一片。非常值得跨境电商玩家入局&#xff01;今天就来给大家整理一份tk小店…

行情分析 - - 加密货币市场大盘走势(11.24)

大饼昨日震荡幅度很小&#xff0c;而今天延续昨日的空头思路。当然如果从MACD日线来看&#xff0c;处于上涨趋势&#xff0c;稳健的可以选择观望等待。空头思路是因为目前EMA21均线和EMA55均线依然保持很远&#xff0c;最近两个月BTC上涨40%&#xff0c;而最近持续保持高位很快…

Motion Plan之基于采样的路径规划算法笔记

Motion Plan之搜索算法笔记 背景&#xff1a; 基于采样算法是一种在路径规划中广泛应用的有效方法。它通过在图中随机选择点来生成一个简化的搜索图&#xff0c;从而加速搜索过程。这种方法的主要优点包括减少内存使用&#xff0c;避免计算错误&#xff0c;具有动态障碍物对抗…

NFT Insider115:The Sandbox开设元宇宙Diorama快闪店,​YGG Web3 游戏峰会已开幕

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members、BeepCrypto联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据&#xff0c;艺术新闻类&#xff0c;游戏新闻类&#xff0c;虚拟世界类&#…

如何用SWIG封装c++接口给java使用?

SWIG是什么&#xff1f; SWIG(Simplified Wrapper and Interface Generator)是一个将C/C接口转换为其他语言接口的工具&#xff0c;从而可以讲C/C的库集成到其他语言的系统中。目前SWIG已经可以支持Python, Java, C#,Ruby&#xff0c;PHP,R语言等十多种语言。 官方网址&…

银河麒麟安装Docker

# 配置阿里云 Centos8 镜像源&#xff0c;需要额外的一些依赖&#xff0c;而这些依赖在麒麟官方的源里面是没有的 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo# 配置阿里云 docker 镜像源 sudo yum-config-manager --add-r…

无人智能货柜:提升购物体验

无人智能货柜&#xff1a;提升购物体验 随着移动支付的普及&#xff0c;人们日常生活中的主要场景已经渗透了这一支付方式。同时&#xff0c;无人智能货柜作为购物的重要渠道&#xff0c;正在崭露头角。通过人工智能、图像识别和物联网技术的应用&#xff0c;无人智能货柜将使购…