要访问教程CityEngine中,单击 帮助>下载教程和例子…。选择教程或示例后,项目会自动下载并添加到您的工作区。

对立面结构建模

本教程展示了如何根据图片对建筑物进行建模,并介绍了一些更复杂的 CGA 技术。在本节中,您将使用 CGA 规则创建外观的基本结构。

教程设置

  1. 将 Tutorial_07_Facade_Modeling 项目导入 CityEngine 工作区。
  2. 打开 Tutorial_07_Facade_Modeling/scenes/FacadeModeling_01.cej场景。

立面建模

本教程介绍了如何编写一组 CGA 规则以根据真实世界的照片重新创建外观。您要建模的立面如下图所示:

GUID-340655E4-26C5-428D-835B-CA12D549506B-web

 

随着您继续扩展规则集,您将不断更详细地分析照片。您将学习如何分析现有的现实世界外观并将其结构转换为 CGA 语法规则。您还将了解如何在 CGA 规则中使用预建模资产。

创建规则文件

    1. 单击新建> CityEngine > CGA 规则文件
    2. 确保容器设置正确(Tutorial_07_Facade_Modeling/rules),将文件命名为facade_01.cga,然后单击Finish
创建一个新的 CGA 文件,并打开 CGA 编辑器。除了一些头信息(/* …. */)和版本标签版本“2011.1”之外,它是空的。

体积和立面

建筑的实际创建现在开始。首先,使用拉伸操作创建质量模型。您将使用建筑物高度的属性。

    1. 将属性高度添加到规则文件的开头。
attr height 			= 24
    1. 使用 extrude 命令编写起始规则,并调用形状 Building。
Lot --> extrude(height) Building
    1. 对于本示例,您只对立面感兴趣,因此您将使用组件拆分来移除除正面之外的所有面,然后调用 Frontfacade 规则。
Building --> comp(f) { front : Frontfacade}
属性可以有可选的注释,例如@Group 或@Range,它们控制属性在检查器中的显示。有关 CGA 注释的详细信息,请参阅 CGA 参考。

楼层

正面被水平分割成不同的楼层,每个楼层都有一个 floor_height 属性。地板形状使用 split.index 参数化,这是地板索引。此参数将传递给子规则以确定要在特定楼层上创建哪些元素。

GUID-A1419AE4-EEC2-43CF-9235-729CBDF5A82F-web
    1. 对于顶层,floorindex 设置为 999。这使您可以轻松识别该楼层。
请注意中间楼层的重复拆分。这使建筑能够动态适应不同的建筑高度,并用中间楼层填充剩余的垂直空间。
    1. 定义地板尺寸的一些属性。
attr groundfloor_height 	= 5.5
attr floor_height 		= 4.5
    1. 添加规则。
Frontfacade -->	
	split(y){ groundfloor_height    : Floor(split.index) 	// Groundfloor
		| floor_height : Floor(split.index)   		// First Floor
		| floor_height : Floor(split.index)		 	// Second Floor
		| {~floor_height : Floor(split.index)}*		// Mid Floors
		| floor_height : Floor(999)			// Top Floor, indexed
                                                     // with 999
		| 0.5 : s('1,'1,0.3) LedgeAsset}   		// The top ledge just
                                                     // below the roof
现在您将第一次生成外观。
    1. 在 3D 视口中选择地块。
    2. 通过单击“形状” >“分配规则文件”分配规则文件,选择您的facade_01.cga规则文件并单击“确定”
    3. 单击顶部工具栏上的生成按钮(或按Ctrl-G)。
结果类似于下图:

GUID-08D488BD-936E-4673-8121-89745CBD836B-web

地板壁架

地板现在分为壁架和瓷砖形状。底部壁架特定于地板,因此您将使用此规则的 floorindex 参数。

  • 一楼(floorindex 0)没有壁架,因此仅称为瓷砖。
  • 因为窗户从地板开始,所以二楼没有底部壁架。这一层的阳台将在后面的步骤中创建。
  • 所有其他楼层都有底部壁架、瓷砖和顶部壁架区域。

 

