全景图和锚点编辑
今天来简单聊聊threejs全景图和锚点编辑的方案。 全景图也就是所谓的天空盒子,所应用到的场景例如:场景模型的天空背景、夜晚的星空背景、VR看房等~
锚点编辑这篇重点讲一讲锚点编辑,也就是所谓场景编辑的方案。其中思想无限接近于Low Code,说到Low Code!我拖更了四篇文章,由于过年那段时间太忙了,实在是没时间更新!看到许多人都在等我完事,感到十分抱歉,后续一定会整理好更新!
全景图
其实全景图没什么内容。可以想象成一个非常大正方体的盒子,通过六个面的图片衔接而成。而我们相机则是存在于正方体内部,这样就能形成一个视觉误差,认为我们处于场景中。
全景图拆解
以下就是全景图正方体拆解图,六个面互相衔接,可以脑补下当将这个正方体组装后,我们所看到就是一个无缝衔接的一个场景,当然认真看还是可以看出正方体的边界处。
可以把骰子脑补成相机所在的位置,这样就很容易理解
既然有天空盒子,那多个场景的天空盒子肯定存在不同之处。在我们切换场景如何切换对应的天空盒子呢?很简单,我们只需封装一个切换函数如下
// 添加地面和天空盒 Viewer.prototype.changeSkyBox = function (skydir) { const that = this // 创建几何模型 BoxGeometry('x轴', '轴', 'z轴') const geometry = new THREE.BoxGeometry(999, 999, 999) // 创建纹理贴图 前后 上下 左右 const texture0 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/px.jpg`)) const texture1 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/nx.jpg`)) const texture2 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/py.jpg`)) const texture3 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/ny.jpg`)) const texture4 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/pz.jpg`)) const texture5 = new THREE.TextureLoader().load((gAppPath + `/images/ysThree/sky/${skydir}/nz.jpg`)) // 添加材质 const material = [ new THREE.MeshBasicMaterial({color: 0xffddff, map: texture0, side: THREE.DoubleSide}), new THREE.MeshBasicMaterial({color: 0xffddff, map: texture1, side: THREE.DoubleSide}), new THREE.MeshBasicMaterial({color: 0xffddff, map: texture2, side: THREE.DoubleSide}), new THREE.MeshBasicMaterial({color: 0xffddff, map: texture3, side: THREE.DoubleSide}), new THREE.MeshBasicMaterial({color: 0xffddff, map: texture4, side: THREE.DoubleSide}), new THREE.MeshBasicMaterial({color: 0xffddff, map: texture5, side: THREE.DoubleSide}) ] // 创建网格对象 const cube = new _3d.Mesh(geometry, material) cube.layers.enableAll() cube.name = 'skybox' that.skybox && this.scene.remove(that.skybox) that.skybox = cube that.AmbientGroup.add(cube) }
场景编辑方案
不同场景的灯光位置以及图标点位可能不同,作为一个基础编辑平台,我们肯定要考虑如何将我们的场景变成可配置化,根据不同的需求做出不同的改变。之前的文章介绍过我们将模型通过JSON的形式去配置,那我们如何将场景中的点位以及灯光位置做出配置呢?
transformControls
变换控制器,这里它的作用主要是对锚点的平移、缩放、旋转操作
初始化控制器
//移动控制器 this.transformControls = new TransformControls(this.camera, this.renderer.domElement) this.transformControls.setSize(0.5) this.scene.add(this.transformControls) //添加入场景
添加可移动对象
我们可以在比如说灯光类中,添加一个transform_attach方法,在启用编辑后调用该方法。只有attach后才能够被拾取,进行平移、旋转、缩放的操作
transformControls.attach(...) //对象类中 transform_attach(value){ if (value) { this.transformControls.attach(this.dlight) } else { this.transformControls.detach(this.dlight) } }
平移、缩放、旋转
gapp.history.execute 这里主要是业务逻辑,将编辑后的对象保存起来,主要用于回显
SetPositionCommand 这里主要的作用是记录下旧的位置信息与新的位置信息
抛开上面两个方法,其实只要添加进变换控制器就可以对物体就行操作了,下面我单独介绍为什么需要保存信息
Viewer.prototype.bindTransformEvent = function () { this.transformControls.addEventListener('mouseDown', () => { //鼠标拾取到 var object = gapp.transformControls.object //获取拾取对象 this.objectPositionOnDown = object.position.clone()//保存位置 this.objectRotationOnDown = object.rotation.clone()//保存角度 this.objectScaleOnDown = object.scale.clone()//保存缩放大小 gapp.controls.enabled = false }) this.transformControls.addEventListener('mouseUp', () => {//鼠标提起 var object = gapp.transformControls.object//获取拾取对象 if (object !== undefined) { switch (gapp.transformControls.getMode()) { //这里判断是要进行平移、缩放、旋转操作 case 'translate': if (!this.objectPositionOnDown.equals(object.position)) { gapp.history.execute(new SetPositionCommand(this, object, object.position, this.objectPositionOnDown)) } break case 'rotate': if (!this.objectRotationOnDown.equals(object.rotation)) { gapp.history.execute(new SetRotationCommand(this, object, object.rotation, this.objectRotationOnDown)) } break case 'scale': if (!this.objectScaleOnDown.equals(object.scale)) { gapp.history.execute(new SetScaleCommand(this, object, object.scale, this.objectScaleOnDown)) } break } } gapp.controls.enabled = true }) }
保存对象
其实这里和我Low Code的思想很像,就是我们最终要将所有配置好的物体信息保存成一个JSON的形式。以便于在任意其它项目中做回显。怎么回显呢? 假如说我们现在编辑好一个灯光的位置信息以及通过gui调整好的颜色、亮度等,我们可以将该灯光object对象通过toJSON转成JSON后存储于我们最终对象中,后续通过接口取回JSON通过转化重新add到场景中即可
//转化为threejs特有的json格式 scene.toJSON() (了解过json的同学可以发现,threejs为了缩小大小,将瓦片对象最大限度的拆分材质等,通过id关联并保存为json) //解析json转为对象 Viewer.prototype.fromJSON = function (json, layeridx, isRayobj) { return new Promise((resolve, reject) => { // 解析 json 对象 let loader = new THREE.ObjectLoader(); let loadedMesh = loader.parse(json); let scene = this.mergeToMaterialsMap(loadedMesh, true) resolve(scene) }) }
结语
这篇文章主要介绍思想,相对来说这篇文章比较基础,当然后续可以根据自己的要求进行扩展
到此这篇关于threejs全景图和锚点编辑的文章就介绍到这了,更多相关threejs全景图和锚点编辑内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!