
简介本资源是面向城市规划师、三维建模工程师及CityEngine初高级用户的道路规则库实战套件专为解决复杂城市路网快速建模与风格统一难题而设计。包内含1247个文件总计202.24MB涵盖725张道路纹理与示意JPG/PNG图、172个带材质的道路OBJ模型、84个MTL材质定义文件、34个ArcGIS地理索引ATX文件以及18个CGB/CGA规则脚本和1个完整CityEngine项目工程.cej支持直接导入使用或二次开发。已有1178人学习下载充分验证其在实际项目中的可用性与稳定性。用户可即刻调用预设的几何生成、交叉口逻辑、车道分级、附属设施人行道/绿化带/标线及视觉样式规则结合GIS数据驱动建模同时通过CGA脚本与GDB地理表结构深入理解规则与空间数据的耦合机制大幅提升从概念方案到高保真可视化落地的效率。 StreetEngine 道路规则库说白了就是一套用 CGAComputer Generated Architecture规则语言写好的“道路生成配方”它把道路建模从“手动一点点拉模型”变成“写规则自动批量生成”。你给 StreetEngine 一条中心线、一个断面模板规则库就能自动把车道、人行道、路缘石、标线、路灯、树木全部“长”出来。而且这套配方是参数化的改一个宽度值整条路的模型就跟着变不需要重做。这篇文章更适合谁看如果你在用 StreetEngine 做城市级大场景建模或者想从零搭一套自己的道路规则库又或者只是想知道 CGA 规则里那些路网相关的函数到底怎么用那这篇文章应该能帮你少走不少弯路。我会把规则库的完整结构、每个模块的实现思路、以及实际调试中踩过的坑都摊开来讲尽量让小白也能跟着动手搭一套自己的道路规则。1. 道路规则库的整体设计与拆解思路1.1 为什么需要“道路规则库”而不是手动建模先聊一个最基础的问题StreetEngine 里已经有了手动建模工具为什么还要搞规则库我自己的体会是手动建模适合做“单条样板路”也就是你只需要一两条特别精细、要拿去渲染特写的道路但一旦进入城市级批量生成阶段道路里程可能上百公里手动建模就完全撑不住了。规则库的核心优势在于三点。第一是批量一条规则可以同时作用于整个路网的所有路段不管你导入的是 10 条路还是 1000 条路生成逻辑完全一致。第二是参数化道路宽度、车道数、人行道宽度、路缘石高度这些全部变成规则文件顶部的变量改参数等于改整座城市不需要一条路一条路去翻模型。第三是可复用一套规则库做出来换一个城市的数据只要坐标系和数据格式对得上直接套用顶多调整参数。还有一个很多人忽略的点规则库让模型“活”了。手动建的模型是静态几何体改一处要手动改多处规则库生成的模型每次重新生成都会按照最新的规则和属性重新计算天然适合多方案比选和迭代。1.2 规则库在 StreetEngine 中的运行逻辑要搭规则库先得弄明白 StreetEngine 里道路建模的基本逻辑。它跟普通 CGA 建模不太一样普通建筑规则处理的是 Shape而道路规则处理的是 Graph Segments——也就是路网线段。我简单梳理一下这条链路第一步导入路网数据。常见的做法是把 OpenStreetMap、ArcGIS 里的路网数据导出成 Shapefile 或者 Geodatabase然后通过 StreetEngine 的“Import”功能导入。数据导入后路网会变成由中心线和交叉口组成的拓扑网络。第二步给路段指定规则。你可以给整个路网统一指定一套规则也可以根据道路等级高速、主干道、次干道、支路分别指定不同的规则文件。第三步StreetEngine 会自动把每条路段切分成 Segment然后 Segment 会沿着中心线方向extrude出道路几何体。第四步规则代码里通过Lane、Sidewalk、Street这些函数组合出道路剖面再通过Crossing函数处理交叉口。这里有一个很关键的点StreetEngine 处理的不是“面”而是“线”。它先把中心线变成有宽度的 Street Shape然后你在 Street Shape 这个 Scope 下做细分。理解这一点后面写规则就不会晕。1.3 道路规则库的标准目录结构我在实际项目里习惯把道路规则库拆成多个文件而不是全部堆在一个.cga文件里。这样做的好处是规则可以分开维护、分开测试、多人协作时不打架。下面这套目录结构是我多次调整后觉得比较顺手的规则库根目录/ ├── road_rule.cga # 总入口规则根据道路属性分发 ├── assets/ │ ├── road_textures/ # 路面纹理 │ ├── markings/ # 交通标线纹理 │ └── props/ # 路灯、护栏等模型 ├── modules/ │ ├── lane.cga # 机动车道规则 │ ├── sidewalk.cga # 人行道规则 │ ├── curb.cga # 路缘石规则 │ ├── crossing.cga # 交叉口规则 │ ├── vegetation.cga # 行道树规则 │ └── streetlight.cga # 路灯规则 └── config/ └── parameters.cga # 全局参数所有尺寸统一管road_rule.cga是整个规则库的总入口。它在执行时拿到路段的属性比如streetType字段然后判断该走哪套分支。parameters.cga则是所有全局变量比如LANE_WIDTH 3.5这样各个模块引用变量而不是魔法数字后期改起来非常方便。2. 核心模块拆解车道、人行道、路缘石与附属设施2.1 机动车道模块车道的拆分与纹理映射机动车道是整个道路规则库里最简单的模块但里面有几个细节值得注意。先看一段基础的 CGA 代码// lane.cga // 默认单条车道宽度 3.5m双向 4 车道 StartRule Lane -- split(u) { ~1 : LanePiece }* LanePiece -- extrude(0.2) setupProjection(0, scope.xy, 3.5, 0.2) texture(assets/road_textures/asphalt.jpg) projectUV(0)这段代码做的事情很直白把一条 Street Shape 沿着横向split成等宽的多条车道然后挤出 0.2 米厚作为路面层再贴沥青纹理。不过实际项目中车道宽度不应该这样固定。现实中车道宽度跟道路等级强相关快速路车道 3.75 米普通城市道路 3.5 米支路可能只有 3 米。所以更好的做法是在参数文件里定义变量// parameters.cga LANE_WIDTH 3.5 // 标准车道宽度米 CURB_WIDTH 0.2 // 路缘石宽度米 SIDEWALK_WIDTH 4.0 // 人行道宽度米然后在 Lane 规则里引用变量Lane -- split(u) { ~LANE_WIDTH : LanePiece }*这样改宽度就只需改一个地方。还有个小技巧不同等级道路的纹理也可以做区分。主干道用新的沥青纹理支路用稍微旧一点的纹理代码里可以通过streetType属性做条件判断。2.2 人行道与路缘石如何让道路剖面有层次道路剖面不只是马路中间那一块它还包括人行道和路缘石。路缘石虽然不起眼但在视觉上能把道路和建筑底商区分开是提升场景真实感的关键元素。人行道和路缘石的组合逻辑一般是这样的先通过 Street Shape 的细分把道路分成“车行道”和“人行道”两部分然后分别生成。我常用的写法是Street -- Lane Sidewalk Sidewalk -- extrude(SIDEWALK_HEIGHT) split(x) { CURB_WIDTH : Curb | ~1 : SidewalkSurface }路缘石的生成逻辑就是在人行道的外缘分出一条细长条然后挤出不同的高度贴上深灰色石材纹理。细节上路缘石的高度一般在 0.15 米左右材料适合用带防滑纹的混凝土材质这样在近距离渲染时才不会显得假。人行道的贴图和道路的贴图不一样最好在规则里单独指定纹理SidewalkSurface -- setupProjection(0, scope.xy, 2.0, 2.0) texture(assets/road_textures/sidewalk.jpg) projectUV(0)贴图尺寸也要根据实际人行道板的尺寸调整一般城市人行道砖的规格是 0.6 米 x 0.6 米或者 1 米 x 1 米贴图平铺尺寸跟这个保持一致视觉效果才自然。2.3 交通标线标线怎么贴才不会拉伸变形交通标线是道路规则库里一个容易翻车的点。很多新手做标线时直接拿一张标线纹理贴在路面上结果路一弯曲或者宽度一变标线就被拉伸得不成样子。正确的做法是给标线单独做一个Marking层并且要控制好纹理的平铺参数和方向。中心黄虚线、车道白虚线、停止线这些都要用不同的规则去生成。我举个例子双向车道中间的黄色虚线Markings -- split(u) { 3.0 : Marking_Dash | 4.0 : Marking_Gap }* Marking_Dash -- extrude(0.01) setupProjection(0, scope.xy, 1, 1) centerUV(x, 0) texture(assets/markings/yellow_line.png) projectUV(0)注意centerUV(x, 0)这一行它的作用是把纹理在水平方向上居中这样不管车道总宽度怎么变虚线都在车道正中。这个函数在道路标线规则里几乎是标配一定要记住。如果是双黄线可以把两条线分别用offset偏移后再生成如果是停止线就把它摆在交叉口入口处垂直于行车方向。标线纹理的尺寸一般要按实际长度来虚线段 3 米、间隔 4 米是比较标准的高速公路参数城市道路略有不同可以按规范调整。2.4 交叉口处理自动生成斑马线和停止线交叉口是道路规则库里最复杂的部分因为交叉口不再是简单的“一条路”而是多条路交汇。StreetEngine 对交叉口的处理有一套专门的机制——Crossing 规则。交叉口规则会在路网相交处生成一个交叉口 Shape你可以在这个 Shape 上单独绘制斑马线、停止线、转弯导流线等。如果规则没有特殊处理交叉口默认情况下两条路相交时会出现路面重叠或断面的问题看起来非常不专业。我通常的交叉口规则会做三件事Crossing -- Crosswalk StopLine Crosswalk -- s(8, 1, scope.sz) t(0, 0.01, -5) i(assets/props/crosswalk.obj)斑马线我用的是一个带黑白条纹的模型尺寸按实际斑马线标准做宽度 8 米、长度根据道路宽度定然后平移到路口入口的位置。停止线则是一根白色横条放在斑马线前大概 2 米的位置。交叉口规则的一个注意事项不同交叉口形态十字、T 字、不规则出来的 Shape 几何不一样规则必须对origin交叉口 Shape做兜底处理避免在某些特殊角度时出现模型穿插。2.5 附属设施路灯、行道树与护栏的自动布置道路两边只有路面和标线是不够的路灯、行道树、护栏这些附属设施能快速提升场景的“城市感”。它们的逻辑其实非常简单——沿道路中心线按固定间距排布。以路灯为例间距一般 30 米到 40 米道路两侧交替排列。CGA 里可以用split(u)配合r旋转和t平移来实现StreetlightRow -- split(u) { ~35 : StreetlightInstance }* StreetlightInstance -- t(0, 0, 3.0) r(0, 180, 0) i(assets/props/streetlight.obj)行道树的逻辑类似但间距会小一些通常是 6 到 8 米一棵。这里有一个性能上的建议在大场景中附属设施的模型面数不宜过高否则整体渲染压力会非常大。我一般在 StreetEngine 里先生成带附件的“完整版本”确认效果后再生成一个不带路灯和树木的“基础版本”用于大场景集成这样能节省大量资源。3. 实操过程从零搭建一套道路规则库3.1 数据准备路网数据要从哪里来搭建规则库之前第一步是搞定路网数据。如果你有 ArcGIS 或者 QGIS 的基础可以用它们导出 Shapefile如果没有也可以直接从 OpenStreetMap 下载路网数据然后用 ArcGIS Pro 的Export Features功能转成 StreetEngine 能识别的格式。这里有个经验路网数据必须要有道路等级属性。Osm 里常见的字段是highway等于motorway、trunk、primary、secondary、tertiary、residential等StreetEngine 导入后会把这些字段作为 Shape 的 attribute。有了这个规则里才能区分主干道和支路分别套用不同的参数。如果你手上没有带属性的路网也有变通方案在 StreetEngine 导入时手动给不同图层指定不同的规则。但这样可维护性差后期加路网还得手动指定不如一开始就把属性弄好。3.2 在 StreetEngine 中导入路网并生成初始模型数据准备好后在 StreetEngine 中的操作步骤如下新建一个 Scene坐标系选择跟路网数据一致的坐标系这点很重要坐标系不一致会导致路网偏移到外太空。点击菜单栏的Layer-Add Layer-Shapefile选择你准备好的路网数据。导入后选中路网图层右键选择Assign Rule File指定刚才建的road_rule.cga。如果规则写好了场景里会立刻出现带纹理的道路模型如果没出现检查一下规则文件有没有语法错误。我第一次做道路规则时在Assign Rule File之后等了半分钟没有任何反应后来发现是规则文件里引用的纹理路径写错了。这里建议把所有的资源路径都用相对路径避免换电脑或者换项目时找不到贴图。3.3 参数文件与主规则文件的分工与联调在调试阶段我会把parameters.cga里的参数逐个修改看场景中的模型变化快速验证规则是否生效。比如把LANE_WIDTH从 3.5 改成 3.2如果道路宽度立刻变化说明规则引用参数的链路是通畅的。主规则文件的写法要考虑到属性的分发。我一般这样设计逻辑StartRule Road -- case streetType motorway: MotorwayDesign case streetType primary: PrimaryRoadDesign case streetType residential: ResidentialRoadDesign else: DefaultRoadDesign这样每条路会根据自身的属性走不同的生成逻辑。不同等级的道路可以拥有不同的车道数量、人行道宽度和附属设施密度。3.4 编码实现人行道、车道与分隔带的完整规则下面给出一套简化的完整示例大家可以直接复制这一个文件配上对应贴图和模型路径就能跑通StartRule Street -- Lane Markings Sidewalk StreetlightRow Lane -- split(u) { ~LANE_WIDTH : LanePiece }* LanePiece -- extrude(0.2) setupProjection(0, scope.xy, 3.5, 0.2) texture(assets/road_textures/asphalt.jpg) projectUV(0) Markings -- split(u) { 3.0 : Marking_Dash | 4.0 : Marking_Gap }* Marking_Dash -- extrude(0.01) setupProjection(0, scope.xy, 1, 1) centerUV(x, 0) texture(assets/markings/yellow_line.png) projectUV(0) Sidewalk -- extrude(0.15) split(x) { 0.2 : Curb | ~1 : SidewalkSurface } Curb -- setupProjection(0, scope.xy, 1, 1) texture(assets/road_textures/curb.jpg) projectUV(0) SidewalkSurface -- setupProjection(0, scope.xy, 1, 1) texture(assets/road_textures/sidewalk.jpg) projectUV(0) StreetlightRow -- split(u) { ~35 : StreetlightInstance }* StreetlightInstance -- t(0, 0, 3.0) r(0, 180, 0) i(assets/props/streetlight.obj)这只是最简版本真正项目里会复杂很多比如公交车道、非机动车道、机非分隔带、停车位等这些都可以在 Lane 的split里继续往下细分。核心思路是大剖面拆成小剖面小剖面再拆成模型层层递归。3.5 效果检查Local View 与 StreetEngine 场景切换写完规则后建议在 StreetEngine 的 Local View 窗口里查看单条路的生成效果。Local View 可以实时反馈规则执行结果很方便调试。如果道路在场景里出现破面或者错位优先检查两件事一是 SCOPE 的坐标系是否在遇到旋转后“绕晕”了二是纹理的方向轴是否正确。很多时候问题出在projectUV之前忘记setupProjection导致纹理坐标混乱贴图像被揉成一团。确认单条路没问题后再对整个路网图层执行 Generate做全场景的规则生成。4. 性能优化与规则库调优技巧4.1 减少三角面数哪些参数会影响最终表现道路规则库一旦应用到大范围场景性能问题就出来了。最开始我拿一个 10 平方公里的城市区块测试结果生成一次要十几分钟场景缩放也卡顿严重。后来逐项排查发现三角面数爆炸主要来自几个地方人行道挤出高度方向分段数过多其实挤出 0.15 米高的长方体只要 6 个面就够了路灯和树木的模型面数太高一个路灯模型三万多面整条路生成一百多个路灯直接卡死纹理尺寸过大贴图都是 4K 纹理GPU 显存直接被打满优化思路要分两步。第一步是改参数把路灯、树木这种重复模型的间距调大把贴图全部压缩到 2K 以内人行道和路缘石的挤出高度分段改为 1。第二步是改结构大场景中把附属设施和路面分开两个图层一个管大范围路网一个管重点区域精细模型。4.2 CGA 代码编写中的性能陷阱CGA 代码本身也有性能差异。split操作会比extrude更消耗资源因为 split 会生成更多子 Shape。但道路模型又离不开 split所以关键是要控制 split 的递归深度。写规则的一个优化原则是尽量不要在每次生成时都重建整个模型。如果你发现某条规则每次都从最外层重新跑一遍而它的子 Shape 已经可以缓存了可以考虑把子规则用NIL提前终止或者用Hidden属性标记不需要显示的子规则。还有一个小细节CGA 中三维模型的导入量很高。如果你把路灯的 OBJ 文件直接用i()插入模型文件本身的大小会直接影响生成速度。规则库的资产管理上优先使用低模版本必要时用 Billboard 替代实体模型。4.3 规则库调试技巧如何快速定位规则报错StreetEngine 的报错机制不算太友好经常只给一句话“Unknown attribute”或者“Division by zero”。我的调试经验是先用print()把关键变量的值打印出来看看。比如StartRule Street -- print(streetType) print(scope.sz)这样在控制台就能看到当前路段在被规则处理时的属性值和尺寸能很快判断是数据问题还是规则问题。还有一个技巧是“最小复现”。当某个复杂的规则执行报错时把规则代码一步步注释直到只剩最简单的extrude加texture确认能跑通后再慢慢把复杂度加回来。很多时候报错原因就藏在某个细微参数上比如路面宽度为 0 导致split除零。5. 常见问题与排查技巧实录5.1 规则生成了但场景里什么都没有遇到这种情况第一反应先看控制台有没有报错。如果没有报错那大概率是规则生成的模型尺寸太小或者位置偏离视口。我碰到过一种比较隐蔽的情况路网数据导入时坐标系对不上导致道路生成到了离场景原点非常远的地方。这时候只需要把图层的坐标重新投影到和场景一致问题就解决了。还有一种情况是贴图资源路径错误导致模型生成了但表面是纯色。这时候在规则里加上print(texture loaded)只能确认逻辑走到不能确认贴图加载成功建议直接在 StreetEngine 的资源管理器里双击贴图确认能否打开。5.2 交叉口处路面断裂或重叠交叉口问题基本都出在 Crossing 规则缺失或写法不当。如果你想快速解决可以试试关闭交叉口自动生成改用复杂一点的 Street Shape 端点处理逻辑把交叉口区域直接并入两条街道的规则里。但最佳方案还是老老实实写 Crossing 规则。注意几个要点Crossing 规则的生成单元是交叉口 Shape而不是街道 Shape斑马线和停止线都放在 Crossing 的坐标系下要通过t()精确平移到路口如果交叉口的 Shape 方向不固定需要根据scope.rz判断旋转5.3 道路两端的边界有问题如何封口很多时候道路生成出来后两端是开放的能看到模型的内部结构非常影响效果。解决方案是给道路规则加一个“封口”分支Street -- Lane Sidewalk EndCap EndCap -- s(scope.sx, scope.sy, 0.1) t(0, 0, -0.05) i(assets/props/road_end.obj)这里i()插入一个专门做的端头封口模型可以是一块简单的深色面板也可以做一个完整的道路端部建模。5.4 关于规则库文件组织和版本管理的建议规则库文件多了之后版本管理也是个麻烦事。我建议把整个规则库文件夹纳入 Git 管理贴图、CGA、模型资源全部统一版本。这个建议虽然听起来像是常识但在实际项目中真正做到的团队并不多。我自己的习惯是每次修改规则文件后至少在提交说明里写明改了哪个模块、调整了哪个参数。等某次生成效果变差了你就可以用 Git 回退到上一个稳定版本对比效果。没有版本管理的话规则库改坏了只能靠记忆慢慢往回找那酸爽试过一次就懂。6. 道路规则库的扩展方向与实际应用心得6.1 从“能看”到“能用”数据驱动规则库道路规则库最大的价值在于它可以和真实数据联动。如果你能拿到道路的车道数、路宽、限速、公交线路等真实属性规则库就能生成非常准确的交通模型而不是停留在“看起来像道路”的阶段。做法也很简单在路网数据的属性表里加几个字段比如lanes、width、sidewalk然后在 CGA 规则里读取case lanes 4: WideMultiLane case lanes 4: NormalRoad这样的话规则库就变成了一套“从数据到模型”的自动化工具比手动建模的信息承载力高得多。6.2 和 GIS 数据联动从 JSON 或 Geojson 生成道路StreetEngine 支持的导入格式有限但你完全可以通过中间工具把数据转成支持格式。比如用 Python 处理 GeoJSON转换成 Shapefile再导入 StreetEngine。转换过程中可以顺便补充属性字段一次搞定。如果你做的是 WebGIS 项目想通过 StreetEngine 生成道路模型后导出成 glTF/OBJ再放到 Cesium 或者 Mapbox 上展示那也是完全可行的。道路规则库生成的模型几何结构清晰导出后在其他引擎里基本能无缝使用。6.3 我在实际操作中的体会和几个小技巧最后分享几个我自己总结的小技巧。第一规则命名要规范。给规则起一个清晰的名字比如Lane_4Lane、Sidewalk_Narrow、Crosswalk_Standard比叫Rule1、Rule2好用一百倍。因为规则文件一多你根本记不住Rule1是干嘛的。第二善用注释。CGA 支持//注释在每个规则文件头部写清楚这个文件的作用、修改日期、修改人比什么都重要。第三不要追求绝对真实。道路建模做到一定程度边际效益递减非常明显。与其花大量时间调整某条路的局部细节不如把精力放在整个路网的结构正确性和视觉统一性上。把主干道、支路、人行道、标线的粗细对比做出来整体效果就已经很好了。第四定期做全量重新生成。StreetEngine 支持局部更新但局部更新有时会因为缓存导致效果不一致。每次改完规则库建议全量重新生成一遍确保所有路段都是按照最新规则生成的。这几点看起来简单但都是实打实踩过坑换来的。规则库这东西做得越久越会发现它比拼的不是技巧难度而是代码组织能力和数据管理能力。就像做菜一样菜谱谁都会看但刀工火候都得靠时间练出来。本文还有配套的精品资源点击获取