教程 8:体量建模

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

生成的建筑物视图从侧面显示了纹理立面

在本教程中,您将学习如何使用 L 形和 U 形建筑物进行体量建模,如何将高度和退缩尺度变化应用于宗地,并最终生成包含纹理的外观多样的场景。

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

添加 L 和 U 形状

首先,您将使用 CGA 创建建筑物的 L 和 U 体量模型,这些是城市建筑物的常见类型。

创建规则文件

要创建规则文件,请执行以下操作:

  1. Navigator 窗口中展开 Tutorial_08_Mass_Modeling 教程文件夹。
  2. 打开 scenes 文件夹中的 01_MassModeling.cej 场景。

    当系统询问您“是否要重新生成这些模型?”时,请单击

    这是因为您稍后将在工作流应用规则并生成模型。

  3. 单击新建 > CityEngine > CGA 规则文件
  4. 单击下一步
  5. 将规则命名为 myMass_01.cga
  6. 单击完成

    系统随即创建一个新的 CGA 文件,并显示 CGA 编辑器窗口。 除了版本号之外,此为空文件。

添加 L 形状

首先,您将使用简单的 L 形状体量模型。

  1. myMass_01.cga 规则添加以下内容:

    attr height    = rand(30, 60)
    attr wingWidth = rand(10, 20)
    
    Lot --> 
    	LShape
    
    LShape -->
    	shapeL(wingWidth, wingWidth) { shape : LFootprint }
    
    LFootprint --> 
    	extrude(height) Mass
    
    LotInner -->
    	OpenSpace

    LShape 规则将创建 L 形状的覆盖区,尺寸范围在 10 到 20 米之间。 LFootprint 规则可将 L 形状拉伸到其高度。

    LotInner 将应用 OpenSpace,将地块形状保持原样。

    接下来,您需要将规则应用于地块。
  2. Ctrl+S 以保存规则文件。
  3. 此刻,您不需要街道来创建模型,因此单击可见性设置 可见性设置,并取消选择图形网络 (F10) 以隐藏街道。
  4. 要选择所有地块,请在 Scene Editor 窗口中右键单击 Streetnetwork 图层中的 Blocks 子图层,然后单击选择对象

    选择 Scene Editor 窗口中的地块

  5. 要将 myMass_01.cga 规则分配给地块,请执行以下操作:
    1. 确保已选择检查器窗口中的地块选项卡。
    2. 规则文件旁边,单击检查器窗口中的分配

      检查器窗口中的分配规则

    3. 单击分配规则文件对话框中的 myMass_01.cga 规则。
  6. 单击选择工具 选择工具 (Q) 以在视窗窗口中选择一些地块。
  7. 单击生成 生成 (Ctrl+G):

    小型城市部分上的简单 L 形状

仅显示 L 形体量模型可以运行,但使用不同模型形状的混合可能效果更好。 接下来,您需要将体量模型添加更多变化。

向 L 形状添加变化

当前的 L 形状侧翼始终位于主体量的左侧。 使用 rotateScope 操作,可以将 L 形放置在右侧。

  1. 使用概率运算符 % 更改 LShape 规则,以优化 L 形状,使其侧翼位于左侧或右侧。

    LShape -->
    	50%  :
    		shapeL(wingWidth, wingWidth) { shape : LFootprint }
    	else :
    		rotateScope(0, 90, 0) 
    		shapeL(wingWidth, wingWidth) { shape : LFootprint }

  2. 使用 convexify 操作,您可以将 L 形状分割为两翼,并更改两翼的高度。 您将再次编辑 LFootprint 规则,来使用随机概率来添加变化:

    LFootprint -->
    	75%  :
    		extrude(height) Mass
    	else :
    		convexify
    		comp(f) { 0   : extrude(height) Mass
    			| all : extrude(height*0.7) Mass }

  3. 保存规则文件并生成:

    可变 L 形状

现在,侧翼出现在左侧和右侧,并且高度有所不同。 现在,您将添加其他形状类型。

