Ceisum三维场景demo如何实现高级互动体验?

2026-04-28 15:592阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1541个文字,预计阅读时间需要7分钟。

Ceisum三维场景demo如何实现高级互动体验?

技术栈资源:EarthSDK(基于Cesium二次开发包)+ 底图:ArcGIS公开地图服务 + 楼层白膜:自有shape数据,使用CesiumLab切片生成tileset.json和glb模型 + 资源管理器:window资源管理器自带提取 + 车行效率效果 + 添加“‘

技术栈&资源
  • 框架:EarthSDK(基于cesium二次开发包)
  • 底图:Arcgis公开地图服务
  • 楼栋白膜:自有shape数据,使用CesiumLab切片为tileset.json
  • glb模型:window资源管理器自带提取
汽车行驶效果

添加车辆模型

let position = [1.998931022772786, 0.6645500890446009, 0] //添加车辆 var json = { "czmObject": { "xbsjType": "Model", "url": "/assets/glb/delivery.glb", "minimumPixelSize": 128, "maximumScale": 20, "viewDistance": 500, // "maximumScale": 500, // "viewDistance": 1000, "xbsjPosition": position, }, "ref": "model1", } //添加到场景 this._earth.sceneTree.root.children.push(json) 添加车辆行驶路径

let path = [ [1.9996593978198705, 0.6643798949112332, 0], [1.999667332682519, 0.6643399484248445, 0], [1.999661980266788, 0.6643084183139442, 0], [1.9996677912020882, 0.6642558052776021, 0], [1.9996703854084483, 0.6642032457083487, 0], ........ ] let json_path = { "ref": 'path1', "czmObject": { "xbsjType": "Path", "positions": path, "rotations": [ [0, 0, 0] ], "currentSpeed": 50,//当前播放速度,单位为米 "alwaysAlongThePath": true,//始终沿着路径方向运动 "show": false, // 显示路径 "loop": true, // 是否为环线 "playing": true, // 飞行 // 是否循环播放 // 如果为false,则playing设置为true时,会从当前位置播放到最后一个关键点,并停止播放,此时playing属性会自动变成false。 若此属性为true时,播放到最后一个关键点以后,将自动重第一个关键点继续播放。 "loopPlay": true, "cameraAttached": false// 相机绑定 } } this._earth.sceneTree.root.children.push(json_path) 绑定车辆和路径,让车辆在路径上按照一定速度行驶

let model1 = this._earth.sceneTree.$refs.model1.czmObject; let path1 = this._earth.sceneTree.$refs.path1.czmObject; this._uw1 = XE.MVVM.watch(path1, 'currentPosition', position => { model1.xbsjPosition = [...position]; }); 其他

实例代码仅供参考,还需考虑以下问题,

Ceisum三维场景demo如何实现高级互动体验?

  • 车辆的车头方向和即将进行路线一致
  • camera视角绑定
  • 车辆头部指向性箭头的绑定
楼栋分层效果

准备数据
  1. 自有楼栋面数据,且必须有楼高或层数信息
  2. 使用arcmap或qgis等数据处理工具,给shp添加一个字段,字段内容为面的顶点信息coordinates,要根据面的顶点去构造每层的面
  3. 使用cesiumLab的矢量楼块切片功能进行shp切片
  4. 切片完成后,在cesiumLab的服务界面中,可以看到刚刚进行切片的url访问路径,或者处理结果放到自己的web服务器下进行访问
  5. 思路
    1. 双击建筑物后隐藏当前建筑物
    2. 读取建筑物的顶点和层数属性,构造层数个面,每层间隔为楼高
    3. 生成楼层标注,1F , 2F , 3F…
    4. 双击其他建筑物,还原上一个建筑物的 1 , 2, 3 步骤,再重新执行当前建筑物的 1 , 2, 3 步骤
加载建筑白膜

//动态效果 var fsBody = ` // 可以修改的参数 // 注意shader中写浮点数是,一定要带小数点,否则会报错,比如0需要写成0.0,1要写成1.0 float _baseHeight = 0.0; // 物体的基础高度,需要修改成一个合适的建筑基础高度 float _heightRange = 20.0; // 高亮的范围(_baseHeight ~ _baseHeight + _heightRange) 默认是 0-60米 float _glowRange = 120.0; // 光环的移动范围(高度) // 建筑基础色 float vtxf_height = v_elevationPos.z - _baseHeight; float vtxf_a11 = fract(czm_frameNumber / 120.0) * 3.14159265 * 2.0; float vtxf_a12 = vtxf_height / _heightRange + sin(vtxf_a11) * 0.1; gl_FragColor *= vec4(vtxf_a12, vtxf_a12, vtxf_a12, 1.0); // 动态光环 float vtxf_a13 = fract(czm_frameNumber / 360.0); float vtxf_h = clamp(vtxf_height / _glowRange, 0.0, 1.0); vtxf_a13 = abs(vtxf_a13 - 0.5) * 2.0; float vtxf_diff = step(0.005, abs(vtxf_h - vtxf_a13)); gl_FragColor.rgb += gl_FragColor.rgb * (1.0 - vtxf_diff); `; this._earth.sceneTree.root.children.push({ "ref": "tileset_sjz_building", "czmObject": { "xbsjType": "Tileset", "name": "楼栋白膜", "url": "localhost/building3d-tiles/tileset.json", "xbsjStyle": "var style = {\n color: \"vec4(0, 0.5, 1.0,1)\"\n}",//颜色 "xbsjClippingPlanes": {}, "xbsjCustomShader": {//光环上下的动态效果 "fsBody": fsBody, } } }) 楼栋分层

//双击事件,建筑物创建切片 var _that = this this._earth._viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) { //判断是否为3DTileFeature对象 if (pickedFeature !== undefined && pickedFeature instanceof Cesium.Cesium3DTileFeature) { //隐藏当前对象,并且创建楼层切片 pickedFeature.show = false; //coordinate 和 floor 即为顶点和层高信息 var res = _that.createBuildingLevelImg(pickedFeature.getProperty("coordinate"), pickedFeature.getProperty("floor")); } }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); //创建楼层切片 createBuildingLevelImg(coordinate, level) { var l = parseFloat(level); var positions = this.PolygonWktToArray(coordinate); var x_min = positions[0]; var y_min = positions[1]; for (var i = 0; i < positions.length; i++) { if (i % 2 == 0) { if (x_min > positions[i]) { x_min = positions[i] } } else { if (y_min > positions[i]) { y_min = positions[i] } } } //设置材质 var material = new Cesium.Material({ fabric: { type: 'Color', //Image uniforms: { color: new Cesium.Color(30 / 255, 144 / 255, 1.0, 0.5) //imgUrl color } } }) var primitives = []; var indexs = [] //循环生成GeometryInstance,高度累乘 for (i = 1; i <= l; ++i) { let height = 0 + 3 * (i - 1); var t = ""; if (t < 10) { t = i; } else { t = ("" + i).split('').join(' ') } // 添加层数 1F、2F、3F、4F....... this._earth._viewer.entities.add({ id: "building_label_" + i, position: Cesium.Cartesian3.fromDegrees(x_min, y_min, height), label: { text: t + " F", verticalOrigin: Cesium.VerticalOrigin.BASELINE, font: "12px sans-serif", pixeloffset: new Cesium.Cartesian2(300.0, 100.0), scale: 0.2, horizontalOrigin: Cesium.HorizontalOrigin.RIGHT, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 2000), scaleByDistance: new Cesium.NearFarScalar(100, 10, 2000, 1), }, }); // 添加面 var primitive = new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.PolygonGeometry({ polygonHierarchy: new Cesium.PolygonHierarchy( Cesium.Cartesian3.fromDegreesArray(positions) ), vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT, height: height }), id: "building_level_" + i }), appearance: new Cesium.EllipsoidSurfaceAppearance({ material: material, aboveGround: true, //显示背面 }) }); var index = this._earth._viewer.scene.primitives._primitives.length; this._earth._viewer.scene.primitives.add(primitive, index) primitives.push(primitive); indexs.push(index); } } 其他

  • 双击事件需要考虑
  1. 判断双击对象是否为3DTileFeature对象
  2. 标注和面通过ID前缀进行分组,一批操作里的ID前缀一致( 个人拙技,只能想到通过此行为区分 )
  3. 是否只允许一个对象进行切片,若不允许,需要把上一次双击的对象( 3DTileFeature 和 楼层面 )显示状态还原。
  4. 分层后,面的点击事件,例如:点击选中为红色,鼠标经过为黄色
  5. 退出按钮:和(3)一致,即把双击的对象( 3DTileFeature 和 楼层面 )显示状态还原。
  6. 3DTile对象也分level,在level5下双击建筑物进行了建筑物分层后,若缩放至level6,建筑物则又会重新显示,此时需要记录点击的位置,监听视角变化(类似的事件都可以),当变化时,重新判定双击处是否有新的3DTileFeature生成,若有,则继续隐藏。
广告牌、Label、光线效果

光柱、DIV跟随且超过距离隐藏

本文共计1541个文字,预计阅读时间需要7分钟。

Ceisum三维场景demo如何实现高级互动体验?

技术栈资源:EarthSDK(基于Cesium二次开发包)+ 底图:ArcGIS公开地图服务 + 楼层白膜:自有shape数据,使用CesiumLab切片生成tileset.json和glb模型 + 资源管理器:window资源管理器自带提取 + 车行效率效果 + 添加“‘

技术栈&资源
  • 框架:EarthSDK(基于cesium二次开发包)
  • 底图:Arcgis公开地图服务
  • 楼栋白膜:自有shape数据,使用CesiumLab切片为tileset.json
  • glb模型:window资源管理器自带提取
汽车行驶效果

添加车辆模型

let position = [1.998931022772786, 0.6645500890446009, 0] //添加车辆 var json = { "czmObject": { "xbsjType": "Model", "url": "/assets/glb/delivery.glb", "minimumPixelSize": 128, "maximumScale": 20, "viewDistance": 500, // "maximumScale": 500, // "viewDistance": 1000, "xbsjPosition": position, }, "ref": "model1", } //添加到场景 this._earth.sceneTree.root.children.push(json) 添加车辆行驶路径

let path = [ [1.9996593978198705, 0.6643798949112332, 0], [1.999667332682519, 0.6643399484248445, 0], [1.999661980266788, 0.6643084183139442, 0], [1.9996677912020882, 0.6642558052776021, 0], [1.9996703854084483, 0.6642032457083487, 0], ........ ] let json_path = { "ref": 'path1', "czmObject": { "xbsjType": "Path", "positions": path, "rotations": [ [0, 0, 0] ], "currentSpeed": 50,//当前播放速度,单位为米 "alwaysAlongThePath": true,//始终沿着路径方向运动 "show": false, // 显示路径 "loop": true, // 是否为环线 "playing": true, // 飞行 // 是否循环播放 // 如果为false,则playing设置为true时,会从当前位置播放到最后一个关键点,并停止播放,此时playing属性会自动变成false。 若此属性为true时,播放到最后一个关键点以后,将自动重第一个关键点继续播放。 "loopPlay": true, "cameraAttached": false// 相机绑定 } } this._earth.sceneTree.root.children.push(json_path) 绑定车辆和路径,让车辆在路径上按照一定速度行驶

let model1 = this._earth.sceneTree.$refs.model1.czmObject; let path1 = this._earth.sceneTree.$refs.path1.czmObject; this._uw1 = XE.MVVM.watch(path1, 'currentPosition', position => { model1.xbsjPosition = [...position]; }); 其他

实例代码仅供参考,还需考虑以下问题,

Ceisum三维场景demo如何实现高级互动体验?

  • 车辆的车头方向和即将进行路线一致
  • camera视角绑定
  • 车辆头部指向性箭头的绑定
楼栋分层效果

准备数据
  1. 自有楼栋面数据,且必须有楼高或层数信息
  2. 使用arcmap或qgis等数据处理工具,给shp添加一个字段,字段内容为面的顶点信息coordinates,要根据面的顶点去构造每层的面
  3. 使用cesiumLab的矢量楼块切片功能进行shp切片
  4. 切片完成后,在cesiumLab的服务界面中,可以看到刚刚进行切片的url访问路径,或者处理结果放到自己的web服务器下进行访问
  5. 思路
    1. 双击建筑物后隐藏当前建筑物
    2. 读取建筑物的顶点和层数属性,构造层数个面,每层间隔为楼高
    3. 生成楼层标注,1F , 2F , 3F…
    4. 双击其他建筑物,还原上一个建筑物的 1 , 2, 3 步骤,再重新执行当前建筑物的 1 , 2, 3 步骤
加载建筑白膜

//动态效果 var fsBody = ` // 可以修改的参数 // 注意shader中写浮点数是,一定要带小数点,否则会报错,比如0需要写成0.0,1要写成1.0 float _baseHeight = 0.0; // 物体的基础高度,需要修改成一个合适的建筑基础高度 float _heightRange = 20.0; // 高亮的范围(_baseHeight ~ _baseHeight + _heightRange) 默认是 0-60米 float _glowRange = 120.0; // 光环的移动范围(高度) // 建筑基础色 float vtxf_height = v_elevationPos.z - _baseHeight; float vtxf_a11 = fract(czm_frameNumber / 120.0) * 3.14159265 * 2.0; float vtxf_a12 = vtxf_height / _heightRange + sin(vtxf_a11) * 0.1; gl_FragColor *= vec4(vtxf_a12, vtxf_a12, vtxf_a12, 1.0); // 动态光环 float vtxf_a13 = fract(czm_frameNumber / 360.0); float vtxf_h = clamp(vtxf_height / _glowRange, 0.0, 1.0); vtxf_a13 = abs(vtxf_a13 - 0.5) * 2.0; float vtxf_diff = step(0.005, abs(vtxf_h - vtxf_a13)); gl_FragColor.rgb += gl_FragColor.rgb * (1.0 - vtxf_diff); `; this._earth.sceneTree.root.children.push({ "ref": "tileset_sjz_building", "czmObject": { "xbsjType": "Tileset", "name": "楼栋白膜", "url": "localhost/building3d-tiles/tileset.json", "xbsjStyle": "var style = {\n color: \"vec4(0, 0.5, 1.0,1)\"\n}",//颜色 "xbsjClippingPlanes": {}, "xbsjCustomShader": {//光环上下的动态效果 "fsBody": fsBody, } } }) 楼栋分层

//双击事件,建筑物创建切片 var _that = this this._earth._viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) { //判断是否为3DTileFeature对象 if (pickedFeature !== undefined && pickedFeature instanceof Cesium.Cesium3DTileFeature) { //隐藏当前对象,并且创建楼层切片 pickedFeature.show = false; //coordinate 和 floor 即为顶点和层高信息 var res = _that.createBuildingLevelImg(pickedFeature.getProperty("coordinate"), pickedFeature.getProperty("floor")); } }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); //创建楼层切片 createBuildingLevelImg(coordinate, level) { var l = parseFloat(level); var positions = this.PolygonWktToArray(coordinate); var x_min = positions[0]; var y_min = positions[1]; for (var i = 0; i < positions.length; i++) { if (i % 2 == 0) { if (x_min > positions[i]) { x_min = positions[i] } } else { if (y_min > positions[i]) { y_min = positions[i] } } } //设置材质 var material = new Cesium.Material({ fabric: { type: 'Color', //Image uniforms: { color: new Cesium.Color(30 / 255, 144 / 255, 1.0, 0.5) //imgUrl color } } }) var primitives = []; var indexs = [] //循环生成GeometryInstance,高度累乘 for (i = 1; i <= l; ++i) { let height = 0 + 3 * (i - 1); var t = ""; if (t < 10) { t = i; } else { t = ("" + i).split('').join(' ') } // 添加层数 1F、2F、3F、4F....... this._earth._viewer.entities.add({ id: "building_label_" + i, position: Cesium.Cartesian3.fromDegrees(x_min, y_min, height), label: { text: t + " F", verticalOrigin: Cesium.VerticalOrigin.BASELINE, font: "12px sans-serif", pixeloffset: new Cesium.Cartesian2(300.0, 100.0), scale: 0.2, horizontalOrigin: Cesium.HorizontalOrigin.RIGHT, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 2000), scaleByDistance: new Cesium.NearFarScalar(100, 10, 2000, 1), }, }); // 添加面 var primitive = new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.PolygonGeometry({ polygonHierarchy: new Cesium.PolygonHierarchy( Cesium.Cartesian3.fromDegreesArray(positions) ), vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT, height: height }), id: "building_level_" + i }), appearance: new Cesium.EllipsoidSurfaceAppearance({ material: material, aboveGround: true, //显示背面 }) }); var index = this._earth._viewer.scene.primitives._primitives.length; this._earth._viewer.scene.primitives.add(primitive, index) primitives.push(primitive); indexs.push(index); } } 其他

  • 双击事件需要考虑
  1. 判断双击对象是否为3DTileFeature对象
  2. 标注和面通过ID前缀进行分组,一批操作里的ID前缀一致( 个人拙技,只能想到通过此行为区分 )
  3. 是否只允许一个对象进行切片,若不允许,需要把上一次双击的对象( 3DTileFeature 和 楼层面 )显示状态还原。
  4. 分层后,面的点击事件,例如:点击选中为红色,鼠标经过为黄色
  5. 退出按钮:和(3)一致,即把双击的对象( 3DTileFeature 和 楼层面 )显示状态还原。
  6. 3DTile对象也分level,在level5下双击建筑物进行了建筑物分层后,若缩放至level6,建筑物则又会重新显示,此时需要记录点击的位置,监听视角变化(类似的事件都可以),当变化时,重新判定双击处是否有新的3DTileFeature生成,若有,则继续隐藏。
广告牌、Label、光线效果

光柱、DIV跟随且超过距离隐藏