尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CityEngine道路规则库实战:从CGA语法到数字孪生道路建模

CityEngine道路规则库实战:从CGA语法到数字孪生道路建模 简介Cityengine 道路规则库是一套面向城市规划、三维建模与视觉模拟场景的规则素材包适合使用 Cityengine 的建模师、规划师和学生快速搭建逼真道路网络。资源共 1247 个文件压缩包约 202.24MB包含 172 个 obj 道路模型、84 个 mtl 材质定义、725 个 jpg 与 134 个 png 贴图文件以及 cga/cej 规则脚本、gdb 地理数据库相关文件可在 ArcGIS 与 Cityengine 工作流中直接调用或二次开发。规则覆盖道路几何、交叉口、附属设施、交通元素等常用维度结合 GIS 数据可生成符合现实尺度的主干道、次干道和支路不同层级的道路拥有各自宽度、车道与人行道配置便于按项目需求快速组合。包内文件还包含大量贴图与三维模型可直接用于场景渲染与视觉表现。已有 1221 人学习下载适合希望借助完整规则库提升建模效率、减少重复手工调整的进阶使用者。1. Cityengine 道路规则库解决的是哪一类高痛点的“修路”需求做城市级数字孪生项目的时候我遇到过最折腾的不是建筑体块而是城市道路。把规划局的矢量路网数据丢进 Cityengine如果不做任何干预引擎只会按默认规则给你生成一条条光秃秃的黑色沥青路面——没有车道分界线、没有斑马线、没有路缘石路口接缝处还会时常出现三角面撕裂。这个时候Cityengine 道路规则库就成了绕不开的解决方案它本质上是一套用 CGA 语法写好的、带着参数和纹理资源的“道路自动化加工说明书”。只要你把路网数据喂给它它就能自动完成车道划分、标线敷设、人行道铺装以及路口衔接甚至能根据道路等级区分双向四车道和社区窄路。这套方案适合做智慧城市底座、规划方案推演以及游戏化城市沙盘的人。接下来这篇笔记我会把自建规则库的完整链路和踩过的坑摊开讲希望能帮你少走几天弯路。2. 认识 CGA 下的道路内核节点、线段、相交的规则形态在动手写代码之前得先把 Cityengine 处理道路的逻辑基座说清楚。道路规则库不是孤立存在的你写出来的每一段 CGA 规则都要被引擎放在“街道图形、路段图形、道路节点”这套底板上执行。2.1 从 Graph 到 Shape规则库真正操作的三个唯度Cityengine 内部对道路网络的管理通常不加区分地称为 Street Network Graph简称 Graph。导入矢量路网后引擎会立即把线要素切分成三类基础 Shape一是代表街道中轴线的 Street Segment二是代表两条或多条道路交汇处的 Intersection Shape三是连接关系里的 End Node 和 Side Node。规则库的核心思路就是对这三类 Shape 分别声明“遇到它们应该调用什么规则”。一个简单的例子是如果你只写了处理 Street Segment 的规则而漏掉了 Intersection Shape 的规则那么所有交叉路口都会退化成默认的“梯形几何体”。这不是玄学而是规则库组织混乱时最常见的现象。从 CGA 语法角度来看常用的规则入口函数有三个// 定义属性决定道路横断面 attr RoadWidth 24 attr SidewalkWidth 4 // 当引擎识别到一个道路路段时会调用这里的规则 StreetSegment -- alignScopeToAxes(y) // 让当前坐标轴与街道走向对齐 s(RoadWidth, 0, scope.sz) // 设置路面的宽度、高度和长度 RoadBody() // 当引擎识别到路网交叉口时会调用这里的规则 Intersection -- s(scope.sx, 0, scope.sz) IntersectionBody()很多刚接触 Cityengine 规则库的人会误以为只要写一套StreetSegment规则就能通吃所有道路。实际踩过坑之后你会发现节点和路段的处理必须分开写甚至交叉口内部还要细分出十字4-Way和丁字3-Way不同分支。判断依据也很直接规则库中的Intersection属性值是字符串引擎会根据节点上连接的路段数量自动切换。2.2 规则文件结构和纹理下载资源池的放置习惯聊完逻辑内核再看文件构建。很多人把规则库理解为单一的一个.cga文件这其实是个偏窄的理解。真正工程化的道路规则库应该是一个包含多个模块的文件夹我的习惯是这样组织的目录层级文件/文件夹作用说明1roads.rpk规则库的打包文件可被 Cityengine 或 ArcGIS Pro 直接导入2assets/存放纹理包含路面沥青、车道标线、人行道砖等3assets/textures从各种渠道下载的贴图资源4rules/存放多个.cga文件按道路等级拆分为主干道、次干道、支路5rules/BaseRoad.cga基础规则文件定义公共参数如车道数量、路缘石高度关于纹理下载这是一个常被新手忽略的资源问题。默认的 Cityengine 材质库里欧式铺装纹理占据了大部分对国内项目的适配性很差。我通常习惯在国内开放的素材站和标准图集里下载或者直接从已有的规划汇报模型库里提取纹理。但有一点值得强调无论从哪下载纹理都要先统一图片分辨率。比如道路标线纹理固定为 512x512人行道铺装固定为 1024x1024这样才能避免后面的 UV 计算时出现比例偏差。这也是我后来把纹理下载行为演进成“纹理池”管理的原因而不是零散地往规则文件里堆路径。3. 从零建一个规则库主干道和步行道的 CGA 代码与参数理解了内核和文件位置接下来就是硬啃代码的环节。很多人问规则库里到底是怎么把一条直线变成“有颜有型”的主干道的这里的核心机制是在局部坐标系里做 XY 平面上的切片再在 Z 轴方向做纵向拉伸。我们把 24 米宽的道路横断面切成“人行道—非机动车道—机动车道—中央分隔带—机动车道—非机动车道—人行道”这样的层次序列。3.1 用 split 操作符生成车道序列最容易让你的规则库跑出效果的操作符是split。它在规则的横断面上按指定长度或比例切分空间。attr RoadWidth 24 attr SidewalkWidth 4 // 道路主体进入横断面切分 RoadBody -- split(z) { ~SidewalkWidth : Sidewalk | 3 : BikeLane | ~3.5 : VehicleLane | 2 : Median | ~3.5 : VehicleLane | 3 : BikeLane | ~SidewalkWidth : Sidewalk }逻辑说明split(z)表示将当前 Shape 的 Z 轴即道路横向宽度方向按照大括号内的规则划分出若干小切片。~SidewalkWidth的波浪线代表这个宽度是“自适应伸缩”如果总宽度减去后面硬性宽度后剩余尺寸分配到这里就会自动调整成为人行道的实际宽度。|符号表示绝对数值分割。参数说明在这个示例中我把非机动车道固定为 3 米机动车道固定为 3.5 米中央分隔带固定为 2 米。实际项目里如果遇到城市快速路VehicleLane需要调整到 3.75 米以上此时只需要改动attr RoadWidth和这一段数值规则库就能自动匹配不同等级的街道不需要重复写代码。3.2 给细分路段绑定纹理注意 UV 坐标系的方向切片完成之后如果直接渲染模型是白色的。我们必须给每个切片绑定具体的纹理同时指定纹理在道路纵向上的平铺次数。Sidewalk -- setupProjection(0, scope.xy, 1, 1) texture(assets/textures/pavement_brick_04.png) color(#C0A080) projectUV(0) VehicleLane -- setupProjection(0, scope.xy, scaleToTexture(10, 2), scaleToTexture(10, 2)) texture(assets/textures/asphalt_dark_02.png) projectUV(0)逻辑说明setupProjection(0, scope.xy, ...)的含义是设置一个 UV 投影平面。这里的0代表纹理坐标索引scope.xy表示我们要把纹理平铺在当前 Shape 的 XY 平面上也就是道路表面。这个操作极其重要如果漏掉它纹理贴图会被默认拉伸到整个路面边缘出现粗糙的马赛克效果。参数说明最后的projectUV(0)是触发投影计算的动作。scaleToTexture(10, 2)是一个高级函数它让纹理的每个小图元在纵向重复 10 次、横向重复 2 次让沥青颗粒感看起来更真实。这个数字不是越大越好通常根据项目最终导出的视口距离来调整。近景逼真的话纵向重复次数可以加大到 20但也会加重显卡负担。3.3 用 Centerline 规则处理道路中心标线道路的中间还会有一条分界线在规则库中是一个独立的小模块。VehicleLane -- split(z) [0.5 : LaneMarking] setupProjection(0, scope.xy, 1, 1) texture(assets/textures/dashed_line_white.png) projectUV(0)这段代码的意思是在每个车道切片靠近中央的那一侧再切出 0.5 米宽的空间独立贴上白色虚线纹理。写到这里可以发现道路规则库的本质就是在反复执行“拆衣服、贴图案”的操作只要分层得当路缘石、隔离墩、路灯底座都可以以这种形式堆叠在横断面上。4. 道路规则库避坑四类常见故障的处理顺序和根因自建规则库最折磨人的不是写不出来而是写完了运行报错或者模型翻车。我在三个城市项目里调度交通路网前后迭代了百余次总结出四个最高发的问题按故障概率排序如下。4.1 翻车点一交叉路口抠出黑洞路面突然塌陷现象当你把规则库套到大型路网图上发现所有十字路口的地方模型中间会出现一个无底洞或者路面穿插进地下只有路段的边缘亮着。原因绝大多数情况是你只写了StreetSegment的规则却没有声明Intersection要怎么处理。引擎对路段剪裁后的开放边界没有闭合指令几何体自然就产生了退化。另外如果交叉路口的Split规则里把路口自身的streetWidth设成了 0也会导致路面断带。解决在规则库主文件开头添加attr IntersectionMode Disabled或显式调用Intersection规则本体。如果是后者确保在交叉口规则里重新生成一次路面面板代码类似Intersection -- s(scope.sx, 0, scope.sz) setupProjection(0, scope.xy, 1, 1) texture(roadTexture) projectUV(0)通常这一行s(...)就能让几何体重新闭合。注意s(scope.sx, 0, scope.sz)意味着高度强制为 0防止路面随地形上下起伏。4.2 翻车点二纹理被拉长成面条沥青图像像宽窄不一现象生成的模型整体看起来没问题但细看某一路段车道线特别粗人行道砖变成大长条放眼望去像是此生不换系列的拉伸图片。原因纹理坐标和道路长度不一致。因为默认的 Cityengine 规则库对 UV 的设定是跟随总长度当路段长宽比悬殊时纹理单胞就被拉成一个极端的长方形。这时候就需要用到前面提到的scaleToTexture或直接给固定重复次数。解决给所有路面纹理统一设置投影基准。不要顺手写setupProjection(0, scope.xy, 1, 1)后就完事。这里的1, 1意思是整个面只贴一遍纹理。对于机动车道改成一个基于米数的定比投影像是setupProjection(0, scope.xy, 5, 5)让纹理每五米平铺一次。这个坑不看现场很难发现因为单看模型不觉得一旦导出为 FBX 后拉近视图瑕疵就特别明显。4.3 翻车点三中央分隔带与双向车流的错位现象双向四车道的规则库生成到地图上后模型中央的隔离带不在正中间向左偏出半米多导致右侧两车道极宽、左侧两车道极窄。视觉上特别别扭。原因因为在split(z)内切分切片时中间用了一个数字固定值例如上文的 2 米两侧的~自适应模式把剩余的微小偏差全部吞掉了。由于~的弹性机制是按比例分配的远端的人行道把偏差吃掉了所以结果偏离。解决把隔离带的宽度也设为~X的形式或者用固定的 fractional 比例。例如设置| 10% : Median这能保证隔离带始终位于横断面总宽 10% 的位置不随总宽度波动而飘移。修改后重新生成模型中线就对齐了。4.4 翻车点四立交和高架的道路“z 轴打架”现象当你在做城市级大场景时高架桥和地面道路使用同一套道路规则库。导出后发现高架车辆穿行到地面以下或者地面道路同时被高架桥的护栏打断。原因规则库里的店面几何体没有强制设定高度偏移量。原始数据中的线要素虽然带有高度字段但如果你的规则是attr RoadHeight 0默认的话引擎就会把所有道路映射到同一个高度层。解决在StreetSegment入口处将道路根据属性字段attr BridgeHeight 0计算实际 z 轴偏移量。按照桥梁段和地面段分开写两个规则块使用case分支选择调用哪个块。StreetSegment -- case BridgeHeight 0: alignScopeToAxes(y) t(0, BridgeHeight, 0) s(RoadWidth, 0, scope.sz) RoadBody() else: alignScopeToAxes(y) s(RoadWidth, 0, scope.sz) RoadBody()这段t(0, BridgeHeight, 0)就是给高架路增高。处理这类问题时我一般建议优先检查管理图层里道路的 z 坐标统计再回看规则库分支不要盲目堆叠偏移量。4.5 避坑专项为什么修改规则参数后城市模型没有变化现象费劲在规则库里改了车道宽度点击生成后模型表面的车道线依然是旧宽度。原因这是最容易让人怀疑自己代码写错的地方。其实绝大部分原因是模型缓存没有失效也就是说旧几何体没有被清理。Cityengine 是基于稀疏空间索引重建的只有当你点击“重置”之后它才会重新计算道路网络涉及的几何节点否则你改的规则属性就像是没有保存进项目的幽灵参数。解决修改规则库参数后点击左侧工具栏中的“Reset”按钮或者在文件菜单下选择“Cities in the Scene”强制清空当前场景内的道路生成缓存。对于大型路网清空后重新生成可能需要几分钟这期间不要做其他操作不然容易触发线程锁。养成“改参数必重置”的习惯能有效减少自我怀疑的时间。5. 把规则库管起来多等级道路属性映射与纹理打包复用当你的城市模型里不仅有大动脉还有毛细血管般的社区小路时单一规则文件很难管理。这一章说说工程化的组织方式以及怎么把纹理资源封装进规则包让团队协作更顺滑。5.1 通过 attribute map 实现道路分级自动化规则库的终局不是“一套规则打天下”而是“一套规则识别天下”。我通常会在规则库存放一个叫StreetClass的属性从外部图层导入属性。这里的关键是理解为数据图层与 CGA 属性之间的映射关系。以常见的矢量路网数据为例数据属性表里往往会有一个highway字段值一般为motorway、trunk、primary、residential等类别。在规则库里把类别映射为尺寸attr StreetClass residential // 根据道路等级动态调整横断面 attr RoadWidth case StreetClass motorway: 33.5 case StreetClass trunk: 28.5 case StreetClass primary: 24.0 else: 18.0 attr LaneCount case StreetClass motorway: 6 case StreetClass trunk: 4 case StreetClass primary: 4 case StreetClass residential: 2 else: 2逻辑说明这个写法比在外部 GIS 软件里给几千条线逐条赋RoadWidth快得多。代码在规则初始化时读取图层的属性值用case表达式实现“分档设参”。当你从 Cityengine 的数据层导入矢量数据时会弹出一个属性映射窗口将高架类型源字段对应到StreetClass目标属性即可完成参数传递。参数说明这个表格可以作为项目规则库的参数参考横幅但在具体使用时要根据当地规划规范进行调整比如日本道路截面的机动车道就很窄国内南方城市也不过是 3.5 米到 3.75 米之间浮动。5.2 将道路规则打包成 RPK 的流程与注意点当规则库文件和assets文件夹里的纹理下载资源越来越多手动分发就容易出现路径丢失。我一般会把整个规则库项目打包成一个.rpk规则包城区的同事可以直接拖拽进场景使用。打包流程通常是这样的在 Cityengine 的 Navigator 视图中选中rules文件夹和assets文件夹的根节点点击右键选择 Export as Rule Package。此时会生成一个.rpk文件。注意打包时不需要把整个 Assets 目录全部塞进去很多底纹边缘效应惊人的贴图其实没有被任何 CGA 引用到。打包完成之后别人在导入.rpk时CGA 代码里的texture(assets/...)路径会自动解析到包内位置不会出现路径断链。这是我特别推荐团队内部采用的方式避免了沟通中路径写错的风险。5.3 关于 Textures 文件夹命名的一个小约束容易翻车的还有纹理文件名。在 CGA 代码中如果文件名出现中文或特殊空格比如人行道砖 VB2.png会导致规则在跨平台环境下识别失败。我一般要求文件命名统一为英文小写加下划线例如sidewalk_tile2.png、lane_marking_white_dash.png。这一点在业务需求紧张的时候特别容易被忽略但一旦出现排查就是一场血泪。把纹理文件放在assets的子目录里同时确保 CGA 引用时用的是相对路径这才是规则库打包跨机器的铁律。6. 校准规则库的最后一公里跟视觉死角斗争的验收习惯一套道路规则库做到第 5 章这个规模功能上已经能支撑不少项目了。但在通用卫星图上还会暴露一些细节问题我习惯用两套方法做最终验收。第一个是用“相机高度巡检法”。我把地图场景切换成俯视视角然后把相机提升到距离地面约 300 米的虚拟高度用快速平移法扫过整个路网。这个高度下车道标线是否对齐、人行道纹路是否“锯齿飞边”会一览无余。一旦扫到标线断裂的地方立刻定位到对应的 CGAsplit切片。大多数情况下都是因为projectUV(0)只投影在中心面上而在路缘石侧面没有投影导致出现黑色边缘。第二个是用“交叉向量检查法”。在规则库里专门设置一个debugIntersection布尔开关正常渲染时输出一个高亮材质给交叉口。这样可以快速检查十字路口的面是否连续。很多纹理在道路上看着正常一到交叉口因为斜切会形成尖锐角度常常导致物理光照渲染时的黑色悬浮。这时我就会在Intersection规则里手动切成多个三角面或者引入额外的Corner规则来平滑接缝。日常我做规则库迭代时有两条死记硬背的习惯一是每次修改 CGA 前先CtrlS备份当前文件避免改坏后旧版不回退二是当天停止开发前必然导出一次.rpk作为快照第二天接着从快照改。在这个方向上没有检查就没有伤害数据反馈是规则库唯一的进化动力。希望这些经验能帮到你少走弯路赶紧给自己的场景套上一套真正合身的道路规则库。本文还有配套的精品资源点击获取
返回列表