vue + threejs 实现3d装车

这篇博客介绍了Three.js的基本概念和使用,包括场景、相机、渲染器的创建和配置,以及如何导入和处理3D模型。场景中包含了对象、雾化效果和遍历方法;相机包括正投影和透视投影类型,以及它们的参数设置;渲染器涉及了反锯齿、透明度和分辨率设置。此外,文章还详细讲解了灯光类型,如环境光、点光源、聚光灯和平行光,并展示了如何添加和配置。最后,通过加载OBJ和MTL文件,展示了如何加载和显示3D模型。
摘要由CSDN通过智能技术生成

安装

npm install threejs -D

引用threejs

import * as THREE from 'three'
// 鼠标控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// obj模型
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
// 材质
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'

场景
在threejs中场景就只有一种,用THREE.Scene来表示,要构建一个场景只要new一个对象就可以了。

 this.scene = new THREE.Scene()

属性

属性描述
children数组,用于存储添加到场景中的所有对象
fog雾化,雾化效果的特点是场景中的物体离得越远就会变得越模糊,有三个参数:雾的颜色,最近距离,最远距离
方法
方法描述
Add()向场景中添加对象
Remove()移除场景中的对象
getObjectByName()获取场景中指定名称的对象
tranverse()以一个方法作为参数,这个方法将会在每一个子对象上执行。如果子对象本身还有子对象,该方法将会在所有的子对象上执行,直到遍历完场景树中的所有对象为止
相机
相机决定了场景中那个角度的景色会显示出来,相机就像人的眼睛一样,站在不同的位置,抬头或者低头都能看到不同的景色。分为两种正投影相机THREE.OrthographicCamera和透视投影相机THREE.PerspectiveCamera。
正投影与透视透视投影区别
正投影相机
OrthographicCamera( left, right, top, bottom, near, far )
  1. left: 渲染空间的左边界
  2. right: 渲染空间的右边界
  3. top:渲染空间的上边界
  4. bottom:渲染空间的下边界
  5. near: near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
  6. far:far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000

透视投影相机
PerspectiveCamera( fov, aspect, near, far )

this.camera = new THREE.PerspectiveCamera(45, this.rendererWidth / this.rendererHeight, 0.1, 10000000)
  1. fov: 视角fov,视角的大小,如果设置为0,相当你闭上眼睛了,所以什么也看不到,如果为180,那么可以认为你的视界很广阔,但是在180度的时候,往往物体很小,因为他在你的整个可视区域中的比例变小了。一般情况下45
  2. near: 表示你近处的裁面的距离,也可以认为是眼睛距离近处的距离(>0)
  3. aspect: 实际窗口的纵横比,即宽度除以高度。这个值越大,说明你宽度越大,那么你可能看的是宽银幕电影了,如果这个值小于1

渲染器
Three.js中的场景是一个物体的容器,开发者可以将需要的角色放入场景中;
相机的作用就是面对场景,在场景中取一个合适的景,把它拍下来;
渲染器的作用就是将相机拍摄下来的图片,放到浏览器中去显示。
new THREE.WebGLRenderer()

属性含义
antialias是否开启反锯齿,设置为true开启反锯齿。
alpha是否可以设置背景色透明。
maxLights最大灯光数,我们的场景中最多能够添加多少个灯光。
logarithmicDepthBuffer模型的重叠部位不停的闪烁。这便是Z-Fighting问题,为解决这个问题,我们可以采用该种方法
方法含义
setSize制定渲染器的宽高,renderer.setSize(width,height)
setClearColor设置canvas背景色(clearColor)和背景色透明度(clearAlpha)
setPixelRatio设置分辨率,解决场景模糊,抗锯齿的一种很好的方法
 this.renderer = new THREE.WebGLRenderer({
  antialais: true,
    alpha: true,
    logarithmicDepthBuffer: true,
  })
  this.renderer.setSize(this.rendererWidth, this.rendererHeight)
  this.renderer.setClearColor(0x39609b)
  this.renderer.setPixelRatio(window.devicePixelRatio)
  const container = document.getElementById('canvasContainer')
  container.appendChild(this.renderer.domElement)

threejs中的坐标系
在这里插入图片描述
threejs中的灯光

光源种类含义
环境光(AmbientLight)笼罩在整个空间无处不在的光,不能产生阴影
点光源(PointLight )向四面八方发射的单点光源,不能产生阴影
聚光灯(SpotLight )锥形效果的光源,能够产生阴影
平行光(DirectinalLight)平行光,类似太阳光,距离很远的光,会产生阴影
// 环境光
this.ambient = new THREE.AmbientLight(0xffffff, 1)
this.ambient.position.set(0, 0, 0)
this.scene.add(this.ambient)
// 平行光
this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
this.directionalLight.position.set(0, 200, 0)
this.scene.add(this.directionalLight)
// 设置点光源
this.pointLight1 = new THREE.PointLight(0xffffff, 0.3)
this.pointLight1.position.set(-500, 200, 0)
this.scene.add(this.pointLight1)
this.pointLight2 = new THREE.PointLight(0xffffff, 0.3)
this.pointLight2.position.set(500, 200, 0)
this.scene.add(this.pointLight2)

