本着从示例中学习使用规则建模的目的,学习一下CityEngine中的教程。前几个教程是熟悉软件以及如何使用规则的,第六个教程是开始使用规则建模,并给建筑物贴纹理的。因此一边学习教程6中的步骤,一边查看其中创建规则的CGA具体语法,希望我们都能从教程中有很多收获。前提要了解如何创建规则和应用规则。
本次学习的教程为Tutorial_06_Basic_Shape_Grammar__2011_1,其中包含四部分,分别为:1.构建简单建筑物;2.为简单建筑物贴纹理;3.添加LOD;4.建筑物属性随机变化。本节学习该教程中的第一部分。
本节学习最终要构建一个如下图的建筑物,该建筑物有地面一楼和其他楼层,一楼的正面有一个入口的门,其他窗口都使用的是一个提前做好的OBJ模型。
下面开始创建规则进行建模:
为了更好的理解规则,我们自己创建一个新的规则,按照教程中的语句进行规则的书写。
1. 在规则文件的最开始处定义建筑的属性(也可以放在规则文件的其他位置)。在CGA文件中,这些属性将对整个规则文件产生作用。这些属性将显示在属性查看器(Inspector)中,可通过属性查看器修改这些属性。
attr groundfloor_height = 4 //地面一楼的高度 attr floor_height = 3.5 //其他楼层的高度 attr tile_width = 3 //将楼面按块划分的宽度 attr height = 11 //楼高 attr wallColor = "#fefefe" //墙面颜色
2. 教程中构建的窗户是使用的一个已经建好的窗户模型window.obj,这个文件存放在assets文件夹中,使用之前也要先定义出来。
window_asset = "facades/window.obj" //指定obj文件
3. 下面我们定义第一条规则为Lot. 在属性检查器中,该规则被指定为开始规则。大量的模型是使用拉伸操作创建而来的:
//对shape使用height中定义的高度进行拉伸,并命名为Building Lot --> extrude(height) Building
拉伸之后如下图:
4. 可以通过应用comp()将Building分解为多个面, 生成了正面(FrontFacade)、多个侧面(SideFacade)和一个顶面(Roof)
Building-->
comp(f){ front : FrontFacade | side : SideFacade | top: Roof}
5. 分解完成之后,就开始开始对这些面进行外观造型。典型的外观造型流程如下:1,将面分解为楼层(Floors)。2,将楼层分解为块(Tile),每一块通常由墙面和窗口构成。这样的细分过程在CGA要素语法的实现过程如下图:
//下面的FrontFacade规则将正面沿y轴方向,分割为两大部分,第一部分高度为groundfloor_height的地面一层Groundfloor,剩余的以floor_height高度进行重复分割(以*符号标记),分割为多个Floor。
FrontFacade --> split(y){ groundfloor_height : Groundfloor | { ~floor_height: Floor }* }
正面分割之后如下图:
6. 细分侧面:
SideFacade -->
split(y){ groundfloor_height: Floor | { ~floor_height: Floor }* }
SideFacade规则将侧面沿y轴方向分割也分为两大部分,这两部分使用的都是是相同的Floor对象,因此侧面这两大部分看起来都应该是一样的。只有高度不一样,高度不一样主要是为了与正前面的楼层高度保持一致。
侧面分割之后如下图,三个侧面都是一致的:
7. 继续对Floor对象进行细化:
//先给每层楼在x轴方向的两端画出宽度为1的墙面(Wall),剩余的部分以tile_width为宽度重复分割(Tile)
Floor --> split(x){ 1: Wall | { ~tile_ Tile }* | 1 : Wall }
如下图:
8. 最后对正面的地面一楼进行细化:
//同样先在x轴方向的两端画出宽度为1的墙面(Wall),按照tile_width划分为多个Tile,并按照tile_width划分出一个入口EntranceTile。 Groundfloor -->split(x){ 1: Wall
|{ ~tile_ Tile }*
| ~ tile_ EntranceTile | 1: Wall }
结果如下图:
9. 下面对Tile进行定义:
//先对Tile在x轴方向中间划分出宽度为2的一部分(对这部分再按y轴方向先划分一个高度为分别为1和1.5的wall和window,剩余的高度也定为wall),然后两边分别划分为宽度大概为1的wall Tile -->
split(x){ ~1 : Wall
| 2 : split(y){ 1: Wall | 1.5: Window | ~1: Wall }
| ~1 : Wall }
结果如下:
10. 对EntranceTile进行定义:
EntranceTile -->
split(x){ ~1 : SolidWall
| 2 : split(y){ 2.5: Door | ~2: SolidWall }
| ~1 : SolidWall }
先对入口在x轴方向划分宽度为2的一部分(这部分在y轴方向先划分2.5高度的Door,剩余的大约高度2定为SolidWall.),然后两边各为大约1米的Solidwall。
11. 最后对前面定义的Window,Door,Wall和SolidWall写具体的规则。
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")
这里解释一下上面出现的几个命令:
① s('1,'1,0.4):
命令:s(float xSize, float ySize, float zSize)
作用设置形状的尺寸。
eg1: s(5,5,5) 表示将三个方向的值设为绝对值5
eg2: s('0.5,'1,'1.5) 等同于 s(0.5*scope.sx,scope.sy,1.5*scope.sz),如果加上'符号,括号内的值在0-1之间,表示设置xyz的值为原先的多少倍。
②t(0,0,-0.25)
平移:t(tx, ty, tz)
③i(window_asset)或者i("builtin:cube:notex")
对象替换:i(geometryPath)
CityEngine有内置的素材可以直接使用,分为Geometry Assets和Textures。
Geometry Assets:
builtin:cube—有节点的单位立方体,带有纹理图层的纹理坐标系
builtin:cube:notex—单位立方体,不带有纹理坐标系
Textures:
builtin:default—表示16×16黑白方格相间的纹理
builtin:uvtest.png—使用CE标准的测试纹理
最终效果:
到此我们完成了简单建筑物的构建,学习了构建流程,如何拉伸、分解面、分割Tile等。规则可以一步步尝试,也可以替换成其他颜色或将obj替换为其他模型。如下图: