チュートリアル 9: 高度なシェープ グラマー

チュートリアル データ

チュートリアル データは、[Help] メニュー → [Download Tutorials and Examples…] を選択し、[CityEngine Tutorial] からダウンロードできます。

概要

写真から建物をモデリングする方法や、いくつかの高度な CGA テクニックについて学習します。

演習
Part 1: 複雑なファサード パターン
Part 2: キャンドラー ビルディング
Part 3: パルテノン神殿

Part 1: 複雑なファサード パターン

チュートリアルのセットアップ

ComplexPatterns.cej シーンを開きます。

建物の生成

このチュートリアルでは、実際の写真と CGA のみから建物を再現する CGA ルールを作成する方法を紹介します。
これからモデリングするファサードの写真は以下のようなものです。この例にはタイルや窓のレイアウトに特殊なパターンが含まれているため、入れ子式の繰り返し分割やパラメーターの受け渡しなどのいくつかのより高度な CGA メカニズムを使用する必要があります。

まず最初に、建物を生成します。

  1. [3D View] ウィンドウの中で区画を 1 つ選択します。

  2. ツールバーの [Generate] ボタンをクリックします。

ファサードの解析

新しいCGAルールを考える際には、ルールを書き始める前に大雑把なレイアウトをスケッチしていくつかのシェープの名前を定義しておくことをお勧めします。

ここでモデリングするファサードには、主に 3 つのフロアタイプ、つまり、最上階 (Top Floor)、地上階 (Ground Floor)、中間階 (Upper Floor) があります。下方の階は窓が含まれるタイルからなっていますが、最上階は窓要素のみから構成されています。また下方の階は、2 つの階ずつが同じ造りになっているので、特定の階について正しい状態 (タイルおよび窓の配置) になるように階のインデックス (floorIndex) を Floor シェープとともに渡す必要があります。

タイルのパターンに基づき、2 つの Tile シェープを含む DoubleTile を中間的に定義します。これは、階のパターンをコード化してしまってからの助けにもなります。

次に、タイルの中に詳細な下位シェープを定義します。これは 2 つの主要パーツからなります。 MilkGlass (すりガラス) および、上部に Blind (ブラインド) と埋め込まれた Subwindow (小窓) を持つ Window シェープです。 これらの要素の位置は各フロアにおける水平方向の位置によって決まるため、下位シェープを正しく配置できるように、この位置のインデックス (tileIndex と呼びます) を Tile シェープのパラメーターとして保存しておく必要があります。

  1. [Navigator] ウィンドウで Tutorial_09_Advanced_Shape_Grammar/rules/complexpatterns_01.cga ファイルをダブルクリックして [CGA Rule Editor] ウィンドウを起動し、ファサードを作成するルールを確認します。

属性、変数およびアセット

属性はルール ファイルの最初の部分で定義されています。これらはルール セット全体を通して使用され、[CGA Rule Editor] ウィンドウの外では [Window] メニュー → [Inspector] から変更することもできます。

// User Attribute
@Group("Building", 1)
@Range(min=5, max=40, restricted=false) @Distance
attr buildingH = 27 // 建物の高さ

@Group("Facade", 2)
@Range(min=3, max=6, restricted=false) @Distance
attr floorH = 3.5 // フロアの高さ
@Range(min=3, max=6, restricted=false) @Distance
attr groundfloorH = floorH + 1 // 地上階の高さ
@Range(min=1, max=4, stepsize=1, restricted=false)
attr nSymmetries = 2
@Range(min=0.1, max=1, restricted=false) @Distance
attr borderwallW = 0.3 // 境界壁ストリップの幅
@Range(min=0.1, max=0.8, restricted=false) @Distance
attr ledgeH = 0.3 // レッジの高さ

@Group("Window", 3)
@Range(min=1, max=5, restricted=false) @Distance
attr windowW = 2.5 // 窓の幅
@Range(min=1, max=5, restricted=false) @Distance
attr milkGlassW = windowW/2 //すりガラスのブレンド幅
@Range(min=0.1, max=2.5, restricted=false) @Distance
attr blindH = 0.8 // ブラインドの高さ
@Range(min=0.01, max=0.5, restricted=false) @Distance
attr frameW = 0.07 // 窓枠の幅

