アドプロ 2018.07.02

効率的なモデリング

前回は, 複数の単純形状物体の組み合わせによる複雑形状物体の作成方法を学んだ.

今回は,さらに進んで, 類似形状の物体群(同種だが個体差のある物体たち)の効率的な定義方法を学ぶ.


#declare の限界

前回学んだ通り,#declare を利用することで, モデリングの作業を効率化できる:

#declare 物体名 = CSG演算 { ... }

object { 物体名 変換 }
ここで,「CSG 演算」は, mergedifferenceintersection, 「変換」は,前回説明の通り, scaletranslaterotate のことだ.

例えば,自動車を効率的に作る場合:

このように,類似形状の物体を複数個使う場合には, #declare を使えば,CSG 演算の記述回数が減り, 作業を素早く楽に進めることができるようになる.

ただし,#declare が有効に使えるのは, 物体同士の形状の違いが全体的に均等な場合(相似形)に限られる. 形状の違いが部分的に不均等な場合には使えない.

失敗例として,前回の鉄アレイと灰皿を流用して, 大きさの異なるものを作ってみよう:

失敗例:重さの異なる鉄アレイたち

シーンファイル:dumbbell.pov

// 鉄アレイの定義
#declare Dumbbell = merge { ... }

// 鉄アレイの配置
object { Dumbbell		// 標準の鉄アレイ
  pigment { color Brown }
}

object { Dumbbell scale 0.5	// 軽い鉄アレイ?
  translate -2.0*z
  pigment { color Navy }
}

一見,成功しているように見えるかもしれないが, よく観察してみよう.

どこが変?本来は同じハズの取っ手のサイズまでもが変わってしまった...

失敗例:深さの異なる灰皿たち

シーンファイル:ashtray.pov

// 灰皿の定義
#declare AshTray = difference { ... }

// 灰皿の配置
object { AshTray		// 標準の灰皿
  translate -2.5*x
  pigment { color Silver }
}
object { AshTray scale 3*y	// 深い灰皿?
  translate 2.5*x
  pigment { color Gold }
}

上の例と同様,よく観察してみよう.

どこが変?本来は同じハズの切込部の形状までもが変わってしまった... また,見えない部分ではあるが,底板の厚みも変わってしまっているハズ.

これらのように,全体的な変形だけでは,不自然なことになってしまう場合がある. しかし,改めて別形状(重さの異なる鉄アレイや深さの異なる灰皿など)として 定義し直すことも面倒である.

モノづくりの効率化を考える場合,勤勉は勉強の邪魔になる. 面倒を感じて,楽に済ます方法を考えよう. 不常識・非真面目に取り組もう.

不真面目な奴らは 「このままで問題ない」と主張し,問題を解決しない. 真面目な奴らは 複数の問題を個別に解決し続けようとし, その分,コストを増加させる. どちらも不適切だ.


可変物体の作成

物体形状が部分的に異なる場合 (ある部分だけは異なるが,他の部分は同じ場合)には, #declare ではなく, #macro を使うと非常に便利である:

#macro 物体名(引数, ...)
  CSG演算 { ... 引数 ... }
#end

object { 物体名(引数, ...) }

#macro では, 一般的なプログラミング言語(Cや Java 等)の関数定義やマクロ定義と同様に, 変化する部分を引数(変数など)によって制御することがポイントである.

変数の名前は,何でもよいが,予約語(特殊変数名や命令語)とかぶると NG. POV-Ray では,多くの予約語があるので, トラブル防止のために,変数名をすべて大文字にするのがよい. (すべて大文字からなる予約語はないので安心.)

#declare#macro の書き方の違いとして, 「=」や「#end」の有無にも注意が必要.

例:深さだけ異なる灰皿たち

では,ashtray.pov を元にして,次のように書き変えてみよう: (できるだけ,楽しよう.変えるべき部分だけ変えること.)

// 灰皿のマクロ定義(深さ HEIGHT)
#macro AshTray(HEIGHT)
difference {
  difference {
    object { Disk_Y scale <2.0, 0.5, 2.0> }                            // 本体
    object { Disk_Y scale <1.8, 0.5, 1.8> translate <0.0, 0.2, 0.0> }  // 内側
    scale HEIGHT*y		// 本体の高さを可変に(全体的な形状が可変)
  }
  merge {
    object { Disk_X scale <3.0, 0.2, 0.2> }  // 切込
    object { Disk_Z scale <0.2, 0.2, 3.0> }  // 切込
    translate HEIGHT*0.5*y	// 切込の位置を可変に(部分的な形状は不変)
  }
}
#end