GUID-A53B0118-06AA-4847-B12B-C97DAAC97FBB-web
Floor(floorindex) -->
	case floorindex == 0 : 
		Subfloor(floorindex)
	case floorindex == 2 : 
		split(y){~1 : Subfloor(floorindex) | 0.5 : TopLedge}
	else : 
		split(y){1 : BottomLedge(floorindex) 
                  | ~1 : Subfloor(floorindex) | 0.5 : TopLedge}
GUID-0B765350-988F-415E-8AE2-DF871361D746-web

底层地板

底层地板由左右边缘的小墙区域和中间的重复瓷砖组成。

GUID-06EB9F2D-88EE-43C0-9A50-DB0AA767E073-web
    1. 在顶部添加以下属性:
attr tile_width 		= 3.1
    1. 接下来,添加规则:
Subfloor(floorindex) -->
	split(x){ 0.5 : Wall(1) 
			| { ~tile_width : Tile(floorindex) }* 
			| 0.5 : Wall(1) }

此时,添加了参数化的墙壁形状。这对于稍后您将纹理立面时很重要。查看立面照片,您会注意到三种墙壁类型:

  • 具有泥土质地的深色砖块
  • 具有泥土质地的亮砖
  • 仅污垢纹理。这主要用于没有砖结构的立面资产。
正如您从以下规则中看到的,墙壁样式产生相同的输出。在后面的步骤中,您将为墙壁类型添加不同的纹理。
attr wallColor          = "#ffffff"
Wall(walltype) -->
	// dark bricks with dirt
	case walltype == 1 :
		color(wallColor)
	// bright bricks with dirt	
	case walltype == 2 :
		color(wallColor)
	// dirt only	
	else :
		color(wallColor)
GUID-0D43975C-346E-4DB2-A1FA-7439828D86E2-web

这个立面上的瓷砖是同质的。您只需要区分底层瓷砖和较高楼层。

GUID-6708D776-1DAF-43A2-B3FF-C1F3CF2AED46-web

使用 door_width 和 window_width 属性设置不同的分割尺寸。

attr door_width		= 2.1	
attr window_width		= 1.4

Tile(floorindex) -->
	case floorindex == 0 :
		split(x){ ~1 : SolidWall 
			|  door_width : DoorTile
			| ~1 : SolidWall }		 
	else : 	
		split(x){ ~1 : Wall(getWalltype(floorindex))
			|  window_width : WindowTile(floorindex)
			| ~1 : Wall(getWalltype(floorindex)) }

 

对于底层瓷砖,添加了新的 SolidWall 形状。这是必要的,因为一楼的门是从立面插入的。为避免门和墙之间出现孔洞,您将通过插入具有定义厚度的立方体来使用实心墙元素。因为您将在稍后的 Door 规则中再次使用此厚度,所以您将其定义为 const 变量 wall_inset。声明多次使用的值是一个好主意,因为它确保在不同的规则中使用相同的值。

const wall_inset = 0.4

SolidWall -->
		s('1,'1,wall_inset) t(0,0,-wall_inset)
		i("builtin:cube:notex")	
		Wall(1)

 

声明一个函数以从地板索引中获取墙壁类型。看着立面,你会看到一楼和一楼有深色纹理,其他地方有明亮的纹理。getWalltype 函数将地板索引映射到相应的墙类型。

getWalltype(floorindex) = 
	case floorindex == 0 : 1
	case floorindex == 1 : 1
	else : 2
GUID-6FB5FB0A-6328-4AA3-9A10-098B878B4177-web

 

现在您将学习如何在这个门面上使用资产。

插入外观资产

本教程的这一部分描述了如何在外观上使用预建模资产。

  1. 如果Tutorial_07_Facade_Modeling/scenes/FacadeModeling_02.cej场景文件尚未打开,请打开它。
  2. 打开Tutorial_07_Facade_Modeling/rules/facade_02.cga 文件。

