CGA建模教程——形状语法(基础篇)
本教程是使用CityEngine的CGA进行建筑建模的入门教程。
原文链接:http://desktop.arcgis.com/en/cityengine/latest/tutorials/tutorial-6-basic-shape-grammar.htm
1、构建简单建筑
本教程介绍CGA形状语法的基本使用。你将分析一个完整的创建建筑过程的规则文件。
步骤:
1. 导入工程Tutorial_06_Basic_Shape_Grammar。
2. 打开场景文件Tutorial_06_Basic_Shape_Grammar/scenes/01_SimpleBuilding.cej 。
你将创建一个具备典型立面结构的建筑,如下图所示:
在3D视图中选择该建筑,在检查视图中查看信息(如果没有该窗口,在Window > Inspector菜单中打开)
此处有2个重要的参数:
Rule File—指向规则文件rules/simpleBuilding.01.cga。当触发建筑生成时执行该规则文件。
Start Rule—定义执行规则文件时执行的第一个规则。此处的起始规则是Lot。
单击规则文件名字链接,或者双击rules/simpleBuilding.01.cga,打开CGA编辑器。
简单建筑规则集
建筑属性
建筑属性通常被定义在规则文件起始处。 (当然可以放在规则文件的任意位置)这些属性会被整个规则集合所用到,并显示在CGA属性映射区域,使得你可以在CGA编辑器外修改他们。
attr groundfloor_height = 4
attr floor_height = 3.5
attr tile_width = 3
attr height = 11
attr wallColor = "#fefefe"
窗的资源
窗是这个简单建筑预先定义好的模型资源。该资源是从工程的assets目录中加载的。
// geometries
window_asset = "facades/window.obj"
Lot规则
实际的建筑建造起点,即指示窗口中设定的第一个规则。
建筑模型通过如下的挤出操作而形成:
Lot -->
extrude(height) Building
Building规则
这是个典型的步骤。一个模型可以通过组件分割的方式分成多个面。
Building -->
comp(f){ front : FrontFacade | side : SideFacade | top: Roof}
该规则将名为Building的形状依照3个不同的部分展开分裂。第一个部分是front(即建筑的正面),第二个部分是side(建筑的多个侧面),第三个部分是Root(即屋顶部分)。
立面可以进一步构建、典型的立面建模流程如下:首先,立面能被分解成多个楼层;然后,楼层可以进一步分解为多个元素“瓦片”。典型的瓦片包括墙面和窗户等元素。
上述分解过程用CGA语法描述如下:
FrontFacade规则
FrontFacade规则将建筑前立面分裂出高度为4的1楼,然后将上面的剩余部分分成多个高为3.5的楼层(使用重复操作符 [ *]) 注意符号~保证无论建筑高度是多少,总是尽可能保证每一层的高度尽可能等于3.5。
第一层的外观和上面各层存在很大不同,如正门,不同的高度,门窗,颜色等等。
FrontFacade -->
split(y){ groundfloor_height : Groundfloor |
{ ~floor_height: Floor }* }
SideFacade rule
SideFacade规则
SideFacade规则将建筑的几个侧面分裂成多个楼层。分裂过程的代码和正面一样,以保证正面和侧面每一层的高度能够保持完全一致。
SideFacade -->
split(y){ groundfloor_height: Floor | { ~floor_height: Floor }* }
【注】此时如图所示:
Floor规则
Floor规则执行典型的分裂操作,使得分出来的每一个瓦片宽度逼近3。为了让楼层变得更加有趣你可以多分割一个宽度为1的墙面元素。
Floor -->
split(x){ 1: Wall
| { ~tile_width: Tile }*
| 1 : Wall }
Groundfloor规则
第一层的规则和上述分裂规则一样,唯一区别在于最右边有个大门入口。
Groundfloor -->
split(x){ 1: Wall
|{ ~tile_width: Tile }*
| ~tilewidth: EntranceTile
| 1: Wall }
上述分裂的结果如图所示:
Tile规则
下面开始构建瓦片内的元素。
Tile -->
split(x){ ~1 : Wall
| 2 : split(y){ ~1: Wall | 1.5: Window | ~1: Wall }
| ~1 : Wall }
瓦片规则,在一个瓦片内,沿着x轴和y轴方向执行分割。(通过嵌套分割)。
设计思路如下:
左边各一个墙面元素,宽度是浮动的(1左右);
中间一个窗户元素,大小是固定的(宽度为2,高度为1.5)。
【注】此时如图所示:
EntranceTile规则
EntranceTile规则通过类似的方式定义大门。显然的区别在于,大门下方不需要墙面元素。
EntranceTile -->
split(x){ ~1 : SolidWall
| 2 : split(y){ 2.5: Door | ~2: SolidWall }
| ~1 : SolidWall }
Window, Door, and Wall规则
最后的规则,用相应的模型资源来替换窗户、门、墙的形状对象,并给其赋予贴图。
Window -->
s('1,'1,0.4)
t(0,0,-0.25)
i(window_asset)
Door -->
s('1,'1,0.1)
t(0,0,-0.5)
i("builtin:cube")
Wall -->
color(wallColor)
SolidWall -->
color(wallColor)
s('1,'1,0.4)
t(0,0,-0.4)
i("builtin:cube:notex")
首先注意,代码中使用平移操作:t(x,y,z),使得窗户向z方向移动了-0.25,制造出窗户嵌入立面0.25米的效果。
其次,插入操作:i(objectname) 将相应的对象插入到当前形状所在的区域。如果不指定大小,模型会自动适应目标大小,否则将使用指定的大小。
大小操作:s(x,y,z)用于在x,y,z轴3个方向放缩目标。 以窗户为例,x,y轴使用(‘1) 使其不受变化影响,z轴设置为0.4,使其向外放大0.4米)
此时如图所示:
下一节你将学习如何贴纹理。
给建筑贴纹理
本小节你将学习如何给门窗等元素贴纹理。
教程步骤
- 打开Tutorial_06_Basic_Shape_Grammar/scenes/01_SimpleBuilding.cej 场景。
- 打开Tutorial_06_Basic_Shape_Grammar/rules/simpleBuilding.01.cga规则文件。
纹理定义
在规则文件的最开始定义纹理对应的资源文件。
1 增加如下纹理定义:
// textures
frontdoor_tex = "facades/textures/shopdoor.tif"
wall_tex = "facades/textures/brickwall.jpg"
dirt_tex = "facades/textures/dirtmap.15.tif"
roof_tex = "roofs/roof.tif"
assets/facade文件夹下有9张不同的窗户纹理。无需定义9个纹理,步骤2一次性返回这9个纹理中的一个。
2 增加如下代码:
randomWindowTexture = fileRandom("*facades/textures/window.*.tif")
3 增加setupProjection()命令给Frontfacade和Sidefacade规则:
FrontFacade --> // 原文此处拼写有误
setupProjection(0, scope.xy, 1.5, 1, 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, 1)
setupProjection(2, scope.xy, scope.sx, scope.sy)
split(y){ groundfloor_height: Floor | { ~floor_height: Floor }* }
setupProjection() 命令对设置纹理的UV坐标。
参数1:纹理通道编号。如颜色贴图通道为0, 污垢贴图通道为2。
参数2:映射坐标。scope.xy表示映射到xy平面。如砖块贴图(通道0)在 x方向上每隔1.5米重复一次,y方向1米重复一次。污垢纹理(通道2)扩展到整个立面,因此可使用参数scope.sx和scope.sy。
4 添加屋顶规则。
Roof -->
setupProjection(0, scope.xy, scope.sx, scope.sy)
texture(roof_tex)
projectUV(0)
屋顶规则将UV坐标覆盖整个面。
5 将texture()命令添加到如下代码中:
Window -->
s('1,'1,0.4)
t(0,0,-0.25)
texture(randomWindowTexture)
i(window_asset)
Door -->
s('1,'1,0.1)
t(0,0,-0.5)
texture(frontdoor_tex)
i("builtin:cube")
对于门窗元素,只需指定颜色通道的贴图即可。特别的,对于窗户,使用 randomWindowTexture() 函数来获取9张纹理中随机的纹理。
6 Wall和SolidWall使用UV坐标指定来贴图。为了对颜色通道和污垢通道进行贴图,还必须用projectUV函数将UV坐标映射到这2个通道上。
Wall -->
color(wallColor)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
projectUV(0) projectUV(2)
SolidWall -->
color(wallColor)
s('1,'1,0.4)
t(0,0,-0.4)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
i("builtin:cube:notex")
projectUV(0) projectUV(2)
```l
此时如图所示:
![这里写图片描述](http://desktop.arcgis.com/en/cityengine/latest/tutorials/GUID-5D082634-56C1-48A6-8ECA-60EF65894E41-web.png)
近距离特写如下:
![这里写图片描述](http://desktop.arcgis.com/en/cityengine/latest/tutorials/GUID-140D68F1-C2DF-40A4-AA07-E40AA6F93918-web.png)
下列图片展示了将该规则用于任意大小的建筑模型上的效果:
![这里写图片描述](http://desktop.arcgis.com/en/cityengine/latest/tutorials/GUID-BEABFDA8-A67A-40BA-80B5-660AF0A7326B-web.png)
----------
<div class="se-preview-section-delimiter"></div>
##增加多层次细节(LOD, level of detail)
本节你将学会添加简单的LOD机制。你将降低模型的复杂度(多边形数量),这对于构建大规模的简单建筑体尤其有用。
<div class="se-preview-section-delimiter"></div>
###步骤
1 打开**Tutorial_06_Basic_Shape_Grammar/scenes/02_SimpleBuilding.cej** 场景。
2 打开**Tutorial_06_Basic_Shape_Grammar/rules/simpleBuilding.02.cga** 规则文件。
<div class="se-preview-section-delimiter"></div>
###添加LOD属性
在规则文件中添加LOD属性。
<div class="se-preview-section-delimiter"></div>
```c
attr LOD = 1
本例我们使用2级LOD:
- LOD 0—低级别LOD,低复杂度。
- LOD 1—高级别LOD,高复杂度。
之前构建的模型可以作为高级别LOD模型。现在只需要简单几步即可完成低级别LOD模型构建。
检查当前模型,可以发现,简化窗户模型可以降低模型复杂度。可以使用戴文丽的平面来代替窗户模型。
- 在Window规则中添加LOD > 0的情况,并在其中使用texture(randomWindowTexture) 来完成窗户构建。如下所示:
Window -->
case LOD > 0 :
s('1,'1,0.4)
t(0,0,-0.25)
texture(randomWindowTexture)
i(window_asset)
else :
setupProjection(0,scope.xy,scope.sx,scope.sy)
texture(randomWindowTexture)
projectUV(0)
如果LOD值大于0(即高LOD),则使用之前的代码;否则,不在加载window模型资源,而是在当前的面上直接贴图。
2. Door规则一样。直接使用平面而不加载立方体模型:
Door -->
case LOD > 0 :
s('1,'1,0.1)
t(0,0,-0.5)
texture(frontdoor_tex)
i("builtin:cube")
else :
setupProjection(0,scope.xy,scope.sx,scope.sy)
texture(frontdoor_tex)
projectUV(0)
- 下面是SolidWall。因为你移除了门的内部,因此不在需要他了。
SolidWall -->
case LOD > 0 :
color(wallColor)
s('1,'1,0.4)
t(0,0,-0.4)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
i("builtin:cube:notex")
projectUV(0) projectUV(2)
else :
Wall
- 选中建筑,查看指示窗中新增的LOD属性,将其修改为0。
下次执行生成的时候,规则文件会用0来执行生成。
- 生成低LOD级别的建筑。
- 使用网格模式显示建筑(快捷键:6),并且去除显示纹理(快捷键:5),两者的差异非常明显。按2下d键,在视图的最上方查看模型的多边形数量。可以发现多边形数量从3699降低到了475。
下一节你将学会如何给模型添加随机属性。
建筑随机属性
下一节你将学会如何给模型添加随机属性以生成多样化的建筑。
步骤
1 打开Tutorial_06_Basic_Shape_Grammar/scenes/03_SimpleBuilding.cej 。
2 打开Tutorial_06_Basic_Shape_Grammar/rules/simpleBuilding.03.cga 规则文件。,
添加随机属性
1 你将给3个建筑属性添加多样化:建筑高度(8-35之间),瓦片宽度(2.5 ~6),三种随机的颜色。
attr tile_width = rand(2.5, 6)
attr height = rand(8, 35)
attr wallColor = 33% : "#ffffff"
33% : "#999999"
else : "#444444"
2 因为要生成大量建筑,将默认LOD设置为0。
attr LOD = 0
3 左边的场景管理列表中,选择Block层。
4 单击Generate生成建筑。