@Group("Balcony", 4)
@Range(min=3, max=6, restricted=false) @Distance
attr balconyDepth = 2

@Group("Colors", 5)
@Color
attr brightblue = "#86B1C7" 
@Color
attr darkblue = "#33556C"
@Color
attr red = "#5C3F40"
@Color
attr grey ="#6B7785"
@Color
attr white = "#FFFFFF"

@Group@Range のような属性に付与されたアノテーションは、[Inspector] ウィンドウでのこれらの表示をコントロールするためのものです。

その他の変数やアセットは次のブロックで定義されています。

tileW = windowW + milkGlassW	// タイル全体の幅
const barDiameter = 0.04

// アセット
const cyl_v = "general/primitives/cylinder.vert.8.notop.tex.obj"
const cyl_h = "general/primitives/cylinder.hor.8.notop.tex.obj"
const window_tex = "facade/windows/1_glass_2_blue.tif"
const milkGlass_tex = "facade/windows/blend_tex.png"

ここから実際の建物の作成が開始されます。まず、押し出し (extrude) オペレーションによりマス モデルが作成されます。最上階がメイン パートから分割 (split) され、さらに分割されてセットバックによりバルコニーが作成されます。

Lot --> 
	extrude(buildingH)  // 建物を立ち上げ
	split(y){ ~1: MainPart | floorH : UpperPart }  // 最上階を下方の階から分割
UpperPart --> 
	// 建物の奥行き方向に分割することによりセットバックを作成
	split(z){ ~1: TopFloor | balconyDepth : Balcony } 

続いて Component split を各ボリューム パートに適用し、これらの前面、側面、上面を区別してそれぞれから Facade、Wall、Roof ルールをトリガーします。

MainPart --> 
	comp(f){ front: Facade | side: Wall | top: Roof }  // 前面にファサード、側面に壁、上面に屋根を作成
	
TopFloor --> 
	comp(f){ front: Floor(-1) | side: Wall | top: Roof }  // 前面にフロア (最上階を-1としてマークする)、側面に壁、上面に屋根を作成

バルコニーのサイズを設定します。手すりを現在のシェープの面上に配置したいので、Railing ルールを適用する前面、左側面、右側面を component split で取得します。

Balcony -->
	s(scope.sx-2*borderwallW,0.7,scope.sz-borderwallW)	center(x) // バルコニーの高さ (手すりの高さ) を 0.7 メートルに設定
	comp(f){ front: Railing | left: Railing | right: Railing }

ファサードとフロア

ここでは、前面のファサードをさらに分割します。最初の分割ではファサードを地上階パートと繰り返し分割 {…}* により複数の中間階に分割します。分割サイズの前にチルダ記号 ~ (例:~groundfloorH) を付けることにより、フロア間がぴったり合うように高さを自在に変えてファサードに隙間ができるのを防ぐことができます。フロアのインデックスを表す分割インデックス ***split.index*** をパラメーターとして渡すことにより、後で特定のフロア フィーチャをトリガーすることができます。

Facade --> 
	// Façade を地上階と中間階の繰り返しとして分割する
// (すべてのフロアはフロア数を表すスプリット (分割) インデックスでマークされる)
	split(y){ ~groundfloorH: Floor(split.index) |  { ~floorH: Floor(split.index) }* }

各フロアの左右の端には、狭い Wall エリアがあります。単純に x 方向の分割によりこれを作成します。

Floor(floorIndex) --> 
	// 各フロアの両側面に狭い壁の要素を作成
	// floorIndex パラメーターはのちに使用されるために渡される
	split(x){borderwallW: Wall | ~1: FloorSub(floorIndex) | borderwallW: Wall }

水平方向の分割コマンドにより、フロア インデックスに基づいてフロアごとに特有な水平要素が作成されます。

  • 上位フロアには上部レッジのみを作成します。
  • 最上階には追加の要素は作成せずに TileRow シェープを直接トリガーします。
  • 後のルールで再度フロア インデックスを使用するため、TileRow シェープによりそれをパラメーターとして渡します。
