要访问 CityEngine 中的教程,请单击帮助 > 下载教程和示例。 在选择教程或示例后,系统会自动下载工程并将其添加到您的工作空间。
使用复杂的立面模式
本教程介绍了如何根据图片对建筑物进行建模,并介绍了一些更复杂的 CGA 技术。
设置
如果尚未打开 ComplexPatterns.cej 场景,请打开该场景。
生成建筑物
本工作流说明了如何创建一组 CGA 规则,以便使用全部 CGA 根据真实照片重建建筑物。 下图显示了要建模的立面。 由于本示例中的切片和窗口布局的模式很复杂,因此您需要使用一些高级 CGA 机制,例如嵌套重复分割和参数传递。
生成建筑物的 CGA 透视显示如下:
要提前生成建筑物,请完成以下步骤:
- 请在 3D 视窗中选择一个地块。
- 单击顶部工具栏中的生成按钮。
执行立面分析
规划新的 CGA 规则时,在开始编写规则之前,勾勒出粗略的布局并定义一些形状名称十分有帮助。
您要建模的立面主要由三种楼层类型组成:顶层、地面和上层。 较低的楼层由包含窗口的切片构成,而顶层仅包含窗口元素。 其他所有较低的楼层都是相同的,因此您将传递带有 Floor 形状的索引 (floorIndex),以便为特定楼层创建正确的外观(切片和窗口对齐)。
由于切片的模式,您将定义一个中间的 DoubleTile 形状,其中包含两个 Tile 形状,这在对楼层模式进行编码后十分有用。
接下来,您将在切片中定义详细的子形状。 它由两个主要部分组成:MilkGlass 和 Window 形状。 Window 形状在顶部包含一个盲区和一个嵌入的 Subwindow 形状。 这些元素的位置取决于切片在楼层上的水平位置,因此您需要将此位置索引(名为 tileIndex)存储为 Tile 形状的参数,以便能够正确放置子形状结构。
双击导航器窗口中的 complexpatterns_01.cga 文件,打开 CGA 编辑器,并查看创建立面的规则。
属性、变量和资产
属性在规则文件的开头定义。 这些属性将用于整个规则集,您可以在 CGA 语法编辑器外部单击窗口 > 检查器来修改属性。
// User Attribute
@Group("Building", 1)
@Range(min=5, max=40, restricted=false) @Distance
attr buildingH = 27 // building height
@Group("Facade", 2)
@Range(min=3, max=6, restricted=false) @Distance
attr floorH = 3.5 // floor height
@Range(min=3, max=6, restricted=false) @Distance
attr groundfloorH = floorH + 1 // groundfloor height
@Range(min=1, max=4, stepsize=1, restricted=false)
attr nSymmetries = 2
@Range(min=0.1, max=1, restricted=false) @Distance
attr borderwallW = 0.3 // width of border wall stripe
@Range(min=0.1, max=0.8, restricted=false) @Distance
attr ledgeH = 0.3 // ledge height
@Group("Window", 3)
@Range(min=1, max=5, restricted=false) @Distance
attr windowW = 2.5 // window width
@Range(min=1, max=5, restricted=false) @Distance
attr milkGlassW = windowW/2 // milkglass blend width
@Range(min=0.1, max=2.5, restricted=false) @Distance
attr blindH = 0.8 // blind height
@Range(min=0.01, max=0.5, restricted=false) @Distance
attr frameW = 0.07 // frame width
@Group("Balcony", 4)
@Range(min=3, max=6, restricted=false) @Distance
attr balconyDepth = 2
@Group("Colors", 5)
@Color
attr brightblue = "#86B1C7"
@Color
attr darkblue = "#33556C"
@Color
attr red = "#5C3F40"
@Color
attr grey ="#6B7785"
@Color
attr white = "#FFFFFF"
将诸如 @Group 或 @Range 等注记添加到属性中,以控制其在检查器窗口中的外观。
其他变量和资产在以下块中予以定义:
tileW = windowW + milkGlassW // total tile width
const barDiameter = 0.04
// assets
const cyl_v = "general/primitives/cylinder.vert.8.notop.tex.obj"
const cyl_h = "general/primitives/cylinder.hor.8.notop.tex.obj"
const window_tex = "facade/windows/1_glass_2_blue.tif"
const milkGlass_tex = "facade/windows/blend_tex.png"
该建筑物的实际创建从现在开始。 首先,通过拉伸操作创建体量模型。 将顶层与主体分割,然后再次分割以创建倒退阳台。
Lot -->
extrude(buildingH) // Extrude the building
split(y){ ~1: MainPart | floorH: UpperPart } // Split top floor from lower floors
// Create a set-back by splitting in the direction of the building depth
UpperPart -->
split(z){ ~1: TopFloor | balconyDepth: Balcony }
然后将组件分割应用于不同的体积部分,以区分正面、侧面和顶面,并触发 Facade、Wall 和 Roof 规则。
// Create a facade on the front face, walls on the side faces, and a roof on the top face
MainPart -->
comp(f){ front: Facade | side: Wall | top: Roof. }
// Create a floor (marked with -1 as top floor) on the front face,
walls on the side, and roof on the top face
TopFloor -->
comp(f){ front: Floor(-1) | side: Wall | top: Roof. }
已设置阳台的尺寸。 扶手将放置在当前形状的表面上,因此您将使用组件分割来获取 Railing 规则的前面、左面和右面。
// Set balcony height to 0.7 meters (railing height)
Balcony -->
s(scope.sx-2*borderwallW,0.7,scope.sz-borderwallW)
center(x)
comp(f){ front: Railing | left: Railing | right: Railing }
体积建模后显示粗略的建筑物形状:
立面和楼层
现在,您将进一步细分正立面。 首次分割借助重复分割 {...}* 将立面细分为底层部分和一组较高的楼层。 分割大小前的波形符号 (~)(例如 ~groundfloorH)支持使用灵活高度,并确保匹配的楼层在立面上没有孔。 通过传递 split.index(代表楼层索引)作为参数,您之后可以触发特定楼层要素。
// Split the facade into a groundfloor and repeated upper floors
Facade -->
split(y){ ~groundfloorH: Floor(split.index)
// (all floors are marked with their split index, which represents the floor number)
| { ~floorH: Floor(split.index) }* }
每个楼层的左边界和右边界都有狭窄的墙壁区域。 您将通过在 x 方向上进行简单的分割进行创建。
// create a narrow wall element on both sides of every floor.
Floor(floorIndex) -->
//the floorIndex parameter is passed on to be used later
split(x){borderwallW: Wall | ~1: FloorSub(floorIndex) | borderwallW: Wall }
根据楼层索引,现在使用水平分割命令为每个楼层创建特殊水平元素:
- 较高的楼层仅有顶部窗台。
- 顶层没有其他元素,可以直接触发 TileRow 形状。
- 您将在以后的规则中再次使用楼层索引,因此需将其再次分配为 TileRow 形状的参数。
FloorSub(floorIndex) -->
case floorIndex == 0: // ground floor with index 0.
split(y){ 1: Wall | ~1: TileRow(floorIndex) | ledgeH: Wall}
case floorIndex > 0: // upper floors
split(y){ ~1: TileRow(floorIndex) | ledgeH: Ledge }
else: TileRow(floorIndex) // topfloor with index -1.
显示包含楼层和窗台分割的立面:
切片
现在您要将楼层分割为切片。 对于顶层而言,没有特殊的模式,仅包含重复的窗口元素。 要稍后处理这些切片,请将这些切片标记为参数 -1。
要为主要楼层创建特殊的重复模式,您将创建一个名为 DoubleTile 的中间形状。 要在后面的步骤中正确地对齐窗口元素,您需要将楼层和切片索引 (split.index) 作为参数传递。
TileRow(floorIndex) -->
case floorIndex == -1:
split(x){ ~windowW: Tile(-1) }*
// Repeating shape Tiles on the top floor, marked again with -1
else:
split(x){ ~tileW*nSymmetries: DoubleTile(floorIndex,split.index) }*
// the floor is subdivided into regular DoubleTile shapes,
// the floor index is passed as parameter
楼层和切片索引的组合确定了窗口的对齐方式。 因此,您有两个规则,可以按不同顺序重复分割 MilkGlass 和 Tile 形状。
DoubleTile(floorIndex,tileIndex) -->
case tileIndex%2 + floorIndex%2 == 1:
// windows are right-aligned
split(x){ ~milkGlassW: MilkGlass | ~windowW: Tile(tileIndex) }*
else:
// windows are left-aligned
split(x){ ~windowW: Tile(tileIndex) | ~milkGlassW: MilkGlass }*
您将首先为将来的窗口纹理设置纹理坐标。 然后将整个 Tile 形状水平分割为窗框和中间部分。 再次分割中心部分,这次是垂直分割为框架、窗口、框架、百叶窗和框架。
Tile(tileIndex) -->
setupProjection(0,scope.xy,scope.sx,scope.sy)
// Set up the texture coordinates for the windows
split(x){ frameW: Frame Bracing
// This triggers the window frame as well as the bracing on the left side of the window
// the center window is split into Frame, Window, Frame, Blind, and Frame from bottom to top
| ~1: split(y){ frameW : Frame
| ~1 : Window(tileIndex)
| frameW : Frame
| blindH : Blind
// frame and bracing on the window's right side
| frameW : Frame }
| frameW: Frame Bracing }
Windows
对于窗户形状,DoubleTile 的切片索引用于确定子窗口的位置。
Window (tileIndex) -->
放置在窗口左半部分的右对齐子窗口。
case tileIndex%nSymmetries >= 1: // the Subwindows are aligned depending
// on the DoubleTile position
split(x){ ~1 : Subwindow("right")
| frameW : Frame
| ~1 : Glass } // right-aligned in the left half of the window
放置在窗口右半部分的左对齐子窗口。
case tileIndex%nSymmetries >= 0:
split(x){ ~1: Glass
| frameW: Frame
// left-aligned in the right half of the window
| ~1: Subwindow("left") }
代表顶层窗口的切片索引 -1 现在用于创建不含子窗口的窗口。
else:
split(x){ ~1: Glass | frameW: Frame | ~1: Glass}
使用左参数和右参数,将 RedWindow 放置在正确的位置。
Subwindow(align) -->
case align == "left":
split(x){~3: RedWindow | ~2: Glass} // Put the RedWindow to the left
else:
split(x){~2: Glass | ~3: RedWindow } // And to the right otherwise
以下规则为 RedWindow 形状创建框架和玻璃元素:
RedWindow -->
split(x){ frameW: RedFrame // left...
| ~1: split(y){ frameW: RedFrame
| ~1: RedGlass
| frameW: RedFrame } // ... bottom, top ...
| frameW: RedFrame } // ... and right frame
RedGlass -->
split(y){ ~1: Glass
| frameW/2: t(0,0,-frameW) Frame
| ~1: t(0,0,-frameW) Glass }
显示 RedWindow 形状的详细窗口几何:
添加材料
添加了颜色和纹理。
Wall --> color(darkblue)
Blind --> color(grey)
Frame -->
extrude(frameW) color(white) // extrude the frame to the front
RedFrame -->
t(0,0,-frameW) extrude(frameW*4) color(red)
Glass -->
projectUV(0) // apply texture coordinates to current shape geometry
texture(window_tex) color(white) // and assign texture and color
set(material.specular.r, 0.4)
set(material.specular.g, 0.4)
set(material.specular.b, 0.4)
set(material.shininess, 4)
set(material.reflectivity,0.3)
MilkGlass -->
s('1,'1,frameW*1.2) i("builtin:cube")
color(brightblue)
setupProjection(0, scope.xy, scope.sx, scope.sy, 0, 0, 0)
texture(milkGlass_tex)
projectUV(0)
set(material.specular.r, 0.7)
set(material.specular.g, 0.7)
set(material.specular.b, 0.7)
set(material.shininess, 20)
set(material.reflectivity,0.05)
应用材料规则之后已添加颜色和纹理的模型如下所示:
添加细节元素
您将通过添加后墙元素,具有深度的立方体和第二个作为盖板的薄立方体来优化楼层窗台。
Ledge -->
Wall
[ s('1,'0.9,0.2) i("builtin:cube") Wall ]
t(0,-0.1,0.2) s('1,scope.sy+0.1,0.03) i("builtin:cube") Wall
插入了水平条块以创建扶手的水平部分。 通过禁用垂直修剪,可以防止下面的垂直角条块被切割。
通过使用浮动分割宽度进行重复分割,垂直条块可均匀分布。
Railing -->
[ t(0,scope.sy-barDiameter/2,0) HBar ]
set(trim.vertical, false)
split(x){ ~tileW/3: VBar }*
插入圆柱资产以创建垂直条块和水平条块。
VBar --> s(barDiameter,'1,barDiameter) t(0,0,-barDiameter) i(cyl_v) color(white)
HBar --> s('1,barDiameter,barDiameter) t(0,0,-barDiameter) i(cyl_h) color(white)
窗口的支撑由顶部和底部固定装置以及中间的垂直条块组成。 对于固定装置,将插入一个立方体,然后 VBar 将再次触发圆柱资产。
Bracing -->
s(barDiameter,'1,0.15) center(x) i("builtin:cube")
split(y){ 0.01: Wall | ~1: t(0,0,0.15) VBar | 0.01: Wall }
添加了细节元素(包括窗台、窗撑和扶手)的最终模型:
现在您已经有了最终模型,请将规则应用于不同的地块形状,或者尝试使用检查器窗口中的用户属性来修改立面设计。
定义样式
您可以使用样式关键字来定义用于重新定义某些属性的新样式。 在这种情况下,您将通过重新定义颜色属性来创建颜色变化。
@Description("A Variation in Red")
style Red_Color_Theme
attr brightblue = "#FF8080"
attr darkblue = "#D20000"
attr grey = "#ECCACA"
attr red = "#361B1B"
应用了 Red_Color_Theme 样式:
下面的坎德勒和巴台农 CityEngine 场景是很好的示例,向您展示了 CGA 的不同可能性。
浏览坎德勒大楼场景
- 打开 Candler Building.cej 场景。
- 在导航器中双击 Candler Building.cga 文件,然后浏览创建坎德勒大楼的 CGA 规则。
浏览巴台农神庙场景
- 打开 Parthenon.cej 场景。
- 再次双击 parthenon.cga 文件以查看巴台农神庙背后的规则。