教程 6:基本形状语法

要访问 ArcGIS CityEngine 中的教程工程,请启动 CityEngine,然后在主菜单中单击帮助 > 下载教程和示例。 在选择教程或示例后,系统会自动下载工程并将其添加到 CityEngine 工作空间。

生成的建筑物

本教程介绍了 CityEngine 的 CGA 形状语法的基础知识。 您将分析并扩展完成的规则文件,其中包含创建基本建筑物的所有步骤。

有关 CGA 形状语法的详细信息,请参阅基于规则的建模教程以及基于规则的建模CGA 建模帮助主题。

为简单建筑物建模

要构建具有典型立面的简单建筑物,请完成以下步骤:

  1. 导航器窗口中展开 Tutorial_06_Basic_Shape_Grammar 教程文件夹。
  2. 打开 scenes 文件夹中的 SimpleBuilding_01.cej 场景以查看以下建筑物:

    具有典型立面的简单建筑物

  3. 视窗中选择地块(或已生成的模型),然后在检查器窗口中查看信息。

    检查器窗口

    您将发现此处显示了两个重要参数:

    • 规则文件 - 包含到 /rules/simpleBuilding_01.cga 规则文件的链接。 触发生成时运行此规则文件。
    • 初始规则 - 定义在规则文件中运行的第一个规则。 在本例中,初始规则为 Lot 规则。
  4. 通过在检查器窗口中单击规则文件链接,或者在 Navigator 窗口中双击 simpleBuilding_01.cga 文件,以在 CGA 编辑器中打开文件,从而打开 CGA 规则文件。

在接下来的部分中,您将探索必要的属性、资产和规则以创建简单的建筑物。

建筑物属性

建筑物属性通常在规则文件的开头定义(尽管可将其放在规则文件中的任意位置)。 这些属性可用于整个规则集,并显示在检查器窗口的 CGA 属性映射区域不分中规则文件初始规则定义下方。也可以在 CGA 编辑器窗口外部对这些属性的值进行设置和修改。

以下是用于定义建筑物的属性:

attr groundfloor_height = 4
attr floor_height       = 3.5
attr tile_width         = 4
attr height             = 11

地块规则

该建筑物的创建从现在开始。 Lot 规则是在检查器窗口中分配的第一条规则。 可以使用拉伸操作创建体量模型:

Lot -->
    extrude(height)
    Building

建筑物规则

可以通过应用组件分割将体量模型分割为其立面:

Building -->
    comp(f) { front : Frontfacade | side : Sidefacade | top: Roof }

此规则通过应用组件分割将建筑物形状或体量模型分割为其立面。 此操作将产生一个正面形状(通常是带有入口的主前立面),多个侧面形状(例如立面)和一个屋顶形状。

现在可以对立面进行建模。 在经典的立面建模工作流中,可将立面分割为楼层,然后将楼层进一步分割为称为切片(楼层细分)的元素。 切片通常由墙壁和窗口元素组成。 此细分方案可应用于 CGA 形状语法,如下所示:

细分方案

Frontfacade 规则

Frontfacade 规则将前立面分割成高度为 4 米的底层形状,并重复(使用重复运算符 [*])高度约为 3.5 米的高层形状:

Frontfacade -->	
    split(y) { groundfloor_height : Groundfloor
             |    { ~floor_height : Floor}* }

~ 运算符保证不限建筑物的实际高度,始终会创建足够数量的较高楼层。 底层的外观通常与其他楼层不同,尤其是前立面。 底层的不同不仅是由于存在入口,而且还通常由于楼层高度尺寸、窗口外观、颜色等不同所导致。

Sidefacade 规则

Sidefacade 规则将侧立面分割为楼层形状。 为了确保楼层高度与前立面同步,将以同样的方式进行细分分割:

Sidefacade -->
    split(y) { groundfloor_height : Floor
             |    { ~floor_height : Floor}* }

Roof 规则

Roof 规则用于将屋顶形状向内偏移 0.4 米并稍微降低其高度,以创建平坦屋顶:

Roof -->
    offset(-0.4, inside)
    t(0, 0, -0.2)

楼层规则

Floor 规则是将楼层细分为宽度大约为 3 米的切片的典型示例。 为了使楼层设计更加有趣,您将在每侧分割一个宽度为 1 米的墙壁元素:

Floor -->
    split(x) {           0.5 : SolidWall
             | { ~tile_width : Tile }*
             |           0.5 : SolidWall }

Groundfloor 规则

Groundfloor 规则使用类似的细分分割来细化一楼形状,不同之处在于入口位于右侧:

Groundfloor -->
    split(x) {           0.5 : SolidWall
             | { ~tile_width : Tile }*
             |   ~tile_width : EntranceTile
             |           0.5 : SolidWall }

下图首先显示了不含分割的已拉伸体量模型,然后显示了带有楼层和切片分割的模型:

已拉伸体量模型
已拉伸体量模型
同一模型分解为楼层和切片
具有楼层和切片的体量模型

切片规则

定义初始立面结构后,即可进一步对 Tile 规则进行建模:

Tile -->
    split(x) {    1 : SolidWall
             | ~1.5 : split(y) { 0.4 : SolidWall | ~1.5 : Window | 0.4: SolidWall }
             |    1 : SolidWall }

Tile 规则可通过沿 x 轴和 y 轴分割(使用嵌套分割)定义切片的外观。 在此设计中,墙壁元素是浮动的(带有 ~),墙壁具有固定宽度 1 米,窗户上方和下方的高度均为 0.4 米。

EntranceTile 规则

EntranceTile 规则以类似于切片形状的方式定义入口形状(但是底部没有墙壁):

EntranceTile -->
    split(x) {  ~1 : SolidWall
             | 2.5 : split(y) { 3 : Door | ~2 : SolidWall }
             |  ~1 : SolidWall }

SolidWall、Window 和 Door 规则

SolidWallWindowDoor 规则用于向立面添加一定的深度和复杂性:

SolidWall -->
    s('1, '1, -0.4)
    primitiveCube()

Window -->
    t(0, 0, -0.2)
    split(y) { 0.1 : Frame
             |  ~1 : split(x)  {  0.1 : Frame | { ~1 : Glass | 0.1 : Frame }* }
             | 0.1 : Frame }

Door -->
    t(0, 0, -0.4)
    split(y) {   ~1 : split(x) { 0.15 : Frame 
                               |   ~1 : Panel 
                               | 0.05 : Frame 
                               |   ~1 : Panel 
                               | 0.15 : Frame }
             | 0.15 : Frame }