资产

查看您要建模的立面照片,您会发现您需要以下资产:

  • Window—用于窗口元素
  • 圆形窗台——用于窗户上方的装饰品
  • 三角窗台——用于窗户上方的装饰品
  • 半弧——用于底层的弧
  • 壁架 – 用于所有壁架
  • Modillion—用于底层的窗饰和弧形饰物
GUID-C2155F96-2425-4083-B4A6-BE960546914C-web

这些资产已经存在于教程项目的资产文件夹中。您可以通过在导航器中选择所需的资产,在 CityEngine Inspector 中预览这些资产。

GUID-C3FED8E3-88EB-4624-B3B1-B81A12E5A2C3-web

 

资产申报

将所有资产声明放在同一位置是个好主意。将以下行添加到您的规则文件的属性声明下方:

const window_asset 		= "facades/elem.window.frame.obj"
const round_wintop_asset 	= "facades/round_windowtop.obj"
const tri_wintop_asset 		= "facades/triangle_windowtop.obj"
const halfarc_asset 		= "facades/arc_thin.obj"
const ledge_asset 		= 
                           "facades/ledge.03.twopart_lessprojection.obj"
const modillion_asset 		=
             "facades/ledge_modillion.03.for_cornice_ledge_closed.lod0.obj"

窗户

    1. 您已经为窗口资产的确切位置准备好了规则。在 WindowTile 规则中调用 Window 形状。
WindowTile(floorindex) --> Window
    1. 添加以下规则以缩放、定位和插入您的窗口资产和后面的玻璃平面:
Window -->
	s('1,'1,0.2) t(0,0,-0.2)
	t(0,0,0.02)
	[ i(window_asset) Wall(0) ]
	Glass
GUID-00D8743F-6C49-4C03-989C-71FFD9B168EB-web

窗饰

再次查看立面照片,您会注意到不同楼层有不同的窗户(或窗户元素)。

GUID-97CAE6B0-1DE9-422D-BFF3-9A171FBFDFBF-web

您需要扩展 WindowTile 规则并触发特定于地板索引的形状,如下所示:

  • 一楼和顶楼无特殊装饰(索引1和999);因此,只有 Window 被调用。
  • 在二楼,您将插入一个额外的 Shape WindowOrnamentRound。由于此元素将与窗口的顶部边框对齐,因此您将当前 Scope 向上平移到 y 轴,值为 ‘1。
  • 其他窗户瓷砖(在中间楼层)获得额外的 WindowLedge 形状,以及 WindowOrnamentTriangle 装饰,再次沿 y 平移。
WindowTile(floorindex) -->
	case floorindex == 1 || floorindex == 999: Window
	case floorindex == 2 : Window t(0,'1,0) WindowOrnamentRound
	else : Window WindowLedge t(0,'1,0) WindowOrnamentTriangle

您将首先插入代理多维数据集,而不是直接使用最终资产。这样可以更轻松地设置实际资产的尺寸。在这种情况下,您可以使用内置的多维数据集资产。

设置尺寸,使范围在 x 轴上居中,插入立方体,并为其着色以获得更好的可见性。

WindowOrnamentTriangle --> 
	s('1.7, 1.2, 0.3) center(x) i("builtin:cube")  color("#ff0000") 

# set dimensions for the triangle window element and insert it
WindowOrnamentRound -->
	s('1.7, 1.2, 0.4) center(x) i("builtin:cube") color("#00ff00")

WindowLedge -->
	s('1.5, 0.2, 0.1) t(0,-0.2,0) center(x) i("builtin:cube")
color("#0000ff")

 

GUID-969DA945-B804-403F-844C-9A1AF8577EE0-web
GUID-FDA1D5D8-0CBF-463A-95BD-47EBB4C00BD5-web

实物资产的交换代理

您的窗饰的尺寸似乎合理,因此您可以插入实际资产而不是立方体(对于 WindowLedge,与立方体保持一致)。

WindowOrnamentTriangle --> 
	s('1.7, 1.2, 0.3) center(x) i(tri_wintop_asset)

WindowOrnamentRound -->
	s('1.7, 1.2, 0.4) center(x) i(round_wintop_asset)
GUID-68A04B40-9188-4981-A9A1-D635EED9C401-web
GUID-4A0A04FD-E93B-48CC-B755-7E726921762E-web

您还没有完成二楼的窗户:圆形窗户装饰品缺少侧柱。您需要向之前添加的 WindowOrnamentRound 规则添加一个新的拆分命令。此行将为以下 modillion 资产准备范围。

WindowOrnamentRound -->
		s('1.7, 1.2, 0.4) center(x) i(round_wintop_asset) Wall(0)
		split(x){~1 : WindowMod | window_width : NIL | ~1 : WindowMod }

设置尺寸,并插入模型资产。请注意,通过在 y 方向应用相对负平移 (‘-1),资产的顶部与装饰品的底面对齐。

WindowMod -->
	s(0.2,'1.3,'0.6) t(0,'-1,0) center(x) i(modillion_asset) Wall(0)
GUID-2C21C225-7627-45AB-B89E-29B8384915C2-web

门砖垂直拆分为门、弧形和弧顶区域。

GUID-8EDAD727-5B82-4BC5-A082-A0935CB9D02B-web

为了保证非椭圆弧,弧区域的高度需要是门宽(当前x范围)的一半。

DoorTile -->
	split(y){~1 : Door | scope.sx/2 : Arcs | 0.5 : Arctop}

在顶部区域,插入了一个墙元素和一个叠加的模型资产。

# Adds wall material and a centered modillion
Arctop -->
	Wall(1)
	s(0.5,'1,0.3) center(x) i(modillion_asset) Wall(1)

弧线区域再次拆分,插入两个弧线资产。使用您之前定义的 wall_inset 变量。您需要旋转右半弧以正确定位。

Arcs -->  
	s('1,'1,wall_inset) t(0,0,-wall_inset)
	Doortop
	i("builtin:cube")
	split(x){ ~1 : ArcAsset 
			| ~1 : r(scopeCenter,0,0,-90) ArcAsset}

在 Doortop 和 Door 上设置 Wall 形状,并插入实际的弧资产。

Doortop -->	Wall(0)
		
Door --> t(0,0,-wall_inset) Wall(0)

ArcAsset --> i(halfarc_asset) Wall(1)
GUID-6196FD30-2608-4089-9531-A9406930837D-web

壁架

对于壁架,您需要顶部和底部壁架的规则。顶部壁架使用简单的墙壁条纹,底部壁架需要在插入实际壁架资产的不同楼层上进行区分。

TopLedge --> WallStripe 

BottomLedge(floorindex) -->
	case floorindex == 1 : split(y){~1 : Wall(0) | ~1 : s('1,'1,0.2) LedgeAsset}
	case floorindex == 999 : split(y){~1 : WallStripe | ~1 : s('1,'1,0.2) LedgeAsset}
	else : WallStripe

WallStripe --> split(x){ 0.5 : Wall(1) | ~1 : Wall(2) | 0.5 : Wall(1) } 

LedgeAsset --> i(ledge_asset) Wall(0)
GUID-6CBF3E71-6107-480B-8F13-0809342359E2-web
GUID-4FBDA8B6-D719-474E-8809-746AECE7B4B5-web

阳台

现在你将在阳台上工作。

    1. 使用楼层规则,并将阳台形状添加到二楼案例。
case floorindex == 2 : 
		split(y){~1 : Subfloor(floorindex) Balcony | 0.5 : TopLedge}
    1. 从一个简单的代理开始,以确保阳台的位置和尺寸。
Balcony --> 
	s('1,2,1) t(0,-0.3,0) i("builtin:cube") color("#99ff55")
GUID-F21F888E-E769-4F18-9367-280DBFABAA36-web
    1. 阳台框现在拆分为其组件:梁、地板和栏杆。
      GUID-7D9FD925-AB3D-4211-826C-7D26D53518D5-web
Balcony --> 
	s('1,2,1) t(0,-0.3,0) i("builtin:cube") 
	split(y){0.2 : BalconyBeams 
			| 0.3 : BalconyFloor 
			| 1 : RailingBox }
GUID-537601ED-E32E-448D-88AF-48924FD10884-web
    1. 支撑阳台的梁是通过重复分割创建的。
BalconyBeams -->			
	split(x){ ~0.4 : s(0.2,'1,'0.9) center(x)  Wall(0) }*
BalconyFloor 形状仅触发墙规则。

BalconyFloor --> Wall(0)
    1. 使用栏杆箱上的拆分组件,提取阳台栏杆所需的面。
# Get the front, left and right components (faces) of the RailingBox shape
RailingBox -->
	comp(f){front : Rail | left : Rail | right : Rail}
GUID-B25FE105-E4DE-4FDD-9303-9B8CB69D74E1-web
    1. 将尺寸插入设置为立方体以创建阳台栏杆。
Rail --> 
	 s('1.1,'1,0.1) t(0,0,-0.1) center(x) i("builtin:cube") Wall(0)

带有几何资源的最终立面

现在你有了最终的模型。将规则应用于不同的地块形状,或在 Inspector 中探索用户属性以修改您的外观设计。

GUID-5359BA93-6532-42F3-86DE-F468D0E65AEB-web
GUID-E4F81817-7060-482A-A714-B2EBDD8DFF5F-web

在下一节中,您将学习如何将纹理应用到这个立面。

纹理立面

本节展示了纹理化立面的技巧。

  1. 如果Tutorial_07_Facade_Modeling/scenes/FacadeModeling_03.cej场景文件尚未打开,请打开它。
  2. 打开Tutorial_07_Facade_Modeling/rules/facade_03.cga文件。

纹理资产

    1. 在规则文件的顶部,添加将用作属性的纹理。
const wall_tex				= "facades/textures/brickwall.jpg"
const wall2_tex				= "facades/textures/brickwall_bright.jpg"
const dirt_tex				= "facades/textures/dirtmap.15.tif"
const doortop_tex			= "facades/textures/doortoptex.jpg"
    1. 对于窗户和门纹理,您将使用一个函数来获取纹理字符串,因此您无需单独列出纹理。
randomWindowTex = fileRandom("*facades/textures/window.*.jpg")
randomDoorTex = fileRandom("*facades/textures/doortex.*.jpg")

设置全局 UV 坐标

使用形状语法的纹理由以下三个命令组成:

  • setupProjection()—定义 UV 坐标空间
  • set(material.map—设置纹理文件
  • projectUV() – 应用 UV 坐标

您将向立面添加两个纹理层:砖纹理和污垢贴图。要在整个立面上保持一致的纹理坐标,您需要将 UV 设置添加到立面规则中。为了预先测试纹理设置,您将添加一个新的中间 FrontfacadeTex 规则。

    1. 将建筑规则更改为以下内容:
Building --> comp(f) { front : FrontfacadeTex}
    1. 创建以下新规则:
FrontfacadeTex -->
	setupProjection(0, scope.xy, 2.25, 1.5, 1) 	
	setupProjection(2, scope.xy, '1, '1) 	
	Frontfacade
setupProjection(0, scope.xy, 2.25, 1.5, 1) 定义纹理通道 0(颜色通道)的纹理坐标。UV 坐标将沿示波器的 xy 平面投影,并在 x 方向上每 2.25 个单位重复一次,在 y 方向上每 1.5 个单位重复一次。将colormap设置为builtin:uvtest.png,这是一个快速检查UV坐标的纹理。然后通过烘焙通道 0 的 UV 坐标来应用 UV 坐标。
    1. 生成外观以查看 UV 设置。
      GUID-7A97A590-0539-4E15-9BB6-DE752A4619D3-web
    2. 添加污垢通道的 UV 设置。
FrontfacadeTex -->
	setupProjection(0, scope.xy, 2.25, 1.5, 1) 
	texture("builtin:uvtest.png")  
	projectUV(0)
	
	setupProjection(2, scope.xy, '1, '1) 	
	set(material.dirtmap, "builtin:uvtest.png")  
	projectUV(2)
此纹理应跨越整个立面,因此您将使用相对运算符 ‘1 和 ‘1 进行 UV 设置,它们是立面的尺寸。
    1. 再次生成门面,得到如下结果:
      GUID-BC71319D-4538-4FD9-97C1-4D54BD671968-web
    2. 要查看外观与实际纹理的外观,请将内置 uvtest 纹理与真实纹理交换。
FrontfacadeTex -->
	setupProjection(0, scope.xy, 2.25, 1.5, 1) 
	texture(wall_tex)  
	projectUV(0)
	
	setupUProjection(2, scope.xy, '1, '1) 	
	set(material.dirtmap, dirt_tex)  
	projectUV(2)
GUID-DBA218D4-A4DB-4998-A1F1-E3DF7CA81FE8-web
UV 坐标适用于立面。
    1. 对于实际的建筑,此时您只需要设置 UV,因此将 FrontfacadeTex 规则更改为以下内容:
FrontfacadeTex -->
	setupProjection(0, scope.xy, 2.25, 1.5, 1) 		
	setupProjection(2, scope.xy, '1, '1) 	
	Frontfacade
旧的 Frontfacade 规则现在为后续元素正确设置了 UV 坐标。

墙壁的纹理

回想一下,您向 Wall 规则添加了一个类型参数。您现在将使用它来获得具有不同纹理的三种墙壁类型。

    1. 将墙规则更改为以下内容:
Wall(walltype) -->
	// dark bricks with dirt
	case walltype == 1 :
		color(wallColor)
		texture(wall_tex)  
		set(material.dirtmap, dirt_tex)
		projectUV(0) projectUV(2)
	// bright bricks with dirt	
	case walltype == 2 :
		color(wallColor)
		texture(wall2_tex)  
		set(material.dirtmap, dirt_tex)
		projectUV(0) projectUV(2)
	// dirt only	
	else :
		color(wallColor)
		set(material.dirtmap, dirt_tex)
		projectUV(2)
所有墙壁元素都有纹理。

GUID-15C4CD15-E68A-45E9-9EFB-9E3C29F7DE2D-web

纹理窗口资产

对于窗口资源,您将使用一组窗口纹理为玻璃窗格着色,因此您需要设置 UV 坐标以跨越整个玻璃形状。这是通过对 x 和 y 方向使用 ‘1 来完成的。

    1. 调用您之前为纹理调用定义的 randomWindowTex 函数。
Glass -->
	setupProjection(0,scope.xy, '1, '1)
	projectUV(0)
	texture(randomWindowTex)
    1. 为玻璃添加镜面光泽。
Glass -->
	setupProjection(0,scope.xy, '1, '1)
	projectUV(0)
	texture(randomWindowTex) 
	set(material.specular.r, 1) set(material.specular.g, 1)   
      set(material.specular.b, 1)
	set(material.shininess, 4)

纹理门的形状

门平面的纹理与窗玻璃相同。

Doortop -->
	setupProjection(0, scope.xy, '1, '1)
	texture(doortop_tex) 
	projectUV(0)
Door -->
	t(0,0,-wall_inset)
	setupProjection(0,scope.xy, '1, '1)
	texture(randomDoorTex) 
	projectUV(0)
GUID-6596F59E-08D1-42BE-9E14-44482EF8F227-web