添加 U 形状

要添加 U 形状,请执行以下操作:

  1. 在起始 Lot 规则中调用 UShape 规则,而非 LShape 规则:

    Lot --> 
    	UShape

  2. 类似地,使用 shapeU 操作,并在 Lot 规则下方添加 UShapeUFootprint 规则:

    UShape -->
    	shapeU(wingWidth, wingWidth*0.7, wingWidth*0.7) { shape : UFootprint }
    
    UFootprint --> 
    	extrude(height) Mass

  3. 保存并生成:
    U 形模型
  4. 接下来,通过将某些 U 形随机旋转 180 度来添加一些变化:

    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 }

  5. 保存并生成:

    包括旋转的可变 U 形状

可以看出,U 形状并非在所有地块上都适用。 在下一部分中,您将组合 L 形状和 U 形状。

L 和 U 形状组合

组合 L 形状和 U 形状并添加一些高度分布方面的变化。

  1. 为了更好地控制建筑物的高度,您将向 height 属性添加条件,即仅大面积地块才能建造高层建筑物:

    attr height =
    	case geometry.area < 1000 : rand(20, 50)
    	else : rand(50, 150)

  2. Lot 规则中,调用 LUShape,而非 Ushape,并添加一个新规则 LUShape,来控制在哪些地块上创建哪种形状类型:

    Lot -->
    	LUShape
    
    LUShape -->
    	case scope.sx > scope.sz :
    		60%  : UShape
    		else : LShape
    	else : LShape

    U 形状最适用于宽度大于深度的地块,或使用 CGA 语法翻译的地块,其中 scope.sx 大于 scope.sz。 在所有其他情况下,您将仅触发 L 形状。

  3. 保存,并在这次选择更多形状以创建更多的建筑物群。
  4. 生成建筑物:

    L 和 U 形状组合

  5. L 形状和 U 形状都不适用于非矩形地块。 下一个 case 语句确保 UShapeLShape 模型仅在近似矩形地块(公差为 15 度)上创建。 否则,您将调用新的覆盖区规则:

    LUShape -->
    	case geometry.isRectangular(15) :
    		case scope.sx > scope.sz :
    			60%  : UShape
    			else : LShape
    		else : LShape
    	else : BasicFootprint

  6. 由于已拉伸地块形状相对于 L 形状和 U 形状过大,因此需要向 BasicFootprint 规则添加负偏移。 由此可在各个建筑物之间留出更多空间。 将其放置于 LFootprint 规则之后:

    BasicFootprint -->
    	offset(-5, inside)
    	extrude(height) Mass

  7. 保存并生成:

    与正态覆盖区拉伸组合的 L 形和 U 形

要查看当前场景的外观,需打开 01_MassModeling_.cej 场景而不保存。 当提示重新生成模型时,单击

在下一部分中,您将学习如何在形状语法中使用递归进行体量建模。

使用递归进行体量建模

现在,您将使用递归形状语法调用对重复的建筑物元素进行建模。