// 灰皿の配置
object { AshTray(1)		// 標準の灰皿
  translate -2.5*x
  pigment { color Silver }
}
object { AshTray(3)		// 深い灰皿
  translate 2.5*x
  pigment { color Gold }
}

説明するまでもないだろうが..., 変数 HEIGHT13 に置き換えられて, 2個の AshTray が描かれることになる.

この通り,本体の寸法だけが異なり, 切込部の形状は同じであることがわかる. 上の失敗例と比べてみよう.

変数と計算式の利用

POV-Ray では,一般的なプログラミング言語と同様に,変数や計算式も利用できる. これは,物体の大きさや配置などを調整する際に便利である.

例えば,変数に計算結果を代入するには:

#declare 変数 = 計算式;

この「計算式」には, 四則演算{ +, -, *, / }の他, 一般的な数学関数{ sin( ), cos( ), sqrt( ), 等 } も利用できる. 詳しくは,マニュアル 1「言語の基本」を参照せよ.

ちなみに,POV-Ray の変数には,数値(式の値)の他, 色名や物体名など多種多様な物も代入できる.

なお,このように #declare された変数は, それ以降,そのシーンファイル全体に通用する. もし,マクロ内だけで通用する変数が欲しければ, #declare の代わりに, マクロ定義の内部で #local を使えばよい. より詳しくは,次回説明予定.

変数の #declare#local では, 末尾にセミコロンが必要. (物体の定義では,セミコロン不要.) 忘れやすいので注意.

アニメーション

特別な変数 clock を使うと簡単にパラパラアニメを作成できる. この変数の値は,レンダリング時に,0.0 〜 1.0 の間で自動的に変化する. ソースコード記述例:

object { ...
  rotate clock*360*y	// 0度〜360度の間で回転
}

レンダリング例:

$ ls
anim.pov

$ povray +KFI0 +KFF20 anim.pov 		# 0〜20 コマの画像を生成

$ ls  *.png
anim00.png
anim01.png
anim02.png
...
anim20.png

なお,povray コマンドの オプション +KFI および +KFF は, Key Frame(パラパラアニメの各コマ)の Initial(最初)および Final(最後)という意味であり, パラメータとしてフレーム番号を指定する. フレーム番号とクロック値の関係については, 教科書 ch.10 等を参照せよ.

動画ファイル作成例:

$ convert -delay 10 anim*.png anim.gif	# PNG画像列からGIFアニメを生成
$ animate anim.gif			# GIFアニメを再生

# または

$ animate -delay 10 anim*.png		# PNG画像列をアニメとして再生

なお,animate コマンドのオプション -delay の後の数値は,フレーム間の遅延時間(単位:ミリ秒)である.

フレーム番号指定について, +KFI は省略可能であり,省略すると最初のフレーム番号が 1 となる. 一方,clock の初期値はゼロなので, フレーム番号と時刻との対応関係がややこしくなってしまう. とゆーわけで,+KFI0 とするのがオススメ.

また,上図左のように循環的な (clock=0.0 の画像と 1.0 の画像が等しくなるような) アニメーションを生成するには, povray コマンドにオプション +KC も指定すること. これは Cyclic を意味し,数値パラメータを伴わない. もし,これを指定しないと,同じ内容のフレーム(最後と最初)が2コマ連続し, その時間だけアニメが一瞬止まって見えることになってしまう.

さらに,フレーム数を変えて再びレンダリングする場合には, レンダリングの前に,すべての画像ファイルを消しておくこと:

$ rm -f anim*.png	# PNGファイルを削除.-f は「強制的(forcedly)」に削除の意味.要注意.
注意:rm -f anim* とかで, シーンファイル anim.pov まで削除してしまわぬこと.

これを怠ると,アニメ化の際,フレーム順序が乱れてしまうだろう.


練習問題

#macro および #declare を駆使して, 類似形状物体を効率的に定義せよ. もし余裕があれば,さらにそれをアニメ化せよ.

次回は,多数の物体を自動的に配置したりする予定. 可変形状物体を多数並べれ!という課題もある. ということで,並べると楽しそうな物体を準備しておこう.

物体の例:

Tips: 効率的に定義するには, ひとつの #macro だけで定義するのではなく, 複数の #macro および #declare階層的に組み合わせるとよい. つまり,小さな部品を作り, それらを組み合わせてちょっと大きな部品を作り, それらを組み合わせてさらに大きな部品を作り, …,全体を構成する.

キーの部品構成の図解はこちらにある. シーンファイルの解読の参考にしよう.

階層的な定義を利用した作品例

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