通过 s(x,y,z) 操作,可以在 SolidWall 规则中设置范围的大小。 由于使用了相对坐标,范围的宽度和高度不受影响:当前范围的 x 和 y 尺寸按一 ('1) 比例缩放,不会发生变化。 z 尺寸设置为 -0.4 米,导致墙壁厚度为 0.4 米(向内指向)。 primitiveCube() 运算会创建填充当前形状范围的立方体体积。

对于 WindowDoor 规则,当前形状通过 t(x,y,z) 运算在 z 方向上分别平移 -0.2 米和 -0.4 米。 这样,窗户会向立面内退缩 0.2 米,门在墙壁末端退缩 0.4 米。 然后沿 x 和 y 方向分割形状,以创建窗框和玻璃和面板部分。

请注意,CGA 编辑器窗口将显示一条警告,指示未定义 FrameGlassPanel 规则。 忽略该警告,您将在下一章中添加这些规则。

将所有这些规则组合在一起,您将得到最终的无纹理建筑物:

具有典型立面的简单建筑物

在下一节中,您将学习如何向建筑物添加纹理和颜色。

向建筑物添加纹理和颜色

现在,您将向建筑物添加纹理和颜色,方法为在规则文件的顶部定义所用的纹理和颜色。 将从 assets 文件夹中加载纹理。

  1. 如果未打开 SimpleBuilding_01.cej 场景,请打开该场景。
  2. 双击 simpleBuilding_01.cga 规则文件以将其打开。
  3. 在规则文件的属性下方添加以下新行:

    // textures & colors
    const wall_tex          = "facades/brick_white_02.jpg"
    const dirt_tex          = "dirtmaps/dirtmap_04.jpg"
    const roof_tex          = "roofs/flat_01.jpg"
    const window_color      = "#85acd6"
    const frame_color       = "#444444"
    const door_color        = "#666666"

    纹理和颜色均定义为常量,因此不会在检查器窗口中显示为规则属性。

  4. setupProjection() 命令添加至 FrontfacadeSidefacade 规则:

    Frontfacade -->
        setupProjection(0, scope.xy, 1.5, 1.5, 0, 0, 1)
        setupProjection(2, scope.xy, scope.sx, scope.sy)
        split(y) { groundfloor_height : Groundfloor
                 |    { ~floor_height : Floor}* }
    
    Sidefacade -->
        setupProjection(0, scope.xy, 1.5, 1.5, 0, 0, 1)
        setupProjection(2, scope.xy, scope.sx, scope.sy)
        split(y) { groundfloor_height : Floor
                 |    { ~floor_height : Floor}* }

    setupProjection() 命令为立面上的颜色(通道 0)和脏蚀贴图(通道 2)准备 UV 坐标投影,投影到范围的 xy 平面上;因此,将 scope.xy 设置为第二个参数。

    砖块纹理(通道 0)将在 x 轴和 y 轴上每 1.5 米重复一次。 脏蚀贴图(通道 2)将覆盖整个立面并使用 scope.sxscope.sy 作为大小参数。

  5. 编辑 Sidefacade 规则下方的 Roof 规则:

    Roof -->
        offset(-0.4, inside)
        t(0, 0, -0.2)
        setupProjection(0, scope.xy, scope.sx, scope.sy)
        texture(roof_tex)
        projectUV(0)

    Roof 规则将使用 UV 坐标以覆盖整个楼层表面,设置楼层纹理,并将纹理坐标应用于几何。

    保持建筑物形状选中,然后在 CGA 编辑器窗口中,每次编辑后进行保存 (Ctrl+S) 并生成 (Ctrl+G),以查看更改。

  6. 使用额外的组件分割编辑 SolidWall 规则,然后直接在其后面添加新的 Wall 规则:

    SolidWall -->
        s('1, '1, -0.4)
        primitiveCube()
        comp(f) { side : Wall | all : setupProjection(0, scope.xy, 1.5, 1.5, 0, '1) Wall }
    
    Wall -->
        texture(wall_tex)
        set(material.dirtmap, dirt_tex)
        projectUV(0) projectUV(2)

    组件分割用于将墙壁的侧面与顶部和底部部分分离。 这样可以将其他 setupProjection() 用于这些部分,以正确缩放和对齐纹理。

    Wall 规则中,颜色和脏蚀通道的纹理均设置为之前定义的常量。 还需要在这两个通道上投影 UV。

  7. Window 规则后面添加 GlassFrame 规则:

    Glass -->
        color(window_color)
        set(material.specular.r, 0.5) 
        set(material.specular.g, 0.5) 
        set(material.specular.b, 0.5)
        set(material.reflectivity, 0.5) 
        set(material.shininess, 50) 
        set(material.opacity, 0.9)
    
    Frame -->
        color(frame_color)

    color() 运算会将窗户和窗框颜色分别设置为蓝色和深灰色。 然后设置不同的材料属性,以使窗户看起来更加真实。

  8. Door 规则后面添加 Panel 规则:

    Panel -->
        color(door_color)

    在此最终步骤中,将门板颜色设置为比门框更浅的灰色。

    打开 simpleBuilding_02.cga 规则以查看完成的规则。

下图显示了最终的建筑物模型:

最终建筑物模型

同一模型的特写视图:

最终建筑物模型的特写视图

下图显示了适用于任意体量模型的规则设置:

适用于任意体量模型的规则设置

添加细节层次

在本部分中,您将向建筑物中添加简单细节层次 (LOD) 机制。 您将减少模型的复杂程度(面计数),这有助于创建较大面积的建筑物。

要添加新的 LOD 属性,请完成以下步骤:

  1. 打开 SimpleBuilding_02.cej 场景。
  2. 打开 simpleBuilding_02.cga 文件。
  3. 在其他属性下方添加新的 LOD 属性:

    @Enum(0,1)
    attr LOD                = 1

    您将定义以下 LOD:

    • LOD 0 - 细节层次低,复杂程度低
    • LOD 1 - 细节层次高,复杂程度高

    您刚刚建模的建筑物将为高分辨率模型。

    检查当前模型,您会发现可以主要在窗口资产上保存面。 您将使用纹理平面代替复杂的窗口资产。 接下来,您将创建模型的低分辨率版本。

    可利用 @Enum(0,1) 注记将检查器窗口中此属性的选项限制为 0 和 1。
  4. Roof 规则中,将现有代码置于 LOD > 0: 下方,并将纹理部分复制到 else 下方:

    Roof -->
    	case LOD > 0:
    		offset(-0.4, inside)
    		t(0, 0, -0.2)
    		setupProjection(0, scope.xy, scope.sx, scope.sy)
    		texture(roof_tex)
    		projectUV(0)
    	else:
    		setupProjection(0, scope.xy, scope.sx, scope.sy)
    		texture(roof_tex)
    		projectUV(0)

    您已向 Roof 规则添加了一个条件:如果 LOD 值大于 0(高分辨率),请使用偏移和较低的屋顶形状。 如果 LOD 属性为 0,仅使用基于初始组件分割的简单平面形状。

  5. 对于 Window 规则,保留两种情况的分割,但是仅在 LOD > 0 时应用嵌入:

    Window -->
        case LOD > 0:
            t(0, 0, -0.2)
            split(y) { 0.1 : Frame
                     |  ~1 : split(x) {  0.1 : Frame 
                                      | { ~1 : Glass | 0.1 : Frame }* }
                     | 0.1 : Frame }
        else:
            split(y) { 0.1 : Frame
                     |  ~1 : split(x) {  0.1 : Frame 
                                      | { ~1 : Glass | 0.1 : Frame }* }
                     | 0.1 : Frame }

  6. Door 规则执行相同的操作:当 LOD > 0 时不进行嵌入:

    Door -->
        case LOD > 0:
            t(0, 0, -0.4)
            split(y) {   ~1 : split(x) { 0.15 : Frame 
                                       |   ~1 : Panel 
                                       | 0.05 : Frame 
                                       |   ~1 : Panel 
                                       | 0.15 : Frame }
                     | 0.15 : Frame }
        else:
            split(y) {   ~1 : split(x) { 0.15 : Frame 
                                       |   ~1 : Panel 
                                       | 0.05 : Frame 
                                       |   ~1 : Panel 
                                       | 0.15 : Frame }
                     | 0.15 : Frame }

  7. 对于 SolidWall 规则,由于您已移除窗户和门的嵌入,因此不再需要实体元素:

    SolidWall -->
        case LOD > 0:
            s('1, '1, -0.4)
            primitiveCube()
            comp(f) { side : Wall 
                    |  all : setupProjection(0, scope.xy, 1.5, 1.5, 0, '1) Wall }
        else:
            Wall

    保存、生成,并查看检查器窗口中的属性。 此处显示了新的 LOD 属性。

  8. LOD 值更改为 0

    检查器窗口显示了属性

    源值将从规则更改为您提供的值。 建筑物模型随即生成,其中 LOD 值设置为 0

    建筑物的低级分辨率图像

  9. 5 将模型更改为无纹理或着色。

    d-d 以显示信息显示栏,其中显示了面计数。 如果 LOD 值设置为 0,则模型将从 2347 个面减少为 816 个面。

    LOD=1 的模型
    LOD=1 具有 2347 个面。
    LOD=0 的模型
    LOD=0 具有 816 个面。

在下一部分中,您将向建筑物添加随机变化。

向属性添加随机变化

最后,您将通过定义随机属性向已生成的建筑物添加变化:

  1. 打开 SimpleBuilding_03.cej 场景。
  2. 打开您刚刚创建的规则,或者双击 simpleBuilding_03.cga 文件。
  3. 使用以下内容,向建筑物属性添加变化:

    attr groundfloor_height = rand(4,7)
    attr floor_height       = rand(3.5,5)
    attr tile_width         = rand(3,6)
    attr height             = rand(11,25)

    这将添加宽度在 3 到 6 米之间的随机切片,高度在 11 到 25 米之间的随机建筑物,以及高度随机的楼层。

  4. 随机化纹理资产和窗户颜色:

    const wall_tex          = fileRandom("facades/*.jpg")
    const dirt_tex          = fileRandom("dirtmaps/*.jpg")
    const roof_tex          = fileRandom("roofs/*.jpg")
    const window_color      = 33%  : "#85acd6"
                              33%  : "#96acb3"
                              else : "#999999"

    使用 fileRandom() 函数时,将从墙壁、尘土和屋顶纹理的对应资产子文件夹中选择随机纹理。

    对于窗户颜色,使用随机函数定义三种颜色而不是一种。

  5. 要在地块中添加空间和变化,使用 setback 运算修改 Lot 规则:

    Lot --> 
     setback(rand(2, 6)) { all = color("#f0f0f0") Ground. | remainder : extrude(height) Building }

    这样,将地块设置为向内缩进 2 到 6 米;将外部地面形状上色成浅灰色,然后基于内部形状生成建筑物。

  6. 由于您将生成大量建筑物,因此请将规则中的默认 LOD 属性值从 1 更改为 0 并保存该规则:

    attr LOD = 0

  7. Scene Editor 窗口中选择 Streetnetwork 图层。
  8. 单击生成工具 生成 (Ctrl+G) 以生成建筑物:

    生成的建筑物

    根据以上内容生成的建筑物

打开 SimpleBuilding_04.cej 场景以查看具有随机属性的最终建筑物。

在本教程中,您学习了如何执行以下操作:

  • 创建生成简单建筑物所需的 CityEngine CGA 语法元素。
  • 向属性添加纹理、颜色、不同的细节层次以及随机性,从而使建筑物具有多样性。

要继续学习 CityEngine,请参阅完整的 CityEngine 教程目录