塔形状

  1. 创建一个名为 myMass_02.cga 的新规则文件。
  2. 向新规则文件添加以下内容:

    height =
    	case geometry.area > 1000 : rand(50, 200)
    	else: rand(20, 50)
    	
    Lot -->
    	Tower
    
    Tower -->
    	extrude(height) Envelope
    	
    Envelope -->
    	RecursiveSetbacks

    height 函数返回建筑物高度的随机值。 Lot 起始规则调用 Tower 规则,用于将覆盖区拉伸到塔形包络。 Envelope 规则调用递归规则。

  3. 对于以下递归规则,您需要三个附加变量:lowHeightscalefloorheight,并需要将它们放在高度函数之后。

    lowHeight =
    	50%  : 0.4
    	else : 0.6
    
    attr scale       = rand(0.75, 0.9)
    attr floorheight = rand(4, 5)

    由于对于建筑物而言,scale 需要保持不变,因此您需要将其定义为一个属性。 这是因为与函数不同,属性只在生成过程开始时计算一次。

  4. Envelope 规则后面添加 RecursiveSetbacks 规则:

    RecursiveSetbacks -->
    		case scope.sy > 2*floorheight :
    			split(y) { 'lowHeight : Mass | ~1 : Setback }
    		else :
    			s('1, floorheight, '1) Mass

    只要体量高于两层楼,RecursiveSetbacks 规则就会将其分割为相对高度为 lowHeight 的较低部分体量。 其余较高部分将生成 Setback 形状。

    如果 RecursiveSetbacks 形状小于两层楼,则其余部分的高度将设置为 floorheight,并生成 Mass 形状。

  5. 添加 Setback 规则,该规则将对形状进行缩放并居中,然后递归调用 RecursiveSetbacks 规则。

    Setback -->
    	s('scale, '1, 'scale)
    	center(xz)
    	RecursiveSetbacks

  6. 保存规则文件。
  7. 选择场景中的所有地块,并在检查器窗口中分配 myMass_2.cga 规则文件。

    上一规则中的简单建筑物群被更改为带有退缩尺度的塔:

    使用递归形状语法调用生成的模型

圆形

您可以使用外部圆柱资产创建递归塔的圆形版本。

  1. 修改 Envelope 规则:

    Envelope -->
    	case geometry.isRectangular(20) :
    		20%  : i("cyl.obj") RecursiveSetbacks
    		else : RecursiveSetbacks
    	else : RecursiveSetbacks

    在全部 20% 的塔中,您将插入圆柱资产,而非使用隐式立方体作为基础形状。

    圆柱资产与隐式立方体的比较

  2. 保存并生成:

    矩形和圆形混合递归塔

02_MassModeling.cej 场景显示了当前场景的外观。

在下一部分中,您将使用退缩尺度来修改宗地。

使宗地适应退缩尺度

对地块形状应用退缩尺度。

街道退缩尺度

要应用街道退缩尺度,请执行以下操作:

  1. 创建一个名为 myMass_03.cga 的新规则。
  2. 向新规则文件添加以下内容:

    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
    				| remainder    : Footprint }
    
    Footprint -->
    	extrude(height)
    
    OpenSpace -->
    	color("#77ff77")

    Parcel 规则会对地块的所有街道两侧应用退缩,并将该区域转至 OpenSpace 规则。 远离街道两侧的内部部分将转至 Footprint 规则,并将拉伸到随机高度。

    street.front 选择器评估 streetWidth 形状对象属性,将为从块创建的动态地块形状自动设置这些属性。 这些属性可能不存在于手动创建的形状上。

  3. 保存规则并选择所有地块。
  4. 在主菜单中,单击形状 > 删除模型,删除所有生成的模型。
  5. myMass_03.cga 规则分配给所有地块。
  6. 仅选择一个地块并生成:

    具有 street.front 退缩尺度的宗地

    您可以看到街道退缩尺度应用于所选地块。
  7. 选择其他地块并再次生成:

    多个仅包含街道退缩尺度的地块

    注意建筑物从街道的退缩尺度,但它们之间没有开放空间。

建筑物距离

现在,您将添加类似的退缩尺度来控制建筑物之间的距离。

  1. distanceStreet 属性下方添加 distanceBuildings 属性:

    attr distanceBuildings =
    	30%  : 0
    	else : rand(4, 8)

  2. 修改 Parcel 规则以在剩余部分调用 SubParcel 规则,并在其后添加新的 SubParcel 规则:

    Parcel -->
    	setback(distanceStreet) { street.front  : OpenSpace
    				| remainder     : SubParcel }
    
    SubParcel -->
    	setback(distanceBuildings/2) { !street.front  : OpenSpace
    				     | remainder      : Footprint }

    SubParcel 规则中,您再次应用退缩,但这次应用于非面向街道的边缘。

  3. 保存并生成。
  4. 检查器窗口中选择要探索的模型。

    在“检查器”窗口中手动设置的 distanceBuildings 和 distanceStreet 规则参数

    可以调整 distanceBuildingsdistanceStreet 规则参数以更改退缩距离。 可以在单个或多个模型上设置这些值。

    要重置为规则文件中的默认随机值,请单击任一参数值旁的下拉菜单,然后单击规则默认值

  5. 选择所有地块并生成:

    产生的退缩宗地

如果您希望查看带有退缩宗地的建筑物示例,则可以打开 03_MassModeling.cej 场景。

在下一部分中,您将组合前一部分中的体量模型和退缩宗地。

合并体量和退缩宗地

接下来,将体量模型与退缩宗地相结合。

导入 LU 形状和塔的体量规则

  1. 确保 myMass_03.cga 窗口处于活动状态,并将其另存为 myMass_04.cga
  2. 在版本说明之后,将以下两项导入内容添加到规则文件的顶部:

    import lushapes : "myMass_01.cga"
    import towers   : "myMass_02.cga"

    导入的规则文件中的所有规则、属性和函数现在可用于当前规则文件。

  3. 修改 Footprint 规则:

    Footprint -->
    	case geometry.isRectangular(15):
    		25%  : towers.Tower
    		else : lushapes.LUShape
    	else:
    		25%  : towers.Tower
    		else : lushapes.BasicFootprint

    Footprint 规则不再只是简单的拉伸,而是生成您在前面步骤中创建的更高级的体量模型。 case 语句中的 geometry.isRectangular 函数可确保仅在基本为矩形的形状上生成 LUShapes

  4. 保存规则文件。
  5. 选择所有地块并分配 myMass_04.cga 规则文件:

    生成的建筑物

    可以查看具有退缩尺度的所有地块,以及不同高度的方塔和圆塔的混合。

    城市混合了 LU 形状、塔形状和退缩宗地

04_MassModeling.cej 场景显示了带有退缩尺度的建筑物和塔。

在下一部分中,您将为体量模型添加纹理立面。

添加纹理立面

要添加纹理立面,请执行以下操作:

  1. myMass_04.cga 规则文件另存为 myMass_05.cga
  2. 在属性下方添加一个 const 函数,该函数从您的 12 个立面纹理贴片中随机选择一个:

    const randomFacadeTexture = fileRandom("*facade_textures/f*.tif")

  3. 接下来,添加 floorHeight 属性:

    attr floorheight = rand(4,5)

  4. 要正确地将纹理切片映射到立面上,需要定义两个函数,用于计算当前体谅的实际楼层高度和切片宽度:

    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

    借助这些函数,您可以确保在立面的边缘没有切掉任何纹理切片。

  5. 在规则文件底部添加一个 Mass 规则,使用组件分割从体量模型中获取立面组件:

    Mass --> 
    	comp(f){ side : Facade | top : Roof. }

  6. 接下来,指示导入的规则使用此 Mass 规则:

    towers.Mass -->
    	Mass
    	
    lushapes.Mass --> 
    	Mass

  7. 最后,在 Facade 规则中,根据实际情况在立面上设置 UV 坐标,使用 randomFacadeTexture 函数定义纹理文件,并投影 UV:

    Facade -->
    	setupProjection(0, scope.xy, 8*actualTileWidth, 8*actualFloorHeight)
    	texture(randomFacadeTexture)
    	projectUV(0)

  8. 保存规则文件。
  9. 选择所有地块并将 myMass_05.cga 规则分配给地块。
  10. 生成建筑物。

    生成的建筑物视图

    现在,模型具有随机纹理的立面。

    生成的建筑物视图显示了纹理立面

    生成的建筑物

    生成的建筑物视图从侧面显示了纹理立面

打开 05_MassModeling.cej 场景以查看最终的纹理建筑物。

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

  • 使用 L 形和 U 形建筑物生成体量建模。
  • 对宗地应用距离和退缩变化。
  • 创建具有多样化形状、高度和纹理的建筑物。

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