本教程是使用CityEngine的CGA进行建筑建模的进阶教程。
原文链接:http://desktop.arcgis.com/en/cityengine/latest/tutorials/tutorial-8-mass-modeling.htm
1、L型和U型建筑
本教程介绍使用CGA形状语法构建大量建筑群,以L型和U型建筑为例。
步骤
- 导入工程** Tutorial_08_Mass_Modeling**。
- 打开场景文件** MassModeling_01.cej ** 。
建立规则文件
- 在‘rule’文件夹下新建一个规则文件。
- 确保‘container’ 被正确设置为 (Tutorial_08_Mass_Modeling/rules),将规则文件命名为myMass_01.cga.
- 确认后,新的CGA文件被创建,CGA编辑器打开。
L型建筑
下面从一个L形状开始。
attr height = rand(30, 60)
attr wingWidth = rand(10, 20)
Lot --> LShape
LShape -->
shapeL(wingWidth, wingWidth) { shape : LFootprint }
LFootprint --> extrude(height) Mass
LotInner --> OpenSpace
shapeL命令创建了一个平面的L形状,大小是10到20之间随机数。
LFootprint规则将L-shape 挤出高度height。
LotInner规则定义L形状的开放空间部分(缺口部分)。
脚本中的属性可以定义一些可选选项,如@Group、@Range等,用来控制这些属性在属性面板中的显示方式。在CGA参考文档中可以查阅更多CGA annotations相关信息。
现在可以给所有的lots应用规则了。
- 在场景管理器中选择Lots层(或者选中某个Block)。
- 点击菜单Shapes > Assign Rule File(也可在Block上点右键菜单找到该操作)。
- 选择myMass_01.cga规则文件,点击OK。
下面可以构建建筑了。
- 选中要构建建筑的lots。
- 点击菜单Shapes > Generate 或者直接快捷键Ctrl+G构建建筑。
大量的L-shaped建筑生成成功了。然而,他们看上去太死板,你需要对他们做更多的多样化。
多样化L shape
现在的 L shape建筑被定义在lot的左下角(即:缺口总是在右上角)。使用旋转操作可以改变建筑的朝向,从而增加多样化。
在规则文件中使用概率操作符 %,使其有一定的概率进行旋转。
LShape -->
50% : shapeL(wingWidth, wingWidth) { shape : LFootprint }
else : rotateScope(0, 90, 0)
shapeL(wingWidth, wingWidth) { shape : LFootprint }
进一步,使用convexify命令完成凸包分解。即将L建筑分成2个子建筑,并独立设置两个部分的高度。同样的,我们使用概率操作来控制该规则。
LFootprint -->
75% : extrude(height) Mass
else : convexify comp(f) { 0 : extrude(height) Mass |
all : extrude(height * 0.7) Mass }
此时已经拥有了旋转效果,且分割后的L建筑也有了不同的高度。下面我们需要跟多的形状。
U shape
下面开始创建U shape规则。在调用的时候注意把起始规则修改为UShape。
attr height = rand(30, 60)
attr wingWidth = rand(10, 20)
// LShape部分省略...
Lot --> UShape
UShape -->
shapeU(wingWidth, wingWidth * 0.7, wingWidth * 0.7)
{ shape : UFootprint }
UFootprint --> extrude(height) Mass
同样引入变化,将UShape规则修改为:
UShape -->
80% : rotateScope(0, 180, 0) shapeU(wingWidth, wingWidth * 0.7,
wingWidth * 0.7) { shape : UFootprint }
else: shapeU(wingWidth, wingWidth * 0.7, wingWidth * 0.7)
{ shape : UFootprint }
很明显, U shapes在某些lot上面效果不好。接下来我们来修复这个问题。
L、 U shapes混合
建筑高度的分布并不合理。要更好的控制建筑高度,你需要一个条件属性,即只有很大的lot才允许创建很高的建筑。
attr height =
case geometry.area < 1000: rand(20, 50)
else: rand(50, 150)
增加新的规则LUShapes来控制创建L shape还是U shape。
当lot的宽度比深度大的时候,更加适合创建U shapes——对应CGA中当scope.sx大于scope.sz的情况。其他情况我们创建L shapes。
Lot --> LUShapes
LUShapes -->
case scope.sx > scope.sz :
60% : UShape
else : LShape
else: LShape
无论L 还是 U shapes都不能很好的在非矩形lots上创建。因此,我们增加一个条件,保证lot近似矩形时才执行LUShapes规则 (可以有15度的误差)。否则,我们调用新的规则BasicFootprint,这个规则执行简单的挤出操作。
LUShapes -->
case geometry.isRectangular(15):
case scope.sx > scope.sz :
60% :UShape
else : LShape
else: LShape
else: BasicFootprint
简答的挤出不规则lot,在视觉上效果不太好——因为看上去太大了。因此,我们做一点修改,将其稍微缩小一点,即使用一个负数做offset偏移操作。这样的结果还可以保证建筑之间有一定的空间,不至于紧密的贴在一起。
BasicFootprint --> offset(-5,inside) extrude(height) Mass
下一节你将学习如何使用递归的方法来生成大量建筑。
2、递归生成大量建筑
步骤
本小节使用递归方法来生成重复的建筑元素。导入工程**MassModeling_01.cej **。
建立规则文件
- 在‘rule’文件夹下新建一个规则文件。
- 确保‘container’ 被正确设置为 (Tutorial_08_Mass_Modeling/rules),将规则文件命名为myMass_02.cga.
- 确认后,新的CGA文件被创建,CGA编辑器打开。
塔型
height属性定义的建筑的随机高度,起始规则调用Envelope来挤出塔身,Envelope规则是一个递归规则。
height =
case geometry.area > 1000: rand(50, 200)
else: rand(20, 50)
Lot --> Tower
Tower --> extrude(height) Envelope
Envelope --> RecursiveSetbacks
接下来的递归规则中,你需要2个变量: lowHeight,scale。
后者是个常量,因此需要定义为属性。
// 在两个值之间随机切换
lowHeight = 50%: 0.4 else: 0.6
// 常量
attr scale = rand(0.75, 0.9)
RecursiveSetbacks规则将高于2层的建筑进行切分。
如果形状RecursiveSetbacks低于2层楼高,则将其缩放为高度height,建立名为Mass 的形状。
attr floorheight = rand(4, 5) // 一层楼的高度(注意是绝对高度)
RecursiveSetbacks -->
case scope.sy > 2 * floorheight :// 下部比例高度固定,上部递归
split(y){ 'lowHeight : Mass | ~1: Setback }
else:
s('1, floorheight, '1) Mass // 最终顶楼的高度为一层楼
Setback规则缩放并居中形状,然后递归调用RecursiveSetbacks规则。
Setback -->
s('scale, '1, 'scale) center(xz) RecursiveSetbacks
将规则应用于Block,可见效果如下:
圆形
使用一个外部的圆柱体,可以创建屋顶为圆柱形的塔。修改塔规则:
Tower --> extrude(height) Envelope
Envelope -->
case geometry.isRectangular(20):
20% : i("cyl.obj") RecursiveSetbacks
else: RecursiveSetbacks
else: RecursiveSetbacks
这样,20%的概率下,塔会使用圆柱来替换原有的立方体。
下一节你将学会使用setbacks来修改大量建筑。
3、使用setbacks修改建筑群
步骤
本小节使用setbacks修改建筑群。(译者注:setbacks是一种类似前文所述进行offset偏移的操作。区别在于setbacks仅对多边形特定的边进行操作。)
导入工程**MassModeling_01.cej **。
建立规则文件
- 在‘rule’文件夹下新建一个规则文件。
- 确保‘container’ 被正确设置为 (Tutorial_08_Mass_Modeling/rules),将规则文件命名为myMass_03.cga.
街道SetBack
Parcel(译者注:不好翻译,指建筑群内的绿化带之类的土地区域。)规则将对lots所在多边形执行setback向内偏移操作 。偏移完成后,移动形成的靠近街道的部分生成Parcel,而其余部分挤出成建筑物部分。
(译者注:每指定一组参数执行一次setbacks,则多边形向内缩进形成新的多边形。一定要理解不同参数导致生成的不同多边形的位置和意义。如下图所示:)
attr height =
case geometry.area > 1000: rand(50, 200)
else: rand(20, 50)
attr distanceStreet =
20%: 0
else: rand(3, 6)
Lot --> Parcel
LotInner --> OpenSpace
Parcel -->
setback(distanceStreet)
{ street.front: OpenSpace // 靠近街道部分,作为Parcel处理
| remainder: Footprint } // 其余部分生成建筑
Footprint --> extrude(height)
OpenSpace --> color("#77ff77") // Parcel设置为绿色
注意:street.front选择子用于评估shape的streetWidth属性,如果shape是通过block建立的,则被自动设置。手动创建的shape则没有设置。
建筑距离
现在通过同样方式添加setback来控制建筑之间的距离。
增加一个distanceBuildings属性,修改Parcel规则,增加新规则SubParcel如下:
attr distanceBuildings =
30%: 0
else: rand(4, 8)
Parcel --> // 第1次缩进
setback(distanceStreet)
{ streetSide: OpenSpace // 左右边,即除去街道的临边和对边的剩余边
| remainder: SubParcel }// 剩余边
SubParcel --> // 第2次缩进
setback(distanceBuildings / 2)
{ noStreetSide: OpenSpace // 非街道的临边,缩进成绿地
| remainder: Footprint } // 其余部分生成建筑
Footprint --> extrude(height)
OpenSpace --> color("#77ff77")
这次会在不靠近街道的边上执行SubParcel 。
4、 多重的setback parcels
步骤
导入工程**MassModeling_01.cej **。
建立规则文件
打开massmodeling_03.cga规则文件,另存为myMass_04.cga。用已有的LUshape和塔的规则文件:
import lushapes : "massmodeling_01.cga"
import towers : "massmodeling_02.cga"
修改其中的Footprint规则:
Footprint -->
case geometry.isRectangular(15):
25% : towers.Tower
else : lushapes.LUShape
else:
25%: towers.Tower
else: offset(-5, inside) lushapes.BasicFootprint
指定给一组lots,执行生成。
下一节为建筑贴纹理。
5、增加立面纹理
打开massmodeling_04.cga规则文件,另存为myMass_05.cga.
接下来,我们写一个函数,来从12个立面纹理文件中随机挑选:
const randomFacadeTexture = fileRandom("*facade_textures/f*.tif")
为了正确的实施纹理映射到建筑立面,需要再定义2个函数来计算实际的楼层高度和切片宽度。
attr floorheight = rand(4,5)
actualFloorHeight =
case scope.sy >= floorheight : scope.sy/rint(scope.sy/floorheight)
else : scope.sy
actualTileWidth =
case scope.sx >= 2 : scope.sx/rint(scope.sx/4)
else : scope.sx
接下来实施组件分割,完成立面元素的构建:
Mass -->
comp(f){ side: Facade | top: Roof. }
指定引入规则中的Mass使用刚才定义的Mass。
towers.Mass --> Mass
lushapes.Mass --> Mass
最后,使用函数randomFacadeTexture设置纹理 UV坐标,并投影 UVs.
Facade -->
setupProjection(0, scope.xy, 8*actualTileWidth, 8*actualFloorHeight)
texture(randomFacadeTexture)
projectUV(0)