首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >将大规模shp白模贴图转3dtiles倾斜摄影,并可单体化拾取建筑

将大规模shp白模贴图转3dtiles倾斜摄影,并可单体化拾取建筑

原创
作者头像
geobuilding
发布2025-11-05 14:58:26
发布2025-11-05 14:58:26
770
举报
文章被收录于专栏:web三维web三维

对于大规模城市场景,在cesium端流行的方法是,使用工具将GIS数据shp建筑白模或geojson白模直接转换成3dtiles格式。然后使用着色器代码对白模贴图,常见的就是渐变色贴图效果。

但是这种贴图效果不能很好的表达真实建筑纹理。这种网格mesh也不能很好表达建筑属性,比如屋顶属性。因为shp直接转换3dtiles并没有屋顶结构。现在面临的问题,客户要求 1、没有无人机倾斜摄影数据,直接基于白模贴图 2、能部分独立贴图,部分批量贴图,贴图要能反应建筑纹理。 3、实现L3级别的mesh结构,包含屋顶结构。 4、能导出3dtiles格式,和地理对齐 5、能在3dtiles中拾取任意楼栋 buffer被拉满了。我们来尝试解决。 下图是上海市60w+建筑数据

其中部分建筑使用了独立的材质贴图,和设置了屋顶结构。如图白色贴图建筑 和 旁边的低矮建筑。

第一步 导出 gltf贴图模型序列文件,延续原有功能

设置导出参数。选择白模批量贴图方案 《实景风城市》。设置draco压缩,模型切片会更小。

第二步,在导出后生成倾斜摄影的索引文件tileset.json文件. 3dtiles是cesium推出的数据标准,支持直接将gltf模型作为切片加载。

同时导出了基于cesium的3dtiles加载和单体化拾取建筑代码 。代码中详细注释了cesium点击事件,楼栋单体化高亮代码。

代码语言:txt
复制
 Cesium.Cesium3DTileset.fromUrl(
                        'tileset.json',
                        {
                            maximumScreenSpaceError: 1.0, // 降低屏幕空间误差,强制高 LOD
                            skipLevelOfDetail: false, // 禁用 LOD 跳跃
                        }
                    ).then(tileset => {
                        _this.viewer.scene.primitives.add(tileset);
                        //模型简单提亮
                        tileset.style = new Cesium.Cesium3DTileStyle({
                            color: "color() * 1.1"
                        });
                        //鼠标点击事件处理
                        const handler = new Cesium.ScreenSpaceEventHandler(_this.viewer.scene.canvas);
                        handler.setInputAction(async function(click) {
                            const pickedObject = _this.viewer.scene.pick(click.position);
                            if(Cesium.defined(pickedObject) && pickedObject['id']&&pickedObject['id']['geojson']){
                                //点击了单体化围墙
                                alert(JSON.stringify(pickedObject['id']['geojson']))
                            }else if (pickedObject  && pickedObject.primitive==tileset && pickedObject.content) {
                                const content = pickedObject.content;
                                const uri = content.url || content._url || content.uri;
                                if (uri) {
                                    // 提取 b3dm 文件名
                                    const b3dmName = uri.split('/').pop().split('?')[0]; // 防缓存参数
                                    var modelinfo = await (await fetch('modelinfo.json')).json();
                                    const worldPoint = _this.viewer.scene.pickPosition(click.position);
                                    pickedObject.primitive.userData = modelinfo[b3dmName];

                                    if (Cesium.defined(worldPoint)) {

                                        //1、获取模型点击的经纬度和高度。
                                        var modellnglat = _this.gltfInstance.GetClickLnglat(_this.viewer, worldPoint, pickedObject)
                                        /*
                                            2、查询点所在的轮廓。
                                            !!!演示文件通过前端来查询点在轮廓内。!!!实际应用中需通过后端查询,避免GIS数据泄漏!
                                            内置属性解释
                                            https://i1.hdslb.com/bfs/article/55509bc62ca58bb6a8463819342258cd98081c25.png@1192w.avif
                                            如果一个建筑体包含多个gis轮廓数据。在Geobuilding软件内对建筑体的gis数据【选择框】-打组。打组后这些gis数据有相同的属性值groupid
                                            根据groupid可找到关联数据
                                         */
                                        let geojson = await (await fetch('geojson/' + pickedObject.primitive.userData.geojson)).text();
                                        let result = geojson.split("\n").map(function(r) {return JSON.parse(r);});
                                        var hitgeo,pointCircle = turf.circle(turf.point([modellnglat.lng, modellnglat.lat]), 1, {units: "meters"});
                                        for (let i = 0; i < result.length; i++) {
                                            //检查点是否在轮廓内。点扩大成圆判断相交,防止边缘点计算误差
                                            if (turf.booleanIntersects(pointCircle, result[i])){
                                                var demheight = result[i].properties.demheight;
                                                var clickheight = modellnglat.height - demheight;
                                                if(clickheight>= result[i].properties.pfh*result[i].properties.minfloor && clickheight<= result[i].properties.roofHeight+result[i].properties.pfh*result[i].properties.minfloor + (result[i].properties.wfh*result[i].properties.floor)){
                                                    hitgeo = result[i]
                                                    break;
                                                }
                                            }
                                        }
                                        if(!hitgeo) {
                                            console.log("没有找到轮廓")
                                            return;
                                        }

                                        //原始geojson数据
                                        //alert(JSON.stringify(hitgeo))

                                        //3、对hitgeo并进行偏移转换(相对于模型)。
                                        hitgeo = _this.gltfInstance.TransFeature(hitgeo, pickedObject);

                                        //4、将geojson转换成cesium世界坐标,添加单体化高亮围墙。
                                        var wallpos = turf.buffer(hitgeo, 1, {
                                            units: "meters"
                                        }).geometry.coordinates[0].map(function(z) {
                                            return Cesium.Cartesian3.fromDegrees(z[0], z[1], hitgeo.properties.gltfbheight)
                                        })

                                        if (wall) wall.remove();
                                        wall = new WallObject.FlowWall(_this.viewer,wallpos,{
                                                copyright:'geobuilding',
                                                wallHeight: hitgeo.properties.wfh*hitgeo.properties.floor,
                                                wallColor: Cesium.Color.fromCssColorString('rgba(0, 255, 26, 0.3)'),
                                                duration: 1000,
                                                materialType: 3,
                                            }
                                        );
                                        wall.flowWallEntity.geojson = hitgeo;
                                    }else {
                                        console.log('无法获取点击点的世界坐标');
                                    }
                                }
                            }
                        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
                    });

打开demo4查看效果是否符合预期。白模有了更丰富的贴图。

拉近查看自定义贴图部分。可以看到材质和屋顶都准确复现。贴图包含了底商+楼面+楼面顶端+屋顶。

当点击场景中的楼栋时,建筑高亮。

和地理引擎完美对齐

基于高性能mesh结构,经过测试加载60w+贴图建筑也能丝滑加载浏览,游刃有余。 现在我们成功实现了L3级别的数字城市效果,并且支持单体化操作,可应用于城市精细化治理,比如网格化,一标三实,以房找人等场景。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档