アドプロ 2018.07.09

モデリングの自動化(1)

POV-Ray のシーン記述言語は,実は, Cや Java などのプログラミング言語と同様に, 条件判断(選択)や繰り返し(反復) などの自動処理機能(制御構造)を備えている. また,すでに学んだように,変数や関数のような機能 (#declare#macro)も備えている. これらを活用すれば,非常に複雑なシーンを効率良く作成できるようになる.

今回は,プログラミング言語としての POV-Ray について理解するとともに, 複雑なシーンを自動的に生成するための方法について学ぶ.


制御構造

選択

条件に応じて処理(物体の配置など)を変更できる:

#if (条件式)
  ... 成立時の処理 ...
#else
  ... 不成立時の処理 ...
#end

不成立時の処理が特にない場合には,#else を省略してよい. これは,C言語などと同様.

使用例:if.pov

...
#macro Bar(V)
  object {
    Disk_Y translate 1*y scale 0.5*<0.8, V, 0.8>        // 長さ V の丸棒
  #if (V < 0)
    pigment { color Red }
  #else
    pigment { color Blue }
  #end
  }
#end

// 棒グラフ(bar chart)
object { Bar( 2.0) translate 1*x }
object { Bar( 3.0) translate 2*x }
object { Bar( 1.0) translate 3*x }
object { Bar(-1.0) translate 4*x }	// この棒だけ Red
object { Bar( 1.0) translate 5*x }
...

なお,条件式の比較演算子には, 次のものがある.

=	<	>	<=	>=

これらも,C言語などとほぼ同じだが, 等値演算子だけは例外である. == 」 ではなく= 」 であることに注意.

つまり POV-Ray では, 「=」の意味が状況によって変化する. #declare では「代入」, #if では「比較」.

一方,Cなどでは,「=」は常に「代入」を意味する. 混乱に注意.

また,複数の条件式を結合するための論理演算子 「&」と「|」もある. これらは,C言語の「&&」と「||」と同じである.

反復

条件が成立している限り,処理を繰り返す.

#while (条件式)
  ... 繰り返したい処理 ...
#end

使用例: while.pov

...
#declare I = 0;
#while ( I < 12 )
  object {
    Sphere translate 5*x
    rotate 30*I*y
    pigment { color Red }
  }
  #declare I = I + 1;
#end
...

この場合, I が 0,1,2,...,11 のときだけ,物体を描く. I が 12 になったときには, 物体を描かずに #end の次へ進む. 結果的に,12 個の物体が描かれることになる.

ここでは円周上に物体を整列させたが, その他の整列方法については, 教科書 ch.9 を参照せよ.

「一定回数の繰り返し」の場合, Cや Java では for 文が使える. POV-Ray ver.3.7 では,これと同様な #for 文がある. 上記 #while ループは #for 文によって コンパクトに記述できる:(POV-Ray ver.3.7 用)

#for (I, 0, 11, 1)	// 変数I を 0から 11まで 1刻みに変化
  object { Sphere ... }
#end

しかし残念ながら,演習室の POV-Ray ver.3.6 には #while 文しかないので, この授業では仕方なく,#while を利用する.

ちなみに,Cの反復2種類:

for (i = 0; i < 12; i++) {
  処理A;
}
i = 0;
while (i < 12) {
  処理A;
  i++;
}

とは等価. 上記の while.pov のコードと比較せよ.


データ構造

配列

とにかく使ってみよう.while.pov を元に変更:

...
#declare C = array[3] { Red, Green, Orange }

// 上の 1行は,次の 4行と同じことだ.
//   #declare C = array[3];  // 配列要素 C[0], C[1], C[2] が用意される
//   #declare C[0] = Red;
//   #declare C[1] = Green;
//   #declare C[2] = Orange;

#declare I = 0;
#while ( I < 12 )
  object {
    Sphere translate 5*x
    rotate 30*I*y
    pigment { color C[mod(I, 3)] }
  }
  #declare I = I + 1;
#end
...
POV-Ray の変数には数値だけでなく,物体や CSG,文字列,等, さまざまなものを代入できる. 活用方法次第で,プログラミング作業をかなり強力に効率化してくれる. 上の例では,色名を代入してみた.

なお,mod( ) は剰余(割算の余り)を計算する関数である. I÷3 の剰余は,0,1,2 の内のどれかなので, C[mod(I, 3)] では, C[0]C[1]C[2] の内のどれかの色が使われることになる.

通用範囲

#declare された変数はシーンファイル全体に通用する. このため,同じ変数を #declare してしまうと, 不具合が発生する可能性がある.

特定の範囲内だけで通用するデータを定義したい場合には, #declare の代りに,#local を使えばよい. 特に,#macro の中では,大抵の場合, #declare を使わず, #local だけを使うべきである.

while.pov をさらに変更:

...
#declare C = array ...

// カボチャ
#macro Pumpkin(R)
  merge {
  #local I = 0;		// これを #declare に変えると...
  #while ( I < 6 )
    object { torus { 0.7, 0.3 } rotate I*30*z rotate 90*x scale R }
  #local I = I + 1;	// これも #declare に...
  #end
  }
#end				// このとき,I = 6 なので...

#declare I = 0;
#while ( I < 12 )		// この反復は永久に終了しない
  object {
    Pumpkin(1.0) translate 5*x	
    rotate 30*I*y
    pigment { color C[mod(I, 3)] }
  }
  #declare I = I + 1;		// ここで常に I = 7 のまま
#end
...

この例では,#local にすべき部分を #declare にしてしまうと, レンダリングが永久に完了しなくなってしまう. 強制終了は [Ctrl]+[C] キー

もちろん,#macro 内で設定する変数であっても, シーンファイル全体に通用させたい場合には, #declare を使ってよい. POV-Ray ではグローバル変数(全体に通用)が基本である.

一方,C言語等ではローカル変数(特定範囲内だけで通用)が基本である.


不規則性の導入

多数の物体が整然と配置されているシーンほど不自然なものはない. 乱数(random number)を利用して,自然感を表現しよう.

なお,POV-Ray の乱数(というか,コンピュータの乱数)は, 不規則に見えるが実は規則的な数列である. このため,乱数を使うときには,最初に一度, かき混ぜ(shuffle)が必要となる. かき混ぜには seed(整数値)を使い, 乱数の取得には rand( ) を使う.

関数 seed() の引数は「乱数の種」と呼ばれる. POV-Ray(コンピュータ)の乱数には再現性もある. 同じ種を使っていれば,いつレンダリングしても,同じ結果が得られる.

ちなみに,kame3d の乱数 Rand()では, 実行した時刻によって異なる種を利用しており, 意図的に再現性が崩されていた.

使用例:(while.pov を更に変更)

...
#declare S1 = seed(2015);	// 引数はテキトーでOK
#declare S2 = seed(703);	// 引数を変えると乱数列が変わる
...
#while ( I < 12 )
  object {
    Pumpkin(1.0) translate 5*x
    translate <rand(S1), rand(S1), 0.0>
    rotate 30*I*y
    pigment { color C[mod(int(10000*rand(S2)), 3)] }
  }
...

なお,rand( ) は 0.0 以上 1.0 以下の実数乱数である. int( ) は,実数を整数化するための関数である.

乱数の値域を変えるには,乱数に四則演算などを組み合わせるとよい. 例えば,MIN以上,MAX以下の乱数を生成する数式: (MAX - MIN)*rand(S) + MIN

レンダリング結果の不規則性が気に入らない場合には, seed( ) の引数を変更して調整しよう.


本日の課題

繰り返し(や乱数)を利用して, 多数の物体からなる複雑なシーンを効率的に記述せよ.

なるべく,前回の練習問題として作成した可変物体を多数配置し, しかも,ある程度の意味のあるシーンを構成しよう.

または代替案:

次のアニメーションでは, 多数の粒子(球)を,球形領域内のランダムな位置に配置し, 初期位置に応じたランダムな速度で放射状に移動させることによって, 爆発(explosion)を表現している:


boom.pov

担当教員へレポートを送信せよ:


(c) 2018, yanagawa@kushiro-ct.ac.jp