FloorSub(floorIndex) -->	
	case floorIndex == 0: 			// インデックス 0 は地上階    
		split(y){ 1: Wall | ~1: TileRow(floorIndex) | ledgeH: Wall}  
	case floorIndex > 0:    			// 中間階
		split(y){ ~1: TileRow(floorIndex) | ledgeH: Ledge }
	else:                   			// インデックス -1 は最上階
    TileRow(floorIndex)

タイル

続いて各フロアをタイルに分割します。最上階は比較的簡単です。このフロアの特有のパターンとして Window 要素を繰り返すだけです。これらのタイルを後で使用するためにパラメーターを -1 としてマークしておきます。

メインフロアに対して特有の繰り返しパターンを作成するため、DoubleTile という中間シェープを作成します。後のステップで Window 要素を正しく配置するためにパラメーターとして渡されるフロア インデックスおよびタイル インデックス (split.index) が必要となります。

TileRow(floorIndex) --> 
	case floorIndex == -1:
		split(x){ ~windowW: Tile(-1) }*
// 最上階についてシェープ Tiles を繰り返し、再度 -1 でマークする
	else: 
		split(x){ ~tileW*nSymmetries: DoubleTile(floorIndex,split.index) }* 
// フロアをDoubleTile シェープにさらに分割し、フロア インデックスをパラメーターとして渡す

フロア インデックスとタイル インデックスの組み合わせにより Window 要素の並べ方が決定されます。したがって、MilkGlass および Tile シェープを異なる順序に繰り返し分割する 2 つのルールを作成します。

DoubleTile(floorIndex,tileIndex) -->    
	case tileIndex%2 + floorIndex%2 == 1: 
// 窓を右に揃える
		split(x){ ~milkGlassW: MilkGlass | ~windowW: Tile(tileIndex) }* 
	else:
// 窓を左に揃える 
		split(x){ ~windowW: Tile(tileIndex) | ~milkGlassW: MilkGlass }*

まず窓テクスチャ用のテクスチャ座標を設定します。Tile シェープ全体を水平方向に分割して窓フレームと中心パートに分けます。中心パートを再度垂直方向に分割して Frame、Window、Blind および Bracing に分けます。

Tile(tileIndex) --> 
	setupProjection(0,scope.xy,scope.sx,scope.sy)
// 窓に対するテクスチャ座標を設定
	split(x){ frameW: Frame Bracing
// 窓枠 (Frame) と窓の左側の突っ張り (Bracing) をトリガーする
	// 中央の窓を下から上に Frame、Window、Frame、Blind、Frame に分割する
		| ~1: split(y){ frameW: Frame 
					  | ~1: Window(tileIndex) 
					  | frameW: Frame  
					  | blindH: Blind 
					  | frameW: Frame }| frameW: Frame Bracing } // 窓の右側に窓枠と突っ張りを作成する

Windows (窓)

Window シェープに対しては、 DoubleTile のタイル インデックスにより小窓 (subwindows) の位置が決定されます。

Window(tileIndex) -->

この場合は、窓の左半分の中の右側に subwindows を配置した状態が作成されます。

case tileIndex%nSymmetries >= 1:  // Subwindows は DoubleTile の位置に応じて配置 		
 split(x){ ~1 : Subwindow("right") | frameW : Frame | ~1 : Glass             } 	// 窓の左半分で左揃え

この場合は、窓の右半分の中の左側に subwindows を配置した状態が作成されます。

case tileIndex%nSymmetries >= 0:
	split(x){ ~1: Glass | frameW: Frame | ~1: Subwindow("left") } // 窓の右半分で左揃え

最上階の窓を表すタイル インデックス -1 により subwindows のない窓が作成されます。

else:
	split(x){ ~1: Glass | frameW: Frame | ~1: Glass}

パラメーター “left” または “right"により、RedWindow が正しい位置に配置されます。

Subwindow(align) -->
	case align == "left": 
		split(x){~3: RedWindow | ~2: Glass}	// 左側に RedWindow を配置
	else: 
		split(x){~2: Glass  | ~3: RedWindow }	// 条件が偽なら右側に配置する

以下のルールでは RedWindow シェープに対する窓枠およびガラス要素が作成されます。

