バリエーションとアニメーション

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

今回は,さらに進めて, 類似・可変形状(同種ではあるが個体や状況によって変化する形状)について 効率的な定義方法を学ぼう. そして,アニメーションを自動生成してみよう.


#declare の効能と限界

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

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

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

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

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

ただし,#declare が有効に使えるのは, 物体同士の形状の違いが全体的に均等な場合(相似形)に限られる. (要するに,少品種大量生産しか向いていない.) 形状の違いが部分的に不均等な場合には使えない.

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

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

シーンファイル dumbbell.pov:(ダウンロード)

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

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

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

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

どこが変?本来は同じハズの取っ手のサイズまでもが変わってしまった...
失敗例2:深さの異なる灰皿たち

シーンファイル 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 }
}
...

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

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

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

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

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

どちらも不適切だ.

一方,非真面目な人々は,複雑な問題を単純な方法で解決しようとする. 簡単ではないが,成功すれば,ローコスト・ハイバリューですね.

#macro による可変物体の作成

物体形状が部分的に異なる場合 (ある部分だけは異なるが,他の部分は同じ場合... 要するにバリエーション展開の場合)には, #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 が描かれることになる.

この通り,本体の寸法だけが異なり, 切込部の形状は同じであることがわかる.

上の #declare での失敗例と比べてみよう.
変数と計算式の利用

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

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

#declare 変数 = 計算式;

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

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

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

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

変数を利用したカップの定義例:(#declare版)

...

#declare H = 1.5;	// 高さ
#declare R = 1.0;	// 外径
#declare T = 0.1;	// 厚み
#declare RI = R - T;	// 内径
#declare Cup = difference {
	object { Disk_Y scale <R, H, R> }
	object { Disk_Y scale <RI, H, RI> translate y*T }
}

object { Cup pigment { ... } translate { ... } }
...

変数を利用したカップの定義例:(#merge版)

...

#macro Cup(H, R, T)
#local RI = R - T;	// 内径
difference {
	object { Disk_Y scale <R, H, R> }
	object { Disk_Y scale <RI, H, RI> translate y*T }
}
#end

object { Cup(1.5, 1.0, 0.1) pigment { ... } translate { ... } }
...

寸法等について,数値そのものを使うより,変数として名前を付けておけば, CSG演算の意味が分かり易く,形状の仕上がりが予想し易くなる. 要するに生産性が向上する.

練習として,この技を dumbell.pov や ashtray.pov に適用してみては?
アニメーション

特別な変数 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 マニュアル 15「アニメーション」を参照せよ.

動画ファイル作成例:

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

# または

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

なお,convert および animate コマンドの オプション -delay の後の数値は, フレーム間の遅延時間(単位:cs = センチ秒 = 1/100 秒)である.

フレーム番号の指定について, +KFI を省略すると最初のフレーム番号が1となる. 一方,clock の初期値はゼロなので, フレーム番号と時刻との対応関係とか, 番号と枚数との関係とか,ややこしいぞ... と云う訳で,通常の場合には,+KFI0 とし, 最初のフレーム番号をゼロとするのがオススメ. この場合,+KFF の数値は,画像の枚数−1を表わすことになる.

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

循環的アニメの場合には,+KFI を省略し, 最初のフレーム番号を1とするのがオススメ. この場合,+KFF の数値は,画像の枚数を表わすことになる.

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

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

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

練習問題

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

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

物体の例:

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

階層的な定義による作品例:

表情のバリエーション展開
↑姿勢・動作のバリエーション展開 ( 2JM「創造工学基礎演習」のサンプルの応用例)
本日の課題

適度に複雑な形状をもち, かつバリエーション展開が可能な物体を独自に設計し, #macro で定義せよ. そして,その物体の複数(2個以上)のバリエーションを並べてレンダリングせよ.

(もし余裕があれば,アニメーションも作成せよ.)

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