vue + threejs 实现3d装车
安装
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 ) |
- left: 渲染空间的左边界
- right: 渲染空间的右边界
- top:渲染空间的上边界
- bottom:渲染空间的下边界
- near: near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
- far:far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000
透视投影相机
PerspectiveCamera( fov, aspect, near, far )
this.camera = new THREE.PerspectiveCamera(45, this.rendererWidth / this.rendererHeight, 0.1, 10000000)
- fov: 视角fov,视角的大小,如果设置为0,相当你闭上眼睛了,所以什么也看不到,如果为180,那么可以认为你的视界很广阔,但是在180度的时候,往往物体很小,因为他在你的整个可视区域中的比例变小了。一般情况下45
- near: 表示你近处的裁面的距离,也可以认为是眼睛距离近处的距离(>0)
- 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, // 点光源1pointLight2: null, // 点光源2controls: 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 - 7866this.carLength = bbox.max.z - bbox.min.z - carHead // 车箱长度,不含车头this.carHeight = bbox.max.x - bbox.min.xthis.carWidth = bbox.max.y - bbox.min.yvar difx = (bbox.max.x - bbox.min.x) / 2obj.rotation.y = -Math.PI / 2obj.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.containerconst packData = data.boxlistpackData.forEach((item) => {var realVirtualRatio = Math.min(this.carWidth / carData.width, this.carHeight / carData.height, this.carLength / carData.length)var goodsLength = item.length * realVirtualRatiovar goodsWidth = item.width * realVirtualRatiovar goodsHeight = item.height * realVirtualRatiovar goodsX = item.x * realVirtualRatiovar goodsY = item.y * realVirtualRatiovar goodsZ = item.z * realVirtualRatioconst 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.boxIdMeshItem.type = item.typeMeshItem.position.copy(new THREE.Vector3(goodsX + goodsLength / 2, goodsY + goodsWidth / 2, goodsZ + goodsHeight / 2))MeshItem.castShadow = !0MeshItem.receiveShadow = !0this.scene.add(MeshItem)})},}
}
vue + threejs 实现3d装车相关推荐
- vue + threejs 给3D模型添加label标签(dom的方式)
webGL.js封装的代码. const THREE = window.THREE; // webGL对象配置 export const webglOBJ = {renderDom: null,Sce ...
- 使用threejs 实现3D物体展示,平移实现类似百度地图功能
为了实现类似百度地图功能 使用threejs 实现3D物体,通过鼠标平移,缩放,键盘箭头按钮左右移动的功能展示. <!DOCTYPE html> <html> <head ...
- 基于ThreeJS实现3D模型上的室内灯光效果模拟
基于ThreeJS实现3D模型上的室内灯光效果模拟 示例描述与操作指南 示例效果展示 实现步骤 示例描述与操作指南 当前示例用于展示室内灯光的多角度光影效果. 示例效果展示 实现步骤 第一步 创建聚光 ...
- Threejs实现3D场景浏览器内存消耗过高导致浏览器卡顿崩溃刷新等问题解决办法以及3D场景在手机浏览器中画质不高的原因
个人主页: 左本Web3D,更多案例预览请点击==> 在线案例 个人简介:专注Web3D使用ThreeJS实现3D效果技巧和学习案例
- threejs创建3d交互地图
文章目录 前言 关键点 源码 总结 前言 基于react-hooks创建的三维地图,只实现了基本的交互展示,可根据个人喜好增加各种交互和展示效果,效果如下. 关键点 使用threejs创建3d地图注意 ...
- threejs生成3d地图所需
threejs生成3d地图所需 1.threejs,郭龙邦弄的教程 熟悉形状.线绘制,绘制白模楼宇 熟悉sprite,用以做POI标注 熟悉光照 熟悉LOD,用以远近不同时加载不同物体.地面 熟悉漫游 ...
- 小程序中使用threeJs渲染3D场景
淘宝小程序中使用threeJs渲染3D场景demo 在做淘宝小程序的项目的时候需要有用到3d场景,然后就想到使用threeJs来做渲染,但是threeJs依据的dom元素在小程序里面是没有的,故而需要 ...
- threejs和3d各种效果的学习
写给即将开始threejs学习的自己,各种尝试,各种记忆.不要怕,灰色的年华终会过去. 一个技术学习的快慢,以及你的深刻程度,还有你的以后遇到这个东西的时候的反应速度,很大程度上,取决于你的博客的深刻 ...
- 基于ThreeJS为3D模型添加贴图
基于ThreeJS为3D模型添加贴图 示例描述与操作指南 示例效果展示 实现方式 示例描述与操作指南 当前示例用于展示如何在三维立体构件六个面上添加贴图 示例效果展示 实现方式 添加贴图:在模型的多个 ...
最新文章
- 字节跳动的面试题.pdf
- uboot移植参考资料
- python3数据类型:Number(数字)
- Xamarin.Forms 5.0 来了
- python回溯方法的模板_Python基于回溯法子集树模板解决0-1背包问题实例
- 企业如何快速响应用户需求 且看云徙“数据+业务”双中台化简为繁
- 事务相关命令 mysql
- [洛谷P3242] [HNOI2015]接水果
- HTTP权威指南读书笔记(一)HTTP概述、URL和资源及报文详解
- UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数
- 完成图书管理系统类图的绘制_中小学图书馆图书管理系统软件
- php json转数组不成功,phpjson转数组出错
- 香农编码、哈夫曼编码、费诺编码的特点、优缺点及应用
- android编程如何调整屏幕亮度,Android编程设置屏幕亮度的方法
- 我的团长我的团第十四集
- Ubuntu安装字体
- Linux系统安全及应用-grub菜单启用密码限制10
- 网络工程师的职业技术要求
- 硬盘主引导记录,主分区表,分区引导记录,分区链表介绍(转)
- 加速微软云服务在中国大陆的连接体验
热门文章
- java assembly_Java技术--maven的assembly插件打包(依赖包归档)
- 嵌入式中的BSP---BSP到底是什么
- ubuntu服务器解除显卡异常占用
- STM32CubeIDE开发(二十九), 如何结合FreeRTOS开发STM32程序
- Excel 字符串拼接
- 思科模拟器:通过对路由器的RIP设置network命令将连在两台路由器上的电脑连通
- js第2章基本语法 课后习题——求出1~100之间的素数、求红白黑球
- String.matches() 与 Matcher.matches() 的区别
- 前端静态资源加载顺序
- Mac-移动硬盘在Mac上能用在Win7不显示