RedWindow --> 
	split(x){ frameW: RedFrame   // 左側...
			| ~1: split(y){ frameW: RedFrame
| ~1:RedGlass | frameW: RedFrame }  // ... 下側、上側 ...
		| frameW: RedFrame }	// ... 右側の枠

RedGlass -->
	split(y){ ~1: Glass | frameW/2: t(0,0,-frameW) Frame | ~1: t(0,0,-frameW) Glass }

材質

色とテクスチャを追加します。

Wall --> color(darkblue)

Blind --> color(grey)

Frame --> 
	extrude(frameW) color(white)	// 窓を前方に押し出し

RedFrame --> 
	t(0,0,-frameW) extrude(frameW*4) color(red)

Glass --> 
	projectUV(0)  // テクスチャ座標を現在のシェープ ジオメトリに適用し、
	texture(window_tex) color(white) // テクスチャと色を割り当てる
	set(material.specular.r, 0.4)
 set(material.specular.g, 0.4)
 set(material.specular.b, 0.4)
	set(material.shininess, 4)
	set(material.reflectivity,0.3)
	
MilkGlass --> 
	s('1,'1,frameW*1.2) i("builtin:cube") 
	color(brightblue)
	setupProjection(0, scope.xy, scope.sx, scope.sy)
 texture(milkGlass_tex)
 projectUV(0)
	set(material.specular.r, 0.7)
 set(material.specular.g, 0.7)
 set(material.specular.b, 0.7)
	set(material.shininess, 20)
 set(material.reflectivity,0.05)

細部の要素

フロアレッジに背面の壁要素、奥行きを与えるためのキューブ、カバープレートとして使用される2 つ目の薄いキューブを追加して作り込みます。

Ledge --> 
	Wall
	[ s('1,'0.9,0.2) i("builtin:cube") Wall ]	
	t(0,-0.1,0.2) s('1,scope.sy+0.1,0.03) i("builtin:cube") Wall

まず横棒を挿入して手すりの水平部分を作成します。縦方向のトリミングを無効にしてこの後作成される角の縦棒が切られるのを防ぎます。
縦棒はフローティングにした分割幅を繰り返すことで等間隔に配置します。

Railing --> 
	[ t(0,scope.sy-barDiameter/2,0) HBar ]
	set(trim.vertical, false)
	split(x){ ~tileW/3:  VBar }*

円筒アセットを挿入して縦棒と横棒を作成します。

VBar --> s(barDiameter,'1,barDiameter) t(0,0,-barDiameter) i(cyl_v) color(white)
HBar --> s('1,barDiameter,barDiameter) t(0,0,-barDiameter) i(cyl_h) color(white)

Bracing は窓の上下の張出しとその間に配置される縦棒からなります。張出しにはキューブを挿入し、ここでも VBar により円筒アセットをトリガーします。

Bracing --> 
	s(barDiameter,'1,0.15) center(x) i("builtin:cube")
split(y){ 0.01: Wall | ~1: t(0,0,0.15) VBar | 0.01: Wall }

これでモデルが完成しましたので、このルールを別の区画シェープに適用したり、[Inspector] ウィンドウにあるユーザー属性を変えてファサードを様々にデザインしてみましょう。

スタイルの変更

style キーワードを使用していくつかの属性を再定義する新しいスタイルを定義します。ここでは、color 属性を再定義することによりカラー バリエーションを作成します。

@Description("A Variation in Red")
style Red_Color_Theme
attr brightblue = "#FF8080"
attr darkblue = "#D20000"
attr grey = "#ECCACA"
attr red = "#361B1B"

Part 2: キャンドラー ビルディング

  1. シーン Candler_Building.cej を開きます。

  2. [3D View] ウィンドウの中で区画を選択します。

  3. 画面上部のツールバーから [Generate] ボタンをクリックします。

  4. [Navigator] ウィンドウより candler.01.cga ファイルをダブルクリックし、キャンドラー ビルディングを作成するためのルールを確認します。

Part 3: パルテノン神殿

  1. シーン Parthenon.cej を開きます。

  2. [3D View] ウィンドウの中で区画を選択します。

  3. 画面上部のツールバーから [Generate] ボタンをクリックします。

  4. [Navigator] ウィンドウより parthenon.cga ファイルをダブルクリックし、 パルテノン神殿を作成するためのルールを確認します。