
简介这份资源是面向CityEngine用户与三维城市建模学习者的森林景观规则合集主要解决自然植被场景批量生成与地形适配的问题适合具备一定程序化建模基础的设计师、规划人员及游戏场景开发者使用。压缩包共收录2000个文件整体约193.71MB以971个js脚本、14个obj模型、13个mtl材质、10个xml配置及若干png、jpg贴图为主另含大量bak备份文件涵盖规则逻辑、模型资源与材质定义等模块。资源围绕植被类型、随机化参数、地形适应性、季节变化、光照阴影与批量生成等维度组织可用于快速搭建松树、阔叶林、灌木等多样化森林群落并支持道路、建筑、水体的避让交互。已有123人学习适合作为程序化森林景观建模的参考素材帮助读者理解规则驱动场景的构建思路与参数调节方式。1. 从一份 Cityengine 规则包说起森林景观怎么用规则批量生成如果你手里拿到一个名为Cityengine规则森林景观规则.zip的压缩包第一反应大概率是这到底是一堆 CGA 规则文件还是一套能直接跑出森林场景的工程答案通常偏向前者——它是一组用 CGAComputer Generated Architecture语法写成的规则文件配合少量纹理和参数配置用来在 CityEngine 里批量生成森林、树丛、林间空地这类景观。核心价值在于你不需要一棵一棵手动摆树而是定义好「哪里长树、长多密、树多高、什么树种」这些规则剩下的交给引擎按地块和地形自动铺开。这件事适合谁做城市三维重建、景观规划可视化、游戏或影视场景预生成的从业者。尤其是当你需要一片几平方公里、上万棵树的森林手动建模基本不可能规则驱动才是正路。热搜里常出现的「cityengine规则库」「语法规则」「gkd订阅规则」其实指向同一个诉求规则怎么组织、怎么复用、怎么校验。这篇就按「规则包是什么 → 怎么导入跑通 → 参数怎么调 → 坑在哪 → 怎么进阶」的顺序把森林景观规则这条线讲透。2. 拆开规则包CGA 森林景观规则的组成与运行逻辑2.1 一个森林景观规则包通常包含哪些文件拿到压缩包先别急着导入解压后看清楚目录结构。常见的森林景观规则包大致是这几类文件文件类型典型命名作用CGA 规则文件forest.cga/tree.cga定义森林和树木的生成逻辑纹理贴图bark_*.jpg/leaf_*.png树皮、树叶的材质资产模型tree_*.obj/*.fbx可选用外部模型替代程序化树参数配置*.xml/*.json默认参数、随机种子说明文档readme.txt作者写的使用说明别跳过CGA 是 CityEngine 的核心规则语言本质是一种带几何操作的声明式语言。一个最简的森林规则大概长这样// forest.cga - 森林景观基础规则 attr treeDensity 0.6 // 树木密度 0~1 attr treeHeightMin 8 // 最小树高米 attr treeHeightMax 18 // 最大树高米 attr speciesMix 0.5 // 针叶/阔叶混合比例 Lot -- scatter(surface, treeDensity, uniform) { Tree } Tree -- case speciesMix rand(1): conifer else: broadleaf conifer -- extrude(rand(treeHeightMin, treeHeightMax)) taper(0.3) texture(bark_conifer.jpg) s(0.8, 1, 0.8) Leaf broadleaf -- extrude(rand(treeHeightMin, treeHeightMax)) taper(0.5) texture(bark_broadleaf.jpg) Leaf Leaf -- comp(top) { color(0.2, 0.5, 0.15) texture(leaf_canopy.png) }这段规则做了三件事Lot规则在地块表面按treeDensity散布树Tree规则按speciesMix随机决定针叶还是阔叶conifer和broadleaf分别拉伸树干、收窄顶部、贴树皮纹理最后在顶部生成树冠。scatter是森林景观最关键的算子它决定了树在地块上的分布方式。2.2 规则是怎么被引擎执行的从地块到森林的链路CityEngine 的执行链路是地块Lot→ 规则匹配 → 几何生成 → 纹理映射。你导入规则后引擎会遍历每个地块找到匹配的起始规则通常是Lot然后按规则里的算子顺序执行。scatter会在表面随机撒点每个点触发一次Tree规则最终生成一棵树。这里有个容易被忽略的点scatter的分布模式。上面代码里用的是uniform意思是均匀随机。还有poisson模式它会让树之间保持最小间距避免树挤在一起。森林景观里如果树太密渲染时面数爆炸poisson更实用Lot -- scatter(surface, treeDensity, poisson, 3) { Tree }最后一个参数3是最小间距米意思是任意两棵树的中心距离不小于 3 米。这个参数要根据树冠大小来定树冠直径 6 米的话间距至少 3 米才不会穿插。2.3 导入规则包并跑通第一个森林场景操作步骤不复杂但顺序要对打开 CityEngine新建一个场景导入你的地形数据DEM 或平面地块。在 Navigator 面板右键选择 Import → CityEngine Rules指向解压后的.cga文件。选中地块图层在 Inspector 面板的 Rule File 里指定刚导入的规则文件。确认起始规则名是Lot如果作者用了别的名字比如Forest要手动改。点击 Generate 按钮等待生成。如果生成后一片空白先检查地块有没有被正确分配规则。常见情况是规则文件导入成功但地块的 Rule File 没指过去引擎不知道用哪套规则。另一个检查点是起始规则名CGA 文件里第一个规则不一定是Lot得看作者怎么写的。提示生成前把场景的坐标系和单位确认一遍。森林景观规则里的树高、间距都是按米算的如果场景单位是英尺树会矮一大截。3. 把森林规则调成你要的样子密度、树种、地形适配3.1 treeDensity 和 scatter 参数怎么设才不翻车treeDensity是最直观的参数但它和scatter的配合有讲究。treeDensity在 CGA 里通常表示「每单位面积撒多少个点」但不同作者的实现不一样。有的把它当 0~1 的比例有的当每公顷株数。拿到规则包先看attr定义那几行确认量纲。我一般会这样调先设一个保守值比如每公顷 200 株生成后看效果。太稀就翻倍太密就减半。别一上来就设每公顷 2000 株生成一次可能几分钟面数直接上百万CityEngine 会卡死。// 按每公顷株数控制密度 attr treesPerHectare 200 attr minSpacing 3 Lot -- scatter(surface, treesPerHectare / 10000, poisson, minSpacing) { Tree }这里把treesPerHectare除以 10000 换算成每平方米的密度因为scatter的密度参数是按平方米算的。minSpacing单独拎出来方便和密度解耦调整。3.2 树种混合与随机种子让森林看起来不假纯随机撒同一种树生成出来会像复制粘贴。真实的森林有树种混合、有大小差异、有疏密变化。speciesMix控制针叶和阔叶的比例但光靠一个比例还不够得给每棵树加随机尺寸和随机旋转Tree -- case speciesMix rand(1): conifer else: broadleaf conifer -- extrude(rand(treeHeightMin, treeHeightMax)) taper(rand(0.2, 0.4)) r(0, rand(360), 0) // 随机绕 Z 轴旋转 s(rand(0.8, 1.2), 1, rand(0.8, 1.2)) // 随机缩放 texture(bark_conifer.jpg) Leafrand(360)让每棵树绕自身轴随机转一个角度s()做随机缩放。这两个操作成本很低但视觉上能显著打破重复感。随机种子在场景级别控制同一个种子生成结果可复现换种子就换一片森林。3.3 地形起伏下的森林规则适配森林很少长在平地上。如果地块有高程变化scatter默认是贴着表面撒点的树会跟着地形起伏这没问题。但树干的朝向默认是垂直向上在陡坡上会显得不自然。解决办法是用align算子让树垂直于坡面Tree -- align(scope, surfaceNormal) extrude(rand(treeHeightMin, treeHeightMax)) ...surfaceNormal是地块表面的法线方向align把树的局部坐标系对齐到法线。坡度超过 30 度的地方树会明显倾斜看起来更自然。但要注意如果坡度太陡树根会悬空这时候要么限制森林只长在缓坡要么在树根处加一个小的基座几何。Lot -- case slope(surface) 30: NIL // 陡坡不生成树 else: scatter(surface, treesPerHectare / 10000, poisson, minSpacing) { Tree }slope(surface)返回地块坡度超过 30 度直接返回NIL不生成任何几何。这是最省事的陡坡处理方式。3.4 用规则属性做批量参数控制CityEngine 的 Inspector 面板会把 CGA 里的attr暴露成可调参数。你可以不碰代码直接在面板里拖滑块调密度、树高、混合比例。但如果你有几十个地块一个个调太慢。更好的做法是在规则里定义一套参数然后用attr的继承机制让所有地块共享// 全局参数所有地块共用 attr globalDensity 0.5 attr globalHeightMin 8 attr globalHeightMax 18 Lot -- scatter(surface, globalDensity, poisson, 3) { Tree } Tree -- extrude(rand(globalHeightMin, globalHeightMax)) ...改一处全场景生效。如果某个地块要特殊处理再单独覆盖它的属性值。这种「全局默认 局部覆盖」的模式在大型森林景观里比逐个调参高效得多。4. 森林景观规则常见的 5 个坑从生成失败到性能爆炸4.1 生成后什么都没有规则没匹配上现象点击 Generate 后场景空白Navigator 里地块还在但没有任何几何生成。原因最常见的是起始规则名不对。CGA 文件里第一个规则不一定是Lot有的作者写Start、Forest、Plot。另一个原因是地块的 Rule File 没指定或者规则文件里有语法错误导致整个文件加载失败。解决打开 CGA 文件看第一个规则名在 Inspector 里把起始规则改成对应的名字。如果是语法错误CityEngine 的 Console 面板会报行号按行号去查。常见语法错误包括少分号、括号不匹配、用了未定义的属性。4.2 树全部挤在一起或全部消失scatter 密度量纲搞错现象要么树密到看不见地面要么稀稀拉拉几棵。原因scatter的密度参数是按每平方米算的但很多规则包里的treeDensity是按每公顷写的。作者可能忘了除 10000或者你在调参时直接填了每公顷的数值。解决先确认scatter那行代码里密度参数的实际含义。如果是scatter(surface, treeDensity, ...)而treeDensity默认值是 200那实际密度是每平方米 200 棵必然爆炸。改成treeDensity / 10000或者把默认值改成 0.02。4.3 生成速度极慢或直接崩溃面数失控现象Generate 转圈几分钟没反应或者 CityEngine 直接无响应。原因每棵树的几何面数乘以树的总数很容易上千万。一棵程序化树如果树冠用了高分段几何可能几千面一万棵树就是几千万面CityEngine 扛不住。解决三个方向。第一降低密度先保证能生成出来再逐步加。第二简化树的几何树冠用低面数模型或 billboard始终面向相机的面片。第三用 LODLevel of Detail近处用高模远处用低模。CGA 里可以用lod属性控制attr lod 0 Tree -- case lod 0: highDetailTree case lod 1: lowDetailTree else: billboardTree生成时根据视距切换 LOD能大幅降低实时渲染压力。但批量生成阶段还是得控制总量LOD 主要解决的是生成后的浏览性能。4.4 纹理丢失或显示为纯色路径和格式问题现象树生成了但树皮和树叶是纯色没有纹理。原因CGA 里的texture()路径是相对路径相对于 CGA 文件所在目录。如果纹理文件没跟着规则包一起解压或者目录结构变了路径就断了。另一个原因是纹理格式不被支持比如用了 CityEngine 不认的 WebP。解决检查 CGA 文件里texture()的路径确认纹理文件在对应位置。路径里尽量用正斜杠/不要用反斜杠\。纹理格式用 JPG 或 PNG别用冷门格式。如果纹理确实找不到CityEngine 会回退到默认材质看起来就是纯色。4.5 地形起伏处树根悬空或树干穿地现象在陡坡上树根悬在空中或者树干插进地里。原因scatter撒的点是贴表面的但树的几何原点可能在树干底部也可能在树干中心。如果原点在中心树就会一半在地下。另外如果用了align对齐法线但树根没有额外处理陡坡上会悬空。解决确认树的几何原点位置。如果是外部模型在建模软件里把原点设到树干底部。如果是程序化生成extrude是从原点向上拉伸的原点就在底部没问题。陡坡悬空的话在树根处加一个小的锥体或圆柱作为基座填充和地面的缝隙Tree -- align(scope, surfaceNormal) extrude(rand(treeHeightMin, treeHeightMax)) taper(0.3) Base Leaf Base -- comp(bottom) { extrude(0.5) taper(0.8) color(0.3, 0.2, 0.1) }comp(bottom)选中树的底面向上拉伸 0.5 米做一个基座颜色设成深棕色模拟泥土。这样在陡坡上树根就不会悬空了。5. 进阶用规则属性做森林景观的四季变化与批量出图5.1 用属性切换季节一套规则出四套景观森林景观规则最实用的进阶玩法是用属性控制季节变化。树的结构不变只换树叶颜色和纹理就能从春天嫩绿切到秋天金黄。核心是把季节相关的参数抽出来attr season summer // spring / summer / autumn / winter Leaf -- case season spring: color(0.6, 0.85, 0.4) texture(leaf_spring.png) case season summer: color(0.2, 0.5, 0.15) texture(leaf_summer.png) case season autumn: color(0.8, 0.5, 0.1) texture(leaf_autumn.png) else: color(0.4, 0.35, 0.3) texture(leaf_winter.png)season属性在 Inspector 里是个下拉选项切换后重新 Generate整片森林的树叶就换了一套。冬天还可以把树叶几何去掉只留树干Leaf -- case season winter: NIL else: ...NIL表示不生成任何几何树冠直接消失只剩光秃秃的树干。这个技巧在做四季变化演示时特别省事不用维护四套规则文件。5.2 批量出图用 Python 脚本驱动 CityEngine如果你需要出几十张不同角度、不同季节的森林景观图手动一张张调太慢。CityEngine 提供了 Python 脚本接口可以批量改属性、批量导出。下面是一个最小示例# batch_export.py - 批量切换季节并导出图片 from scripting import * ce CE() seasons [spring, summer, autumn, winter] viewpoints [view1, view2, view3] for season in seasons: # 设置季节属性 ce.setAttribute(Lot, season, season) ce.generate() for vp in viewpoints: ce.setViewpoint(vp) filename fforest_{season}_{vp}.png ce.exportImage(filename, 1920, 1080) print(f导出: {filename})这段脚本遍历四个季节和三个视角每个组合生成一张 1920x1080 的图。setAttribute改属性generate重新生成几何setViewpoint切视角exportImage出图。实际用的时候把视角名换成你场景里的实际视角名称。注意批量生成前先把单张流程跑通确认属性名、视角名、导出路径都对。脚本跑一半报错的话已经生成的图不会自动清理得手动删。5.3 规则校验怎么确认你的 CGA 没有隐藏问题CGA 规则写多了容易出隐蔽问题比如某个分支永远走不到、某个属性没定义默认值、纹理路径大小写不一致。CityEngine 自带一个规则检查功能在 Rules 面板里右键选 Check Rules会列出警告和错误。常见的警告包括未使用的属性定义了但没在规则里引用不影响运行但说明代码有冗余。永远为真的条件比如case 1 0逻辑上没问题但没意义。纹理路径不存在生成时不会报错但纹理加载失败。我一般会在批量生成前跑一遍 Check Rules把错误清掉警告选择性处理。这一步花几分钟能省掉后面大量排查时间。5.4 我踩过的一个坑属性名冲突导致规则静默失效最后说一个血泪教训。有次我在一个森林规则包里加了自己的treeHeight属性结果生成出来的树全部是默认高度我调的参数完全没生效。查了半天才发现规则包里已经有一个同名的treeHeight属性而且它在更内层的规则里被重新赋值了。CGA 的属性作用域是就近原则内层覆盖外层我的全局设置被内层覆盖了。解决办法是给自定义属性加前缀比如my_treeHeight避免和规则包原有属性冲突。或者用attr的const修饰符锁定属性值防止被覆盖。这个坑不报错、不警告只是静默失效排查起来很费时间。后来我养成了一个习惯拿到任何规则包先搜一遍attr定义把所有属性名列出来再决定自己加什么名字。希望帮到你。本文还有配套的精品资源点击获取