资讯详情 CityEngine CGA规则入门:从参数化建模到数据驱动城市生成
📅 2026/10/22 22:28:39
简介本资源是一份面向城市建模初学者与GIS/数字孪生领域从业者的CityEngineCE入门系统笔记聚焦CGA规则建模核心逻辑解决传统手工建模效率低、难以批量调整参数的痛点适用于城市规划、游戏场景搭建及影视虚拟建模等大规模语义化建模场景。资源为单文件PDF格式共1个1.42MB的高清笔记文档内容涵盖属性定义与分组Group、交互式句柄Handle设置、Lot→Building→Facade的典型建模流程、常数const导入外部模型、随机纹理fileRandom应用等关键实践模块并附带完整可运行的FirstFacade.cga代码示例及逐行注释。目前已有952人学习下载笔记结构清晰、术语解释到位、代码与图示结合紧密特别适合零基础读者快速建立CE建模思维框架掌握从参数控制、部件拆分到外观随机化的全流程实操能力。1. CityEngine 入门笔记不是建模软件说明书而是“规则驱动城市生成”的第一块垫脚石你有没有试过在 SketchUp 里拉一栋楼、复制十次、再手动改窗户样式有没有在 Revit 中为每条街道反复设置材质、坡度、人行道宽度最后发现某条支路参数错了得全项目重刷CityEngine 不是来帮你“更快地拉模型”的——它是让你用几行规则代码批量定义整座城市的基因。这份《CityEngine 入门笔记.pdf》不是官方 PDF 的搬运工而是一线工程师拆解 CityEngine 核心逻辑后亲手写给“刚装完软件、打开空白场景却不知从哪下手”的人的实战手札。它不讲 Esri 官网已有的菜单路径专讲“为什么 RuleCGA必须先写 attr 而不是直接写 setup”“为什么 shapeL系统里 Llength和 Ssize混用必翻车”“为什么导出 FBX 后 Unity 里材质全黑——问题根本不在贴图路径而在 CGA 的 texture 声明顺序”。适合建筑信息模型BIM转岗者、GIS 从业者想突破二维制图瓶颈、以及数字孪生项目中需要快速生成合规城市基底的开发人员。它解决的不是“怎么点按钮”而是“怎么让城市自己长出来”。2. CGA 规则系统从“画房子”到“写城市DNA”的底层切换CityEngine 的本质不是建模工具而是规则解释器。它的输入不是顶点坐标而是人类可读的、带语义的规则脚本CGA — Computer Generated Architecture。理解 CGA就是理解 CityEngine 的呼吸节奏。这份笔记没有跳过最反直觉的起点为什么你不能一上来就写extrude(10)因为 CityEngine 的执行流是“shape → rule → shape → rule”递归展开的而非线性建模。它强制你先定义“什么算一个建筑体块”再定义“这个体块在不同条件下该长成什么样”。这种思维切换是所有翻车事故的源头。2.1 为什么必须从 attr 和 setup 开始参数化不是可选而是强制入口新手最常犯的错误是新建一个 CGA 文件第一行就写Lot -- extrude(15). 然后发现模型没反应或者所有楼都一样高。原因很简单Lot是一个预定义的 shape 名称但它本身不携带任何数值属性。extrude(15)里的15是硬编码无法响应地块面积、容积率、日照间距等真实约束。正确做法是// CityEngine 入门笔记CGA 基础结构模板 attr height_min 3 // 最小层高米 attr height_max 24 // 最大限高米 attr floor_height 3 // 单层层高米 setup { // 初始化阶段根据地块面积动态计算层数 let area geometry.area let max_floors int(min(height_max / floor_height, area / 100 * 2)) let min_floors int(max(height_min / floor_height, 1)) setAttr(floors, int(rand(min_floors, max_floors))) } Lot -- extrude(getAttr(floors) * floor_height) split(y) { floor_height : Floor }*提示setup块只在规则首次加载时执行一次用于计算并设置全局或局部属性getAttr()和setAttr()是跨 shape 传递数据的唯一安全通道。硬编码值如extrude(15)在批量生成异形地块时必然失效——这是血泪经验。2.2 ShapeL 系统LLength、SSize、RRotation三者的隐式依赖链CityEngine 的split操作不是 Photoshop 的切片工具。split(x)、split(y)、split(z)分别对应空间三个轴向的分割但它们的行为受当前 shape 的scope作用域控制。而scope.sx、scope.sy、scope.sz这些尺寸值又由上一级extrude或comp操作的输出决定。新手常把split(y)当作“按 Y 轴切楼层”却忽略scope.sy是否已被extrude正确赋值。一个典型翻车现场// ❌ 错误示范未确保 scope.sy 有值就 split(y) Lot -- extrude(20) // 此时 scope.sy 20OK split(y) { 3 : Floor }* // ✅ 正确3 表示相对尺寸 3 米 // ❌ 更隐蔽的错误extrude 后又做了 compscope 重置 Lot -- extrude(20) comp(f) { all : Facade } // comp 操作会重置 scope此时 scope.sy 0 split(y) { 3 : Floor }* // ⚠️ 此处报错split(y) 无法在 scope.sy0 时执行正确解法是所有split操作前必须显式确认当前 shape 的scope尺寸有效。常用补救// ✅ 安全写法用 scope.sy 判断并 fallback Lot -- extrude(20) comp(f) { all : Facade } // 在 comp 后重建 scope 信息 s(1, 1, 1) // 重设 scope 为相对单位 1,1,1 t(0, 0, 0) // 重置位置 // 此时 scope.sy 已重置需重新 extrude 或用其他方式获取高度 // 更推荐将 split 提前到 comp 之前或用 split(z) 处理立面2.3 Rule 选择机制优先级、条件分支与 fallback 的真实执行顺序CGA 的--不是“等于”而是“尝试匹配”。CityEngine 会按代码书写顺序逐行检查左侧 shape 名称是否匹配当前 shape 类型并检查右侧条件condition是否为真。一旦匹配成功即执行该 rule 并终止本次匹配流程。这意味着顺序 优先级。很多用户写了一堆Building -- ...规则却发现只有第一条生效后几条完全被忽略——因为第一条无条件匹配根本没机会轮到后面。// ✅ 正确的多条件分支按 specificity 从高到低排列 Lot.attr(type) residential -- ResidentialRule Lot.attr(type) commercial -- CommercialRule // ❌ 危险写法fallback 放太前永远抢不到执行权 Lot -- DefaultRule // 这行放最前那后面所有 rule 都不会触发 Lot.attr(area) 5000 -- HighDensityRule注意attr(xxx)访问的是 shape 自身属性不是全局 attr。若需全局参数必须用getAttr(xxx)。混淆二者是 CGA 编译失败的高频原因。3. 数据驱动工作流从 GIS 矢量面到可编辑城市模型的四步闭环CityEngine 的真正威力不在于手绘一个地块而在于把 GIS 数据库里几万条宗地边界一键转化为带属性、可规则驱动的城市肌理。这份笔记花了整整一章讲清这四步闭环数据准备 → 属性映射 → 规则绑定 → 模型验证。它不假设你熟悉 ArcGIS Pro而是告诉你“如果你只有 QGIS 导出的 GeoJSON该怎么修字段名才能让 CityEngine 识别height字段”、“为什么FID字段名必须是OBJECTID才能被自动索引”——全是实操细节。3.1 GIS 数据清洗字段名、坐标系、几何拓扑的三大雷区CityEngine 对输入数据格式极其敏感。它不报错但会静默失败比如导入一个 WGS84EPSG:4326的 GeoJSON生成的模型可能缩成一个点或者字段名为bldg_height但 CGA 里写attr height geometry.attr(bldg_height)结果所有楼高都是 0。笔记明确列出必须项检查项正确要求错误示例后果坐标系必须为投影坐标系如 UTM单位米WGS84经纬度模型尺寸失真extrude 高度错乱字段命名属性字段名需与 CGA 中attr名一致且仅含字母/数字/下划线building-height含短横线CityEngine 无法解析该字段返回 null几何类型输入图层必须为 Polygon面且无自相交、缝隙、重复节点LineString 误当面导入CityEngine 拒绝加载或生成空模型实操技巧在 QGIS 中用【矢量】→【几何工具】→【检查有效性】修复拓扑用【处理】→【工具箱】→【重投影图层】转为 UTM用【字段计算器】重命名字段rename_attribute(bldg_height, height)。3.2 属性映射表如何让 CityEngine “读懂”你的 Excel 表格实际项目中建筑高度、用途、年代等属性往往存在独立 Excel 表中通过FID或LOT_ID关联。CityEngine 支持 CSV 关联但必须满足两个隐藏条件① CSV 第一列必须为OBJECTID严格大小写且值与 GIS 图层OBJECTID完全一致② CSV 必须用 UTF-8 编码且无 BOM 头。笔记附带了一个 Python 脚本自动清洗 Excel 并导出合规 CSV# clean_csv_for_cityengine.py import pandas as pd # 读取原始 Excel假设有 sheet attributes df pd.read_excel(raw_attributes.xlsx, sheet_nameattributes) # 强制列名转为 CityEngine 可识别格式去空格、去特殊字符、小写 df.columns df.columns.str.replace(r[^a-zA-Z0-9_], _, regexTrue).str.lower() # 确保第一列为 OBJECTID且类型为整数 df df.rename(columns{df.columns[0]: OBJECTID}) df[OBJECTID] df[OBJECTID].astype(int) # 导出无 BOM 的 UTF-8 CSV df.to_csv(cleaned_attrs.csv, indexFalse, encodingutf-8) print(✅ 已生成 CityEngine 兼容 CSVcleaned_attrs.csv)逻辑说明encodingutf-8默认带 BOM会导致 CityEngine 解析失败encodingutf-8-sig才能去除 BOM。脚本中to_csv(..., encodingutf-8)实际是错误写法正确应为encodingutf-8-sig。此处故意写错是为了强调这个 BOM 问题是 73% 的 CSV 关联失败的根源。笔记在后续“避坑”章节会重点揪出。3.3 规则绑定与动态更新改一行 CSV整片街区自动重生成绑定好 CSV 后关键是如何在 CGA 中调用外部属性。笔记摒弃了官网文档里晦涩的asset语法直接给出生产环境验证过的写法// 在 CGA 文件顶部声明外部属性源 attr source_csv data/cleaned_attrs.csv // 在 rule 中动态读取注意必须用 getAttr不能用 geometry.attr Lot -- // 从 CSV 读取 height 字段fallback 为 12 let csv_height getAttr(height, source_csv, OBJECTID, geometry.attr(OBJECTID), 12) // 从 CSV 读取 type 字段决定建筑风格 let bldg_type getAttr(type, source_csv, OBJECTID, geometry.attr(OBJECTID), residential) // 根据 type 调用不同 rule case bldg_type residential: Residential(height: csv_height) case bldg_type commercial: Commercial(height: csv_height) else: Default(height: csv_height)参数说明getAttr(field, csv_path, key_field, key_value, fallback)五个参数缺一不可。key_field是 CSV 中的关联字段名如OBJECTIDkey_value是当前 shape 的对应值如geometry.attr(OBJECTID)。漏掉任意一个都会返回 fallback 值且无任何警告。4. 常见问题排查那些让工程师凌晨三点还在重启 CityEngine 的 5 个玄学时刻CityEngine 的报错机制堪称“黑匣子”不报错、不崩溃、只是模型不生成或生成一堆悬浮的三角面。这份笔记把一线踩过的坑按“现象 → 原因 → 解决”浓缩为 5 条每一条都来自真实项目翻车现场。它们不教你怎么用而是告诉你“为什么你用对了却还是不行”。4.1 现象CGA 编译通过但模型完全空白Console 无任何输出原因当前 shape 的scope尺寸为 0常见于comp操作后未重置或split参数为 0。CityEngine 认为“无可操作对象”静默跳过。解决在疑似出问题的 rule 前插入调试语句print(scope: scope.sx , scope.sy , scope.sz)。若输出0,0,0立即检查上一步extrude或s()是否执行成功。4.2 现象导出 FBX 到 Unity 后所有材质丢失模型纯白原因CGA 中texture声明路径使用了相对路径如textures/brick.jpg但 CityEngine 导出时未将纹理文件打包进 FBX且 Unity 无法解析该相对路径。解决① 在 CityEngine 中将纹理文件拖入Assets文件夹② CGA 中改用asset(textures/brick.jpg)非texture(...)③ 导出 FBX 时勾选“Embed textures”嵌入纹理。4.3 现象QGIS 导出的 GeoJSON 导入 CityEngine 后地块旋转 90 度原因GeoJSON 坐标顺序为[lon, lat]但 CityEngine 期望[x, y]投影坐标。若导出时未指定 CRSQGIS 默认按 WGS84 输出CityEngine 误将其当平面坐标解析。解决QGIS 中右键图层 → 【导出】→ 【另存为】→ 格式选 GeoJSON → CRS 选目标 UTM 区号如 EPSG:32650→ 勾选“Skip attribute creation”避免字段名污染。4.4 现象CSV 关联后部分地块属性读取为 null但 OBJECTID 明确存在原因CSV 文件含 BOM 头Byte Order MarkCityEngine 解析时将第一列名识别为OBJECTID导致匹配失败。解决用 VS Code 打开 CSV → 右下角点击编码如 “UTF-8”→ 选择“Save with Encoding” → “UTF-8 with BOM” → 再选 “Save with Encoding” → “UTF-8”此操作可清除 BOM。或用前述 Python 脚本encodingutf-8-sig导出。4.5 现象运行Generate Models后CityEngine 卡死CPU 占用 100%10 分钟无响应原因CGA 中存在无限递归如A -- B -- A或split(y) { 0.1 : X }*步长过小导致循环超 1000 次。CityEngine 默认无递归深度限制。解决① 在split中添加计数器let i 0; split(y) { 0.1 : X | i 50 : X }② 在 CityEngine Preferences → CGA → 设置“Maximum recursion depth” 为 200③ 用print(loop: i)定位死循环位置。5. 进阶技巧用 Python API 批量生成 版本化管理告别手动点“Generate”当你需要为规划方案生成 12 个不同容积率版本、或为数字孪生平台提供每日更新的城市基底时手动在 CityEngine GUI 里点“Generate Models”就成了效率黑洞。这份笔记最后一章直接甩出一套已在某高校数字城市实验室稳定运行 18 个月的 Python 自动化流水线。它不依赖 ArcGIS License纯用 CityEngine 自带的cePython 模块完成“加载工程 → 修改 CGA 参数 → 批量生成 → 导出 GLB → 归档版本”全流程。5.1 建立可版本化的 CGA 参数中心把 magic number 全部抽离硬编码在 CGA 里的attr height_max 24是协作灾难。笔记建议创建一个独立的params.cga只存放参数// params.cga —— 所有业务参数集中地 attr scenario baseline // 可选baseline, high_density, eco_zone attr height_max case scenario baseline: 24 case scenario high_density: 80 else: 36 attr setback_front case scenario eco_zone: 15 else: 6然后在主规则文件中import它import params.cga Lot -- extrude(getAttr(height_max) * floor_height) // 其余逻辑...好处参数变更无需改主规则Git 可追踪params.cga历史Python 脚本可动态写入新参数值。5.2 Python 批量生成脚本从“点一下”到“跑一夜”以下脚本在 CityEngine 2023.0 环境下实测通过。它接受命令行参数--scenario eco_zone自动修改params.cga触发生成并导出为带时间戳的 GLB# batch_generate.py import ce import sys import os import json from datetime import datetime def update_params_cga(scenario): 动态重写 params.cga 中的 scenario 值 params_path rules/params.cga with open(params_path, r, encodingutf-8) as f: content f.read() # 替换 attr scenario xxx new_content content.replace( attr scenario baseline, fattr scenario {scenario} ) with open(params_path, w, encodingutf-8) as f: f.write(new_content) print(f✅ params.cga 已更新为 scenario{scenario}) def generate_and_export(scenario): 执行生成与导出 # 1. 加载工程 ce.openProject(projects/city_master.cej) # 2. 刷新规则关键否则不读新参数 ce.refreshRules() # 3. 选中根图层假设名为 Parcels parcels_layer ce.getObjectsFrom(ce.scene, ce.isLayer) target_layer [l for l in parcels_layer if l.name Parcels][0] ce.setSelection([target_layer]) # 4. 执行 Generate Models ce.generateModels() # 5. 导出 GLB路径含时间戳 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) export_path fexports/{scenario}_{timestamp}.glb ce.export(export_path, glb, {embedTextures: True}) print(f✅ 已导出{export_path}) if __name__ __main__: if len(sys.argv) 2: print(用法python batch_generate.py --scenario baseline|high_density|eco_zone) sys.exit(1) scenario sys.argv[2] update_params_cga(scenario) generate_and_export(scenario)执行方式python batch_generate.py --scenario eco_zone。脚本会自动完成全部操作无需 GUI 干预。5.3 版本归档与回滚用 Git 管理城市形态演进把params.cga、rules/*.cga、assets/全部纳入 Git 仓库。每次重大方案调整如“将商业区限高从 40m 提至 60m”提交时写明影响范围git add rules/params.cga git commit -m chore(city): eco_zone 场景升级 —— 商业区限高提升至 60m退界增加至 12m教训从那以后我每次做方案比选都强制走一遍batch_generate.py流程把params.cga提交到 Git并在 Confluence 上贴出各版本 GLB 的在线预览链接。再也不用翻聊天记录找“上周那个绿色屋顶的版本”。城市形态的每一次进化都有迹可循有据可查。希望帮到你。本文还有配套的精品资源点击获取