模型加载
实际开发中,大多数项目,通常是3D美术设计师或建筑、机械等行业工程师提供的由3dmx、blender、substence、Solidworks等软件创建好的三维模型文件
1.加载.obj模型文件:
使用三维软件导出 .obj 模型文件的时候,会同时导出一个材质文件 .mtl , .obj 和 .stl 文件包含的信息一样都是几何体顶点相关数据,材质文件 .mtl 包含的是模型的材质信息,比如颜色、贴图路径等。
加载 .obj 三维模型的时候,可以只加载 .obj 文件,然后借助three.js引擎自定义材质Material,也可以同时加载 .obj 和 .mtl 文件。
只加载obj文件:

<!-- 引入obj模型加载库OBJLoader.js -->
<script src="../../three.js-master/examples/js/loaders/OBJLoader.js"></script>
/**
* OBJ文件加载 只加载obj文件中的几何信息,不加载材质文件.mtl
*/
var loader = new THREE.OBJLoader();
// 没有材质文件,系统自动设置Phong网格材质
loader.load('./立方体/box.obj',function (obj) {
// 控制台查看返回结构:包含一个网格模型Mesh的组Group
console.log(obj);
// 查看加载器生成的材质对象:MeshPhongMaterial
console.log(obj.children[0].material);
scene.add(obj);
})

// 加载后的一些编辑操作
obj.children[0].scale.set(20,20,20);//网格模型缩放
obj.children[0].geometry.center();//网格模型的几何体居中
obj.children[0].material.color.set(0xff0000);//设置材质颜色

同时加载obj文件和mtl文件:
mtl 文件包含了模型的材质信息,比如模型颜色、透明度等信息,还有纹理贴图的路径,比如颜色贴图、法线贴图、高光贴图等等。

<!-- 引入obj模型加载库OBJLoader.js -->
<script src="../../three.js-master/examples/js/loaders/OBJLoader.js"></script>
<!-- 引入obj模型材质加载库MTLLoader.js -->
<script src="../../three.js-master/examples/js/loaders/MTLLoader.js"></script>
/**
* OBJ和材质文件mtl加载
*/
var OBJLoader = new THREE.OBJLoader();//obj加载器
var MTLLoader = new THREE.MTLLoader();//材质文件加载器
MTLLoader.load('./立方体/box.mtl', function(materials) {
// 返回一个包含材质的对象MaterialCreator
console.log(materials);
//obj的模型会和MaterialCreator包含的材质对应起来
OBJLoader.setMaterials(materials);
OBJLoader.load('./立方体/box.obj', function(obj) {
console.log(obj);
obj.scale.set(10, 10, 10); //放大obj组对象
scene.add(obj);//返回的组对象插入场景中
})
})

完整代码:

// html
<div id="canvasContainer" />
// js
// json 数据
const pageData = {container: {length: 15670,width: 2870,height: 3300,},
  boxlist: [{color: 'rgba(235,215,0,0.5)',x: 0,length: 2120, width: 1200,y: 0,z: 0,boxId: '5GD821021(左前叶子板)_1',height: 1640,},
    { color: 'rgba(235,0,215,0.5)',x: 0,length: 2120,width: 1200,y: 1200,z: 0,boxId: '5GD821022(右前叶子板)_1',height: 1640,},
    {color: 'rgba(215,215,30,0.5)',: 2120,length: 1785,width: 2160,y: 0,z: 0,boxId: '5GD823031(发动机盖总成)_1',height: 1530,},
    {color: 'rgba(215,215,30,0.5)',x: 2120,length: 1785,width: 2160,y: 0,z: 1530,boxId: '5GD823031(发动机盖总成)_1', height: 1530,},
    {color: 'rgba(215,30,215,0.5)',x: 3905,length: 1700,width: 2150,y: 0,z: 0,boxId: '3CG827025E(后+R58+F57:Q57)_1',height: 1450,},
    {color: 'rgba(215,195,60,0.5)',x: 3905,length: 1700, width: 2150,y: 0,z: 1450,boxId: '3CG827025E(后+R58+F57:Q57)_1',height: 1450,},
    {color: 'rgba(195,215,60,0.5)',x: 5605,length: 2160,width: 1350,y: 0,z: 0,boxId: '3CG831055D(左前门焊接总成)_1',height: 1700,},
    {color: 'rgba(195,60,215,0.5)',x: 5605,length: 2150,width: 1330,y: 0,z: 1700,boxId: '5DD831055(左前门)_1',height: 1465,},
    {color: 'rgba(215,175,90,0.5)',x: 7765,length: 2160,width: 1350, y: 0,z: 0,boxId: '3CG831056D(右前门焊接总成)_1',height: 1700,},
    {color: 'rgba(175,215,90,0.5)',x: 7765,length: 2150,width: 1330,y: 0,z: 1700,boxId: '5DD833056(右后门)_1',height: 1465,},
    {color: 'rgba(175,90,215,0.5)',x: 9925,length: 2160,width: 1350,y: 0,z: 0,boxId: '3CG831056D(右前门焊接总成)_2',height: 1700,},
    {color: 'rgba(215,155,120,0.5)',x: 9925,length: 2150,width: 1330,y: 0,z: 1700,boxId: '5GD831055A(左前门)_1', height: 1465,},
    {color: 'rgba(155,215,120,0.5)',x: 12085,length: 2160,width: 1280,y: 0,z: 0,boxId: '2GG831051(车门)_1',height: 1560,},
    { color: 'rgba(155,120,215,0.5)',x: 12085,length: 2160, width: 1280,y: 0,z: 1560,boxId: '2GG831051(车门)_1',height: 1560,},
    {color: 'rgba(215,135,150,0.5)',x: 14245,length: 1280,width: 1800,y: 0,z: 0,boxId: '2GG821101(翼子板)_1',height: 1500,},
    {color: 'rgba(135,215,150,0.5)',x: 14245,length: 1280,width: 1800,y: 0,z: 1500,boxId: '2GG821102(翼子板)_1',height: 1500,},
    {color: 'rgba(135,150,215,0.5)',x: 5605,length: 2120,width: 1200, y: 1350,z: 0,boxId: '5GD821021(左前叶子板)_1',height: 1640,},
    {'color': 'rgba(215,115,180,0.5)',x: 7725,length: 2120,width: 1200,y: 1350, z: 0,boxId: '5GD821021(左前叶子板)_2',height: 1640,},
    {color: 'rgba(115,215,180,0.5)',x: 9845,length: 2120,width: 1200,y: 1350,z: 0,boxId: '5GD821021(左前叶子板)_3',height: 1640,},
    {color: 'rgba(115,180,215,0.5)',x: 12085,length: 2160,width: 1280,y: 1280,z: 0, boxId: '5GD833055A(左后门)_1',height: 1480,},
    {color: 'rgba(215,95,210,0.5)', x: 12085,length: 2160,width: 1280,y: 1280,z: 1480,boxId: '5GD833056A(右后门)_1',height: 1480,},,}
export default {
  data(){
    return {
       renderer: null, // 渲染器
		scene: null, // 场景
		camera: null, // 相机
		ambient: null, // 环境光
		directionalLight: null, // 平行光
		pointLight1: null, // 点光源1
		pointLight2: null, // 点光源2
		controls: null, // 轨道控件
		carLength: 0,
	    carHeight: 0,
	    carWidth: 0,
    }
  },
  methods: {
        threeInit() {
      // 初始化渲染器
      this.initThree()
      // 初始化场景
      this.initScene()
      // 初始化相机
      this.initCamera()
      // 初始化灯光
      this.initLight()
      // 加载obj文件
      this.initAgv()
      // 初始化控件
      this.initControls()
      // 循环渲染
      this.animation()
    },
    initThree() {
      this.renderer = new THREE.WebGLRenderer({
        antialais: true,
        alpha: true,
        logarithmicDepthBuffer: true,
      })
      this.renderer.setSize(this.rendererWidth, this.rendererHeight)
      this.renderer.setClearColor(0x39609b)
      this.renderer.setPixelRatio(window.devicePixelRatio)
      const container = document.getElementById('canvasContainer')
      container.appendChild(this.renderer.domElement)
    },
    initScene() {
      this.scene = new THREE.Scene()
    },
    initCamera() {
      this.camera = new THREE.PerspectiveCamera(45, this.rendererWidth / this.rendererHeight, 0.1, 10000000)
      // 相机位置
      var conta = { length: pageData.container.length, width: pageData.container.width, height: pageData.container.height }
      this.camera.position.set(conta.length * 2, conta.width * 2, conta.height * 2)
      // 相机朝向
      this.camera.lookAt(this.scene.position)
      this.scene.add(this.camera)
    },
    initLight() {
      // 环境光
      this.ambient = new THREE.AmbientLight(0xffffff, 1)
      this.ambient.position.set(0, 0, 0)
      this.scene.add(this.ambient)
      // 平行光
      this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
      this.directionalLight.position.set(0, 200, 0)
      this.scene.add(this.directionalLight)
      // 设置点光源
      this.pointLight1 = new THREE.PointLight(0xffffff, 0.3)
      this.pointLight1.position.set(-500, 200, 0)
      this.scene.add(this.pointLight1)
      this.pointLight2 = new THREE.PointLight(0xffffff, 0.3)
      this.pointLight2.position.set(500, 200, 0)
      this.scene.add(this.pointLight2)
    },
    initAgv() {
      const objLoader = new OBJLoader()
      const mtlLoader = new MTLLoader()
      mtlLoader.load('static/model/car.mtl', (materials) => {
        objLoader.setMaterials(materials)
        objLoader.load('static/model/car.obj', (obj) => {
          obj.scale.set(20, 20, 20) // 网格模型缩放
          const bbox = new THREE.Box3().setFromObject(obj)

          var carHead = 5617 // 车头长度 = (bbox.max.z - bbox.min.z)/2 - 7866
          this.carLength = bbox.max.z - bbox.min.z - carHead // 车箱长度,不含车头
          this.carHeight = bbox.max.x - bbox.min.x
          this.carWidth = bbox.max.y - bbox.min.y
          var difx = (bbox.max.x - bbox.min.x) / 2
          obj.rotation.y = -Math.PI / 2
          obj.translateZ(-7866)
          obj.translateY(-2500)
          obj.translateX(2500)
          this.scene.add(obj)
        })
      })
    },
    initControls() {
      // x 红 y 绿 z 蓝
      this.controls = new OrbitControls(this.camera, this.renderer.domElement) // 创建控件对象
      var axes = new THREE.AxisHelper(6000)
      this.scene.add(axes)
    },
    animation() {
      requestAnimationFrame(this.animation)
      this.controls.update()
      this.renderer.render(this.scene, this.camera)
    },
    onLoading() {
      this.drawPackage(pageData)
    },
    drawPackage(data) {
      const carData = data.container
      const packData = data.boxlist
      packData.forEach((item) => {
        var realVirtualRatio = Math.min(this.carWidth / carData.width, this.carHeight / carData.height, this.carLength / carData.length)
        var goodsLength = item.length * realVirtualRatio
        var goodsWidth = item.width * realVirtualRatio
        var goodsHeight = item.height * realVirtualRatio
        var goodsX = item.x * realVirtualRatio
        var goodsY = item.y * realVirtualRatio
        var goodsZ = item.z * realVirtualRatio
        const box = new THREE.BoxGeometry(goodsLength, goodsWidth, goodsHeight)
        const Mesh = new THREE.MeshLambertMaterial({
          color: item.color,
        })
        // 画出网格图形并且定义材质(颜色)
        var MeshItem = new THREE.Mesh(box, Mesh)
        MeshItem.uuid = item.boxId
        MeshItem.type = item.type
        MeshItem.position.copy(new THREE.Vector3(goodsX + goodsLength / 2, goodsY + goodsWidth / 2, goodsZ + goodsHeight / 2))
        MeshItem.castShadow = !0
        MeshItem.receiveShadow = !0
        this.scene.add(MeshItem)
      })
    },
  }
}

装车后的效果

VueThree.js是两个热门的Web开发框架和库,分别用于构建用户界面和创建3D场景。结合使用VueThree.js可以非常方便地搭建一个3D仓房。 首先,在Vue项目中安装并引入Three.js库。可以使用npm或者直接在HTML中引入CDN链接。然后,使用Vue的组件化开发思想,创建一个3D仓房组件。 在Vue3D仓房组件中,可以使用Three.js的场景(Scene)、相机(Camera)、渲染器(Renderer)等基本元素来创建一个空白的3D场景。可以设定相机的位置和方向,调整渲染器的大小和样式。 接下来,可以使用Three.js提供的几何体(Geometry)和材质(Material)来创建具体的仓房模型。例如,可以使用BoxGeometry创建一个长方体模型,然后使用MeshBasicMaterial设置其颜色或者使用纹理材质来进行贴。 在几何体和材质创建好之后,可以将其合并成一个网格(Mesh),并添加到场景中。 为了使3D场景更加生动,可以使用Three.js的灯光(Light)来设置光照效果。例如,太阳光照射到仓房模型上,可以使用光源和颜色来模拟阳光的效果。 最后,在Vue3D仓房组件中添加交互功能,例如旋转、缩放或者平移等,可以使用Three.js提供的控制器(Controller)或者自定义事件监听器来实现。 在Vue项目中的相应页面引入3D仓房组件,并传入相应的参数,即可在浏览器中看到搭建好的3D仓房场景。 总之,使用VueThree.js搭建3D仓房的过程大致如上所述,需要使用Vue的组件化开发和Three.js的渲染和建模功能来实现。这样可以充分利用两个框架和库的优势,简化开发流程,创建出生动逼真的3D仓房场景。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值