レイトレーシング 2018春

学生の頃、レイトレーシングのプログラムを組んで、 F1マシンらしき造形の極短いアニメーションを作ったことがありました。

当時の手持ちの環境は98互換機EPSON-286VS。 C言語で書いてTrubo C++でコンパイルして走らせてみたものの、 1枚の絵を描画するのに果てしなく時間がかかる代物。

そこで学校で所属してた熱工学研究室の機材に目をつけて、空いてる時に少々拝借 ;-p)

と言ってもホストPC環境はPC9801RXだったかな? 貧乏研究室だったのでその98に神戸製鋼のトランスピューターボードを挿して、 ボード側で数値計算のプログラムを走らせ、98はI/Oだけを使ってました。

(当時、隣の流体力学の研究室では、Sunのワークステーションがゴロゴロとあって何とうらやましかったことか)

トランスピュータボード付属の「パラレルCコンパイラ」で通るようにプログラムを修正して、 一晩こっそり走らせると視点をグリグリ動かしたアングルで大量の画像ファイルの出来上がり。

画像ファイルをMOディスクに落として後輩のはまちゃんに渡すと、 X68000でパラパラアニメ表示して学園祭で展示してくれました。

そんな懐かしい思い出に浸りつつ、どんなプログラムだったかぼちぼちと思い出してPythonで書いてみます。

今回OpenCVを使いますが、画像ファイルと動画を扱う箇所のimg.pyだけで限定的に使うようにしてます。

あと、一旦ガーっと作ってからバラしてまとめ直して、 さらに一度単純な機能の段階に戻して、再度肉付けして機能を増やしたりして作っていきます。

なので後の段階のために、一見不要な処理が伏線のように出てくるかも知れませんが、あしからず。



まずはワイヤーフレームから

レイトレーシングの前にアングルの確認とかしたいので、 プレビュー用にワイヤーフレーム表示から入ってみます。

以前にワイヤーフレームは ワイヤーフレームのプログラム で色々とやってみました。

その反省を踏まえて、ごちゃごちゃし過ぎないように、 一部をPythonに持ってきて、簡単な表示だけから試します。

rt_v1.tgz

$ tar xzf rt_v1.tgz
$ cd rt
$ ls -l
-rw-r--r--  1 kondoh  staff  1936  3 16 20:38 ax.py
-rwxr-xr-x  1 kondoh  staff  1269  3 16 21:06 cg.py
-rw-r--r--  1 kondoh  staff   765  3 16 20:38 cylx.py
-rw-r--r--  1 kondoh  staff   710  3 16 20:38 fcx.py
-rwxr-xr-x  1 kondoh  staff  2889  3 16 20:38 img.py
-rw-r--r--  1 kondoh  staff   891  3 16 20:38 line.py
-rw-r--r--  1 kondoh  staff   685  3 16 20:38 lstx.py
-rw-r--r--  1 kondoh  staff  1777  3 16 20:38 mt.py
-rw-r--r--  1 kondoh  staff  3351  3 16 20:38 ut.py
-rw-r--r--  1 kondoh  staff  1448  3 16 20:38 v.py
-rw-r--r--  1 kondoh  staff  1500  3 16 20:38 vecs.py
-rwxr-xr-x  1 kondoh  staff  2597  3 16 20:38 way.py
-rw-r--r--  1 kondoh  staff  2385  3 16 20:50 wf.py


$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/wf
  :

$ ls -l out/ | head
total 20000
-rw-r--r--  1 kondoh  staff  583325  3 16 21:32 wf.mp4
-rw-r--r--  1 kondoh  staff   32257  3 16 21:31 wf00000.jpg
-rw-r--r--  1 kondoh  staff   32474  3 16 21:31 wf00001.jpg
-rw-r--r--  1 kondoh  staff   32999  3 16 21:31 wf00002.jpg
-rw-r--r--  1 kondoh  staff   32937  3 16 21:31 wf00003.jpg
-rw-r--r--  1 kondoh  staff   32903  3 16 21:31 wf00004.jpg
-rw-r--r--  1 kondoh  staff   32445  3 16 21:31 wf00005.jpg
-rw-r--r--  1 kondoh  staff   30759  3 16 21:31 wf00006.jpg
-rw-r--r--  1 kondoh  staff   32098  3 16 21:31 wf00007.jpg

一応、平面と球のつもりです。


部品のソースコードのメモ

ut.py		ユーティリティ関数とか
mt.py		数学よりな関数とか
v.py		3次元ベクトルの処理
line.py		直線
vecs.py		3x3行列関連
ax.py		アフィン変換
cylx.py		円筒の変換
fcx.py		フォーカスと平面の変換
lstx.py		ax cylx fcx の変換を連結、合成して扱う変換
img.py		画像ファイル動画ファイル
way.py		視点移動など、時間とともに変化する量や位置を扱う
wf.py		ワイヤーフレーム描画処理の本体
cg.py		データの定義と描画して動画に落とす処理

自己流のクロージャと空クラスの使い方については、 パックマンもどき2017秋 クロージャと空クラス を参照してください。


ベタなレイトレーシング

レイトレーシングの1発目。 視点から物体までをトレースして、視線と物体の交点を求めます。 物体と交差していれば単純にその物体の色でベタ塗りしてみます。

v2.patch

$ cat v2.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/beta1 div=16 fps=2

$ ls -lt out/ | head
total 208
-rw-r--r--  1 kondoh  staff  21681  3 17 19:35 beta1.mp4
-rw-r--r--  1 kondoh  staff   1155  3 17 19:35 beta100019.jpg
-rw-r--r--  1 kondoh  staff   1207  3 17 19:35 beta100017.jpg
-rw-r--r--  1 kondoh  staff   1164  3 17 19:35 beta100018.jpg
-rw-r--r--  1 kondoh  staff   1143  3 17 19:35 beta100015.jpg
-rw-r--r--  1 kondoh  staff   1174  3 17 19:35 beta100016.jpg
-rw-r--r--  1 kondoh  staff   1314  3 17 19:35 beta100013.jpg
-rw-r--r--  1 kondoh  staff   1228  3 17 19:35 beta100014.jpg
-rw-r--r--  1 kondoh  staff   1394  3 17 19:35 beta100011.jpg

まずは、解像度を1/16、フレームレート1/15でお試し。

処理するピクセル数は本来の 1/(16*16*15) = 1/3840

それでは意を決してフルサイズでトライ。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/beta2

  :

wh : 290716/307200(94.6%) : rest 4.82s : 2018/03/18 03:04:20
wh : 294482/307200(95.8%) : rest 3.71s : 2018/03/18 03:04:19
wh : 298262/307200(97.0%) : rest 2.6s : 2018/03/18 03:04:19
wh : 302059/307200(98.3%) : rest 1.49s : 2018/03/18 03:04:19
wh : 305822/307200(99.5%) : rest 0.4s : 2018/03/18 03:04:19
wh : 307200/307200(100.0%) : fin 1m 29.38s
frm : 300/300(100.0%) : fin 7h 27m 13.67s
$ 

$ ls -lt out | head
total 10584
-rw-r--r--  1 kondoh  staff  153570  3 18 03:04 beta2.mp4
-rw-r--r--  1 kondoh  staff   15780  3 18 03:04 beta200299.jpg
-rw-r--r--  1 kondoh  staff   15763  3 18 03:02 beta200298.jpg
-rw-r--r--  1 kondoh  staff   15247  3 18 03:01 beta200297.jpg
-rw-r--r--  1 kondoh  staff   14890  3 18 02:59 beta200296.jpg
-rw-r--r--  1 kondoh  staff   14265  3 18 02:58 beta200295.jpg
-rw-r--r--  1 kondoh  staff   13989  3 18 02:56 beta200294.jpg
-rw-r--r--  1 kondoh  staff   14135  3 18 02:55 beta200293.jpg
-rw-r--r--  1 kondoh  staff   14031  3 18 02:53 beta200292.jpg

7時間半かかってました。

部品のソースコードのメモに追加しておきます。

cross.py	物体と直線の交点を求める処理
rt.py		レイトレーシング描画処理の本体

パッチファイルのメモも追加。

v2.patch メモ


拡散光

レイトレーシングの2発目。 物体の表面で拡散する光の強さを計算して、球体に陰影をつけてみます。 これまでのベタな塗りは周囲光あるいは自発光として扱います。

真上からの照明を1つ用意して、物体表面の法線と照明の入射角で拡散光の強さを決めてます。

v3.patch

v3.patch メモ
$ cat v3.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/diff1 div=16 fps=2

まずは粗い設定で確認。 光が当たる北半球だけ、ほんのり陰影がでました。

では、覚悟を決めてレンダリング開始。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/diff2

  :

wh : 277377/307200(90.2%) : rest 9.67s : 2018/03/18 19:27:02
wh : 280708/307200(91.3%) : rest 8.58s : 2018/03/18 19:27:02
wh : 284141/307200(92.4%) : rest 7.46s : 2018/03/18 19:27:02
wh : 287539/307200(93.5%) : rest 6.36s : 2018/03/18 19:27:02
wh : 290947/307200(94.7%) : rest 5.25s : 2018/03/18 19:27:02
wh : 294340/307200(95.8%) : rest 4.15s : 2018/03/18 19:27:02
wh : 297750/307200(96.9%) : rest 3.04s : 2018/03/18 19:27:01
wh : 301166/307200(98.0%) : rest 1.94s : 2018/03/18 19:27:01
wh : 304536/307200(99.1%) : rest 0.85s : 2018/03/18 19:27:01
wh : 307200/307200(100.0%) : fin 1m 38.8s
frm : 300/300(100.0%) : fin 8h 17m 55.27s

8時間超えました。


反射光

レイトレーシングの3発目。 物体の表面で反射する光を計算してみます。

物体表面の交点を新たな視点として、再帰的にトレースして色を求め、 物体の反射率を係数としてかけて反射光を算出します。

再帰の処理が終わらない場合があるので、 反射率を掛け算していって、大元の呼び出しへの影響が小さくなった時点で、 再帰を終わらせるようにしています。

物体表面を新たな視点としてトレースする時、 起点の交点そのものは次の交点の候補から外す処理を追加しています。

v4.patch

v4.patch メモ
$ cat v4.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/reflect1 div=16 fps=2

球体の根元に四角の色が写り込むようになりました。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/reflectc2

  :

wh : 296056/307200(96.3%) : rest 6.17s : 2018/03/19 10:47:01
wh : 298242/307200(97.0%) : rest 4.95s : 2018/03/19 10:47:01
wh : 300420/307200(97.7%) : rest 3.74s : 2018/03/19 10:47:00
wh : 302607/307200(98.5%) : rest 2.53s : 2018/03/19 10:47:00
wh : 304786/307200(99.2%) : rest 1.33s : 2018/03/19 10:47:00
wh : 306958/307200(99.9%) : rest 0.13s : 2018/03/19 10:47:00
wh : 307200/307200(100.0%) : fin 2m 49.17s
frm : 300/300(100.0%) : fin 13h 43m 27.77s

13時間半超え。 四角の平面にも青い球体が写り込んでます。 再帰は時間がかかりますね。


屈折光

レイトレーシングの4発目。 物体の表面で屈折する光を計算してみます。

反射光の場合とほぼ同様な処理になりますが、 物体表面の交点を新たな視点として、 トレースする向きが物体の内部側へと向かいます。 いわゆるスネルの法則で屈折率を算出するために、 物体の密度の情報を追加してます。

v5.patch

v5.patch メモ
$ cat v5.patch | ( cd rt ; patch -p1 )

$ cd rt
$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/reflact1 div=16 fps=2

なんだか画像が汚い感じになりましたが、 少し球体が透けて屈折して見えてる様子?

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/reflact2

  :

wh : 298508/307200(97.1%) : rest 12.53s : 2018/03/21 06:32:38
wh : 300056/307200(97.6%) : rest 10.27s : 2018/03/21 06:32:37
wh : 301597/307200(98.1%) : rest 8.03s : 2018/03/21 06:32:35
wh : 303131/307200(98.6%) : rest 5.81s : 2018/03/21 06:32:34
wh : 304676/307200(99.1%) : rest 3.59s : 2018/03/21 06:32:33
wh : 306214/307200(99.6%) : rest 1.4s : 2018/03/21 06:32:32
wh : 307200/307200(100.0%) : fin 7m 16.08s
frm : 300/300(100.0%) : fin 38h 33m 5.59s

38時間半強! とにかく長時間かかりました。 球体の中にある平面部分が屈折して上に見えてるのでしょうか?


画像の貼り付け

物体の表面に画像を投影してみます。

v6.patch

v6.patch メモ
$ cat v6.patch | ( cd rt ; patch -p1 )

$ cd rt

$ wget http://kondoh.html.xdomain.jp/copen-090419.jpg

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/map1 div=16 fps=2

とりあえず時間がかからないように、 デフォルトの設定で反射と屈折は0、周囲光と拡散光だけにしてます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/map2

  :

wh : 293339/307200(95.4%) : rest 7.04s : 2018/03/21 19:18:31
wh : 295575/307200(96.2%) : rest 5.9s : 2018/03/21 19:18:31
wh : 297801/307200(96.9%) : rest 4.76s : 2018/03/21 19:18:31
wh : 300021/307200(97.6%) : rest 3.63s : 2018/03/21 19:18:31
wh : 302169/307200(98.3%) : rest 2.54s : 2018/03/21 19:18:31
wh : 303879/307200(98.9%) : rest 1.68s : 2018/03/21 19:18:31
wh : 306090/307200(99.6%) : rest 0.56s : 2018/03/21 19:18:31
wh : 307200/307200(100.0%) : fin 2m 35.63s
frm : 300/300(100.0%) : fin 11h 2m 56.42s

11時間でした。

拡散光のときの

frm : 300/300(100.0%) : fin 8h 17m 55.27s

に比べて、map_col()の処理分の時間が増えてるはずですね。


動画の貼り付け

画像が貼れるなら同じくらいの負荷で動画も貼れるだろう、 という事で試してみます。

ワイヤーフレームのプログラム で作った動画でも貼ってみます。

どうせワイヤーフレームの動画なので、 周囲光(自発光)のみにして、拡散光もなしのベタ塗り設定にしてみます。

v7.patch

v7.patch メモ
$ cat v7.patch | ( cd rt ; patch -p1 )

$ cd rt

$ wget http://kondoh.html.xdomain.jp/wf/img/g43.gif

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/vmap1 div=16 fps=2

うーむ。粗いお試しだと意味不明ですな。

./cg.py eyep=[0,0,0],200,10 sec=10 name=out/vmap2

  :

wh : 301281/307200(98.0%) : rest 3.63s : 2018/03/22 12:15:16
wh : 303089/307200(98.6%) : rest 2.52s : 2018/03/22 12:15:15
wh : 304980/307200(99.2%) : rest 1.36s : 2018/03/22 12:15:15
wh : 306814/307200(99.8%) : rest 0.23s : 2018/03/22 12:15:15
wh : 307200/307200(100.0%) : fin 3m 8.27s
frm : 300/300(100.0%) : fin 13h 44m 46.54s

13時間45分。 なんか思ってたんと違うー。 自発光のみにしたものの、そこそこ時間かかってました。

+	maps = [
+		{ 'fn': 'g43.gif', 'fn_r': 'g43.gif', 't2m': [ ax.zoom_all(8) ] },
+		{ 'fn': 'g43.gif', 't2m': [ ax.zoom_all(8), ax.zoom_z(-1) ] },
+	]

ここで8倍にしたのが、ちょっと拡大しすぎだったかも。

もう少しまともっぽい動画でためしてみます。

v8.patch

v8.patch メモ

球体の方は貼り付けなしで、青く薄く自発光、拡散光なし、反射光なし。 屈折だけは密度2でがっちりと。 透明のビー玉の感じを狙ってみました。

$ cat v8.patch | ( cd rt ; patch -p1 )

$ cd rt

$ wget http://kondoh2.html.xdomain.jp/rt/IMG_3999_3.mov
$ wget http://kondoh2.html.xdomain.jp/rt/IMG_3999_4.mov

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/vmap3

  :

wh : 299613/307200(97.5%) : rest 4.53s : 2018/03/23 06:02:07
wh : 301843/307200(98.2%) : rest 3.19s : 2018/03/23 06:02:06
wh : 304062/307200(98.9%) : rest 1.86s : 2018/03/23 06:02:06
wh : 306291/307200(99.7%) : rest 0.54s : 2018/03/23 06:02:06
wh : 307200/307200(100.0%) : fin 3m 2.46s
frm : 300/300(100.0%) : fin 15h 48m 45.39s

16時間弱。 屈折はちゃんとかかってるようですね。

屈折光 のときに38時間半もかかってたのは、 屈折と反射のコラボが原因と推察します。

視線からのトレースで球体表面で屈折して内部に向かった後、 球体内部で反射しまくって、減衰するまで処理してるからなのかと。

反射を0にしたので、 動画を貼ってもそこまでは時間はかかりませんでした。

ということで、物体の法線ベクトルの裏側の面はデフォルトでは反射しない事にします。 'reflect_backside'を明示的にあえてTrueに設定した時だけ、裏面も反射を有効にします。

至近距離から強めの点光源を当てて、拡散光の変化が出やすくしてみました。

あと拡散光の処理のバグ修正も少々。

v9.patch

v9.patch メモ
$ cat v9.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/vmap4

  :

wh : 301054/307200(97.9%) : rest 6.82s : 2018/03/24 13:02:22
wh : 302503/307200(98.4%) : rest 5.20s : 2018/03/24 13:02:22
wh : 303998/307200(98.9%) : rest 3.54s : 2018/03/24 13:02:21
wh : 305486/307200(99.4%) : rest 1.89s : 2018/03/24 13:02:20
wh : 306992/307200(99.9%) : rest 0.22s : 2018/03/24 13:02:20
wh : 307200/307200(100.0%) : fin 5m 38.39s
frm : 300/300(100.0%) : fin 24h 43m 13.72s

24時間。まぁ37時間よりは短縮です。

時間はかかっても反射と半透明のコラボは美しいですね。


立方体の処理を追加してみる

平面と球だけというのも何なので、立方体を追加してみます。

v10.patch

v10.patch メモ

まずはワイヤーフレームで確認してみます。

$ cat v10.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/wf_cube wf

続いて粗い設定で。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/cube1 div=16 fps=2

なんとなく立方体が見えてる風味。良さそうなのでレンダリング開始。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/cube2

  :

wh : 306305/307200(99.7%) : rest 2.70s : 2018/03/27 09:28:18
wh : 306711/307200(99.8%) : rest 1.47s : 2018/03/27 09:28:18
wh : 306993/307200(99.9%) : rest 0.62s : 2018/03/27 09:28:18
wh : 307200/307200(100.0%) : fin 15m 27.14s
frm : 300/300(100.0%) : fin 68h 23m 0.11s

68時間半とは3日弱!!!

球をサイコロに置き換えると、サイコロは6つの四角に展開されるので、 処理時間がずいぶんと増えました。


円柱、角柱、円錐、角錐を追加

立方体に続いて色々追加してみます。

cg.py の data, lights の定義は、dat.py に切り出してみました。

v11.patch

$ cat v11.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/objs_wf wf

  :

frm : 285/300(95.0%) : rest 4.58s : 2018/03/27 09:48:37
frm : 289/300(96.3%) : rest 3.36s : 2018/03/27 09:48:37
frm : 293/300(97.6%) : rest 2.14s : 2018/03/27 09:48:38
frm : 297/300(99.0%) : rest 0.91s : 2018/03/27 09:48:38
frm : 300/300(100.0%) : fin 1m 31.98s

10秒のワイヤーフレーム動画作成に1分半。 これまでワイヤーフレームはリアルタイムで表示できていたのに、 嫌な予感。

1/16の解像度で処理ピクセル数を1/256にして、 5秒の時刻の1コマだけ描画してみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/objs1 div=16 n=1 init_sec=5

  :

wh : 928/1200(77.3%) : rest 4.49s : 2018/03/27 09:50:45
wh : 1012/1200(84.3%) : rest 3.03s : 2018/03/27 09:50:45
wh : 1110/1200(92.5%) : rest 1.40s : 2018/03/27 09:50:44
wh : 1200/1200(100.0%) : fin 17.99s

18秒

解像度を元に戻して1コマ描画したとすると

18*(16*16)/(60*60) = 1.28 (h)

お試しするには長すぎなので、 解像度を1/4に落として1コマ描画したとすると

18*(16*16)/(4*4)/60 = 4.8 (m)

試してみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/objs2 div=4 n=1 init_sec=5

  :

wh : 18825/19200(98.0%) : rest 5.45s : 2018/03/27 10:00:54
wh : 18984/19200(98.8%) : rest 3.12s : 2018/03/27 10:00:53
wh : 19146/19200(99.7%) : rest 0.77s : 2018/03/27 10:00:52
wh : 19200/19200(100.0%) : fin 4m 36.09s

この結果から試算しなおして

1コマ
( 4*60+36 )*(4*4)/(60*60) = 1.22666 (h)

30fpsで10秒分だと300コマ
( 4*60+36 )*(4*4)/(60*60) * 300 = 367.999 (h)
( 4*60+36 )*(4*4)/(60*60) * 300 / 24 = 15.333 (d)

15日以上かかるとの予想!

とりあえず、フレームレートを fps=1 にしての 10 sec

( 4*60+36 )*(4*4)/(60*60) * 10 = 12.2666 (h)

これで試してみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/objs3 fps=1

  :

wh : 306885/307200(99.8%) : rest 4.73s : 2018/03/27 20:33:46
wh : 306976/307200(99.9%) : rest 3.36s : 2018/03/27 20:33:46
wh : 307067/307200(99.9%) : rest 1.99s : 2018/03/27 20:33:45
wh : 307135/307200(99.9%) : rest 0.97s : 2018/03/27 20:33:45
wh : 307200/307200(100.0%) : fin 1h 16m 52.02s
frm : 10/10(100.0%) : fin 10h 26m 54.04s

予想よりちょっとましで、10時間半。

角錐のあたり、何かバグってますね。


角錐のバグ修正

角錐のたぐいはワイヤーフレームでは、 三角に展開されてそれらしく表示されてましたが、 レイトレーシングしてみると表示が乱れてました。

まず、dat.py を変更してコマンドライン引数の指定で、 角錐のデータに切り替えれるようにしておいて、 色々と追いかけてみました。

ローカル座標系とグローバル座標系の関係は dataの辞書のキー'l2g'に設定されてます。 ワイヤーフレームでは'l2g'をローカルからグローバルへの方向に変換して使用します。 レイトレーシングでは視線の直線を'l2g'を使って、グローバルからローカルへと逆変換して使用します。

ここで、逆変換の方向の処理にバグがありました。

diff -urN rt_v11/vecs.py rt_v12/vecs.py
--- rt_v11/vecs.py	2018-03-16 20:38:43.000000000 +0900
+++ rt_v12/vecs.py	2018-03-28 22:25:34.000000000 +0900
@@ -12,10 +12,15 @@
 	e.typ = 'vecs'
 	e.v3 = v3
 	l2g = lambda v_: v.lst_add( map( lambda (a, e): v.op1('*', a, e), zip(v3, v_) ) )
-	g2l = lambda v_: map( lambda a: v.dot_product_len(v_, a), v3 )
+	g2l = lambda v: e.rev().tr('l2g', v)
 	e.tr = lambda d, v: { 'l2g': l2g, 'g2l': g2l }.get(d, l2g)(v)
 
-	e.rev = lambda : new( mt.v3_rev(v3) )
+	e.rev_vs = None
+	def rev():
+		if not e.rev_vs:
+			e.rev_vs = new( mt.v3_rev(v3) )
+		return e.rev_vs
+	e.rev = rev
 
 	e.zoom = lambda zm3: new( map( lambda (v_, zm): v.op1('*', v_, zm), zip(v3, zm3) ) )
 	e.zoom_all = lambda zm: e.zoom( [zm,zm,zm] )

修正前の処理では、座標系のベクトルが同じ大きさで直行してる前提でなければ、正しく動作しなかったようです。 とりあえず、逆行列を使う方向で修正してみると、それらしい表示になりました。

v12.patch

v12.patch メモ
$ cat v12.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid_wf wf

  :

frm : 282/300(94.0%) : rest 1.11s : 2018/03/28 22:56:27
frm : 299/300(99.6%) : rest 0.06s : 2018/03/28 22:56:27
frm : 300/300(100.0%) : fin 18.63s

まずは1コマだけで。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid1 init_sec=5 n=1

  :

wh : 304044/307200(98.9%) : rest 6.96s : 2018/03/28 23:09:43
wh : 305019/307200(99.2%) : rest 4.80s : 2018/03/28 23:09:42
wh : 306130/307200(99.6%) : rest 2.35s : 2018/03/28 23:09:40
wh : 307200/307200(100.0%) : fin 11m 13.93s

1コマ11分14秒ということは

(11*60+14)*300/60/60/24 = 2.34 (d)

単純計算で丸2日以上はかかりますね。 高速化を検討すべきでしょうか。 解像度半分でピクセル数1/4。半日(12時間)強で手をうってみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid2 div=2

  :

wh : 75347/76800(98.1%) : rest 2.89s : 2018/03/29 12:19:29
wh : 75933/76800(98.8%) : rest 1.72s : 2018/03/29 12:19:29
wh : 76524/76800(99.6%) : rest 0.54s : 2018/03/29 12:19:29
wh : 76800/76800(100.0%) : fin 2m 32.64s
frm : 300/300(100.0%) : fin 13h 2m 52.90s


ちょっとだけ高速化

できるだけnumpyを使うようにしてみます。 [x,y,z]な要素3のリストの扱いがほとんどなので、さほどな気もしますが...

v13.patch

$ cat v13.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid3 init_sec=5 n=1

  :

wh : 305726/307200(99.5%) : rest 2.81s : 2018/04/01 12:10:05
wh : 307177/307200(99.9%) : rest 0.04s : 2018/04/01 12:10:03
wh : 307200/307200(100.0%) : fin 9m 44.53s

ぱっと見、同じ画像が生成できてる様子? というか、モアベターになってないかい?

v12のとき

wh : 307200/307200(100.0%) : fin 11m 13.93s

だったので

(9*60+44.53) / (11*60+13.93 ) * 100 = 86.73
100 - 86.73 = 13.27

速度的には13.27パーセントの向上です。


もうちょっと高速化

交点を求める処理で、結局最後に使われないかもしれない計算は、 なるべくセコく先延ばしするようにしてみました。

v14.patch

$ cat v14.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid4 init_sec=5 n=1

  :

wh : 304901/307200(99.2%) : rest 3.77s : 2018/04/01 20:53:33
wh : 306183/307200(99.6%) : rest 1.66s : 2018/04/01 20:53:32
wh : 307200/307200(100.0%) : fin 8m 22.68s

v13のとき

wh : 307200/307200(100.0%) : fin 9m 44.53s

だったので

(8*60+22.68) / (9*60+44.53) * 100 = 85.99
100 - 85.99 = 14.01

14.01パーセントの向上です。


えらいこっちゃ

やってしまったか。

さらなる高速化のため numba とやらを試そうかと思ったのが運のつき。

Macでの情報を検索してみると

llvmのインストールbrew install homebrew/versions/llvm38
export LLVM_CONFIG=/usr/local/Cellar/llvm38/3.8.1/bin/llvm-config-3.8
llvmlite のインストールpip3 install llvmlite
numbaのインストールpip3 install numba

ふむ。

$ brew install homebrew/versions/llvm38
  :
To avoid broken installations, as soon as possible please run:
  brew upgrade
Or, if you're OK with a less reliable fix:
  brew upgrade python

Error: homebrew/versions was deprecated. This tap is now empty as all its formulae were migrated.

!? とりあえず...

$ brew upgrade

  :

!?

$ python
Python 3.6.5 (default, Mar 30 2018, 06:41:49) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Python 3 になっとる。

ここまで Python 2 のままで Python 3 には手を出してなかったのに〜!

まぁ、あきらめます。いい機会と思って Python 3 で動くようにしてみます。

numba は後回し。

v15.patch

v15.patch メモ
$ cat v15.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid5 init_sec=5 n=1

  :

wh : 305693/307200(99.5%) : rest 3.13s : 2018/04/03 10:16:01
wh : 306936/307200(99.9%) : rest 0.54s : 2018/04/03 10:15:59
wh : 307200/307200(100.0%) : fin 10m 38.03s

v14のとき

wh : 307200/307200(100.0%) : fin 8m 22.68s

だったので

(10*60+38.03) / (8*60+22.68) * 100 = 126.92
100 - 126.92 = -26.92

26.92パーセントの低下です。

ちびちび速度アップしてきたのに、 map, filter への安易な対策が足を引っ張ってしまったかも (T_T)


python3対応モレ残ってました

ワイヤーフレームの描画をさせてみるとエラー発生。

python3対応を施したものの、 角錐の平面系の絵が1枚描ける事しか確認してませんでしたね。

できるだけ対処して、時間のかからない設定でざっと確認してみます。

v16.patch

v16.patch メモ
$ cat v16.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball name=out/ball_wf wf
  :
frm : 278/300(92.7%) : rest 1.82s : 2018/04/04 17:17:49
frm : 291/300(97.0%) : rest 0.74s : 2018/04/04 17:17:49
frm : 300/300(100.0%) : fin 24.77s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball name=out/ball div=8 fps=15
  :
wh : 3199/4800(66.6%) : rest 1.50s : 2018/04/04 17:28:21
wh : 4242/4800(88.4%) : rest 0.52s : 2018/04/04 17:28:21
wh : 4800/4800(100.0%) : fin 4.31s
frm : 150/150(100.0%) : fin 9m 32.98s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball name=out/ball_1 n=1 init_sec=5
  :
wh : 298537/307200(97.2%) : rest 6.64s : 2018/04/04 17:40:54
wh : 304452/307200(99.1%) : rest 2.07s : 2018/04/04 17:40:50
wh : 307200/307200(100.0%) : fin 3m 50.63s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=cube name=out/cube_wf wf
  :
frm : 279/300(93.0%) : rest 1.69s : 2018/04/04 17:43:08
frm : 293/300(97.7%) : rest 0.56s : 2018/04/04 17:43:08
frm : 300/300(100.0%) : fin 24.19s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=cube name=out/cube div=8 fps=15
  :
wh : 4270/4800(89.0%) : rest 1.11s : 2018/04/04 18:06:52
wh : 4761/4800(99.2%) : rest 0.08s : 2018/04/04 18:06:52
wh : 4800/4800(100.0%) : fin 10.10s
frm : 150/150(100.0%) : fin 22m 55.25s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=cube name=out/cube_1 n=1 init_sec=5
  :
wh : 305653/307200(99.5%) : rest 3.23s : 2018/04/04 18:18:36
wh : 306345/307200(99.7%) : rest 1.78s : 2018/04/04 18:18:35
wh : 307200/307200(100.0%) : fin 10m 40.80s
frm : 1/1(100.0%) : fin 10m 48.61s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out/objs_wf wf
  :
frm : 295/300(98.3%) : rest 2.35s : 2018/04/04 18:27:33
frm : 297/300(99.0%) : rest 1.41s : 2018/04/04 18:27:33
frm : 300/300(100.0%) : fin 2m 21.61s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out/objs div=8 fps=1
  :
wh : 4590/4800(95.6%) : rest 2.16s : 2018/04/04 18:36:47
wh : 4698/4800(97.9%) : rest 1.04s : 2018/04/04 18:36:47
wh : 4800/4800(100.0%) : fin 49.20s
frm : 10/10(100.0%) : fin 6m 52.90s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out/objs_1 n=1 init_sec=5 div=2
  :
wh : 76449/76800(99.5%) : rest 4.40s : 2018/04/04 19:10:47
wh : 76720/76800(99.9%) : rest 1.00s : 2018/04/04 19:10:45
wh : 76800/76800(100.0%) : fin 16m 1.01s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid_wf wf
  :
frm : 273/300(91.0%) : rest 2.38s : 2018/04/04 19:12:18
frm : 288/300(96.0%) : rest 1.04s : 2018/04/04 19:12:18
frm : 300/300(100.0%) : fin 26.01s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid div=8 fps=15
  :
wh : 3429/4800(71.4%) : rest 2.40s : 2018/04/04 19:34:14
wh : 4154/4800(86.5%) : rest 1.09s : 2018/04/04 19:34:13
wh : 4800/4800(100.0%) : fin 7.86s
frm : 150/150(100.0%) : fin 20m 44.41s

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=poly_n_pyramid name=out/pn_pyramid_1 n=1 init_sec=5
  :
wh : 304953/307200(99.3%) : rest 6.95s : 2018/04/04 20:31:36
wh : 306216/307200(99.7%) : rest 3.03s : 2018/04/04 20:31:34
wh : 307200/307200(100.0%) : fin 15m 45.51s

python3 対応以降、アングルが微妙に変わってます。

way.py

	def liss_get(key):
		(p, r, t) = p_r_t(key)
		p = mt.arr(p)
		ps = p - r
		pe = p + r
		t3 = (t, t*4/3, t*5/3)
		return liss(ps, pe, t3)

ここの割り算が、対応前は整数の割り算扱いでした。 対応前と同じにするには '/' --> '//' ですが、実は対応前が間違い。 tの値はfloatを想定していましたが、intの値ばかり指定して試してました。

なので対応後のアングルの方が正解のはずです。

あと、全体的に気泡のようなツブツブが目立つのは気のせい?


気泡

気泡問題は以前反射光のところで

物体表面を新たな視点としてトレースする時、 起点の交点そのものは次の交点の候補から外す処理を追加しています。

だったはずですが...

ref_rate の値を100万分の1固定から、引数の指定で変更できるようにして試してみます。 とりあえず指定がなかったときのデフォルト値は1000分の1ということで。

v17.patch

v17.patch

diff -urN rt_v16/rt.py rt_v17/rt.py
--- rt_v16/rt.py	2018-04-03 09:46:11.000000000 +0900
+++ rt_v17/rt.py	2018-04-04 21:33:33.000000000 +0900
@@ -36,6 +36,8 @@
 	col = np.sum(cols, axis=0) if cols else def_col
 	return ut.map_lst( lambda v: min(v, 255), col )
 
+ref_rate = ut.arg_f('ref_rate', 0.001)
+
 def get_col(data, lights, l_g, video, nest_rate=1.0, ref_len=None):
 	if nest_rate < 0.01:
 		return []
@@ -46,7 +48,6 @@
 		return []
 
 	if ref_len:
-		ref_rate = 0.000001
 		lst = ut.filter_lst( lambda crs: crs.get('t') > ref_len * ref_rate, lst )
 		if not lst:
 			return []

この「候補から外す処理」の内容は

rt.py

def get_col(data, lights, l_g, video, nest_rate=1.0, ref_len=None):

最後の引数ref_lenで「参考にする長さ」を指定
最初の視点からのトレース時は、デフォルト値でNone(指定なし)

		:
	if ref_len:
		lst = ut.filter_lst( lambda crs: crs.get('t') > ref_len * ref_rate, lst )
		if not lst:
			return []

	もしref_lenの指定があれば、ここでふるいにかけてます
	lstは複数の物体の交点情報のリスト
	crs.get('t')は視点から交点までの距離のようなもの
	それが「参考にする長さ」の100万分の1より小さい、
	つまり視点に十分近い位置ならば、除外します


	crs = min( lst, key=lambda crs: crs.get('t') )

	除外処理済みのリストから、一番視点に近い交点をcrsに
		:

	crst = crs.get('t')

	その交点crsの視点との距離のようなものをcrstに


	def reflect_col():
		:
		col = get_col( data, lights, ref_l, video, nest_rate * reflect, crst )
		return col * reflect if col != [] else []

	col_ = reflect_col()
		:
	反射の再帰処理で、交点crsを新たな視点としてトレースするときは
	「参考にする長さ」ref_len として、前回の視点と交点の距離のような値crstを指定します

	屈折の場合もまた同様にref_lenを指定

試してみます。まずref_rateが1000分の1の場合

$ cat v17.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball name=out_v17/ball_1 n=1 init_sec=5
  :
wh : 307200/307200(100.0%) : fin 3m 55.30s

気泡、北半球は消えてる感じです。

次に、これまで通りref_rateが100万分の1の場合

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball name=out_v17/ball_2 n=1 init_sec=5 ref_rate=0.000001
  :
wh : 307200/307200(100.0%) : fin 3m 45.27s

気泡、目立ちますね。 反射光対応の最初から100万分の1としてたはずなのですが。

極端に、ref_rateを10分の1にしてみた場合

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball name=out_v17/ball_3 n=1 init_sec=5 ref_rate=0.1
  :
wh : 307200/307200(100.0%) : fin 3m 5.07s

まぁそうですね。極端にすると逆に問題ありになりますね。 以降ref_rateはデフォルトの1000分の1という事で。

以前の試算で15日以上かかる予想のデータを、このバージョンで荒削り設定で試してみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v17/objs div=2 fps=4
  :
wh : 76569/76800(99.7%) : rest 2.30s : 2018/04/05 06:53:42
wh : 76698/76800(99.9%) : rest 1.01s : 2018/04/05 06:53:42
wh : 76800/76800(100.0%) : fin 12m 46.20s
frm : 40/40(100.0%) : fin 6h 50m 3.11s

それでも多少、気泡が気になりますね。

解像度1/2でピクセル数1/4
フレームレート4/30
よって本来のピクセル処理量の1/30
それで7時間弱かかったので

( ((6*60+50)*60+3.11) * 30 ) / (60*60*24) = 8.54

約8日半

あら、15日以上の予想より速いですね。

以前の予想のときは
$ ./cg.py eyep=[0,0,0],200,10 sec=10 name=out/objs2 div=4 n=1 init_sec=5
  :
wh : 19200/19200(100.0%) : fin 4m 36.09s

解像度1/4の絵を1枚で4分36.09秒

ピクセル数は本来の1/(4*4*300)なので
(4*60+36.09) * (4*4*300) / (60*60*24) = 15.33

まぁ10秒間のうち時刻5秒のときの絵1枚の時間だったので、
たまたま処理が重くなるアングルで、試算していたのかもしれません

それでも一週間以上はかかる予想です。(T_T)


できるだけ高速化

Python3 対応以降のソースを全体的に見直して、高速化してみました。

v18.patch

まずバージョン17から

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v17/objs_32 div=32
  :
wh : 300/300(100.0%) : fin 3.09s
frm : 300/300(100.0%) : fin 12m 51.35s

out_v17/objs_32.mp4

そしてバージョン18

$ cd ..

$ cat v18.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v18/objs_32 div=32
  :
frm : 299/300(99.7%) : total 7m 35.62s : rest 1.51s : 2018/04/08 00:26:20
wh : 0/300(0.0%) : not start yet
wh : 184/300(61.3%) : total 1.63s : rest 0.63s : 2018/04/08 00:26:20
wh : 300/300(100.0%) : fin 1.77s
frm : 300/300(100.0%) : fin 7m 35.91s

out_v18/objs_32.mp4

(7*60+35.91) / (12*60+51.35) * 100 = 59.10
100 - 59.10 = 40.90

40.90パーセントの向上です。v^_^)

ということは、解像度を4倍あげてフレームレート2fpsにおとすと、

4*4*(2/30)=16/15=1.066

だいたい同じくらいの時間で仕上がるはず。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v18/objs_8_2_ div=8 fps=2
  :
wh : 4732/4800(98.6%) : total 32.64s : rest 0.46s : 2018/04/08 00:53:13
wh : 4800/4800(100.0%) : fin 32.60s
frm : 20/20(100.0%) : fin 8m 14.18s

out_v18/objs_8_2_.mp4

ですね。

さらに解像度を2倍でフレームレート30fpsの予想時間は

((8*60+14.18)*2*2*(30/2)) / (60*60) = 8.23

8.23時間。試してみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v18/objs_4 div=4
  :
wh : 19196/19200(100.0%) : total 1m 55.49s : rest 0.02s : 2018/04/08 08:58:21
wh : 19200/19200(100.0%) : fin 1m 55.53s
frm : 300/300(100.0%) : fin 7h 56m 30.00s


気泡 その2

やっぱり気泡が気になります。 多少速度を犠牲にしても良しとして、根本的に対策しておきます。

再帰して交点を求めるときに、 前回の交点となった物体の情報を覚えておいて、 交点算出時に除外する方針で何とかしてみます。

あと、多角形(poly_n)の交点を求める処理を入れてましたが、、、 多角形は複数の三角形に展開して使うので、不要でした orz

v19.patch

まず前のバージョンの結果を確認

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v18/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76009/76800(99.0%) : total 7m 50.93s : rest 4.85s : 2018/04/12 20:50:10
wh : 76469/76800(99.6%) : total 7m 49.10s : rest 2.02s : 2018/04/12 20:50:08
wh : 76800/76800(100.0%) : fin 7m 47.81s

では対策したバージョンで

$ cd ..

$ cat v19.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v19/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 75908/76800(98.8%) : total 7m 42.15s : rest 5.36s : 2018/04/12 21:00:15
wh : 76373/76800(99.4%) : total 7m 40.34s : rest 2.55s : 2018/04/12 21:00:13
wh : 76800/76800(100.0%) : fin 7m 38.74s

円柱の曲面や、円柱と平面の交差するあたりで改善が顕著です。 処理時間はほぼ同等。逆に短縮されたくらいですね v^_^)


別プロセスに切り出し

視線の直線と物体との交点を求める処理を、別プロセスに切り出して処理させてみます。

さらに次のステップでは、切り出した別プロセスのプログラムをC言語で実装しなおしてみて、高速化を試してみます。 (numbaのお試しは、さらに先延ばし...)

とまぁ、この段階ではわざわざ別のserverプロセスに分けても、serverで同じcross.pyの処理を呼び出してるので、 遅くなるだけです。

serverで返した交点の答をあんちょこファイルに落としているのがミソで、次回同様に use_srv 指定で起動した場合にserverの動作は

あんちょこファイルを作ったときと同じ設定にしなければ意味ないのですが、 これは、1つの処理時間の基準になります。

同様のserverをがんばってC言語で実装したとしても、 このあんちょこカンニング版の処理時間が上限で、 これ以上短くなる事はまず無いでしょう。

なので、あんちょこを使っても遅ければ、プロセス間通信のオーバーヘッドが大きいので、 素直に従来通り処理した方が、まだ「まし」という事になります。

v20.patch

まずは、従来の処理の場合から。 use_srv 指定の有無による分岐が1つ追加で入ってるので、影響が大きくないか一応確認しておきます。

$ cat v20.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v20/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76242/76800(99.3%) : total 7m 50.43s : rest 3.41s : 2018/04/12 21:42:35
wh : 76664/76800(99.8%) : total 7m 48.84s : rest 0.83s : 2018/04/12 21:42:34
wh : 76800/76800(100.0%) : fin 7m 48.33s

ふむ。従来通りの処理時間っすね。

では、別serverプロセスを起動する方式で、あんちょこファイルも作ってみます。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v20/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76049/76800(99.0%) : total 9m 12.58s : rest 5.40s : 2018/04/12 23:11:19
wh : 76446/76800(99.5%) : total 9m 10.71s : rest 2.53s : 2018/04/12 23:11:17
wh : 76800/76800(100.0%) : fin 9m 9.07s

$ ls -l xdat
-rw-r--r--  1 kondoh  staff  2636160  4 12 23:58 xdat

同様の画像ができてます。 そして、あんちょこファイル 2.5Mバイト程度。

xdat

別プロセスに分けることで
100 * ( (9*60+9.07) / (7*60+48.33) - 1 ) = 17.23 パーセント低下

続いて、あんちょこファイルでカンニング方式

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v20/objs_1_2_s2 n=1 init_sec=5 div=2 use_srv
  :
wh : 67446/76800(87.8%) : total 2m 3.53s : rest 15.04s : 2018/04/13 00:02:20
wh : 71625/76800(93.3%) : total 1m 57.40s : rest 7.91s : 2018/04/13 00:02:14
wh : 76800/76800(100.0%) : fin 1m 50.29s

無事、同様の画像ができてます。

ほとんどプロセス間通信のオーバーヘッドだけの状態で
100 * ( 1 - ( 1*60+50.29) / (7*60+48.33) ) = 76.45 パーセント向上

これならserverの処理をC言語でトライしてみる価値がありそうですね。 76.45パーセントのどこまで近づけれるものなのか。

ちなみに、生成した画像ファイルはみな同様に見えますが、、、

$ cd out_v20

$ ls -l *.jpg
total 192
-rw-r--r--  1 kondoh  staff  32282  4 12 21:43 objs_1_2.jpg
-rw-r--r--  1 kondoh  staff  32282  4 13 00:00 objs_1_2_s.jpg
-rw-r--r--  1 kondoh  staff  32282  4 13 00:02 objs_1_2_s2.jpg

$ cmp objs_1_2.jpg objs_1_2_s.jpg 
$ 

$ cmp objs_1_2_s.jpg objs_1_2_s2.jpg 
$ 

$ cd ..

無事、一致しておりました。


別プロセスのところをC言語で実装

ということで、がんばってC言語で書いてみました。

起動オプション usr_srv を指定した上で、さらに srv_c を指定すると C言語で実装した方のサーバプログラムを起動します。

v21.patch

$ cat v21.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make
gcc -Wall   -c -o sock.o sock.c
gcc -Wall   -c -o cross.o cross.c
gcc -Wall   -c -o ax.o ax.c
gcc -Wall   -c -o vecs.o vecs.c
gcc -Wall   -c -o line.o line.c
gcc -Wall   -c -o v.o v.c
gcc -Wall   -c -o mt.o mt.c
gcc -o sock sock.o cross.o ax.o vecs.o line.o v.o mt.o

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v21/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 68851/76800(89.6%) : total 1m 0.56s : rest 6.26s : 2018/04/14 11:47:18
wh : 74103/76800(96.5%) : total 57.30s : rest 2.01s : 2018/04/14 11:47:15
wh : 76800/76800(100.0%) : fin 55.70s

$ ls -l out_v21/objs_1_2_sc.jpg 
-rw-r--r--  1 kondoh  staff  32344  4 14 11:48 out_v21/objs_1_2_sc.jpg

$ ls -l out_v20/objs_1_2_s2.jpg 
-rw-r--r--  1 kondoh  staff  32282  4 13 00:02 out_v20/objs_1_2_s2.jpg

画像ファイルのサイズが微妙に違ってますが、まぁ..同様に見えます。

気になる処理時間は、55.70秒 !!!

v20あんちょこカンニング版の時間にどれだけ迫れるものかと思ってたら、 さらに半分近くにまで短縮 !?

他の設定でも試してみます。フルの解像度で1枚だけの場合

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v21/objs_1_sc n=1 init_sec=5 use_srv srv_c port=23482
  :
wh : 299637/307200(97.5%) : total 3m 41.78s : rest 5.46s : 2018/04/14 09:09:08
wh : 306265/307200(99.7%) : total 3m 37.98s : rest 0.66s : 2018/04/14 09:09:05
wh : 307200/307200(100.0%) : fin 3m 37.46s

3分半。ということは

10秒分の300枚の単純な予想では

( 3*60+37.46 ) * 300 / 60 / 60 = 18.12 時間

色んなアングルがあるので、もうちょっと違う設定でじっくりと

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v21/objs_2_15_sc div=2 fps=15 use_srv srv_c port=23484
  :
wh : 76664/76800(99.8%) : total 1m 0.14s : rest 0.10s : 2018/04/14 11:21:29
wh : 76800/76800(100.0%) : fin 1m 0.12s
frm : 150/150(100.0%) : fin 2h 7m 12.09s

解像度1/2でフレームレート15/30倍なので、フルで試したとすると
( (((2*60)+7)*60+12.09) * 2*2*2 ) / 60 / 60 = 16.96

予想では17時間。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v21/objs_full_sc use_srv srv_c
  :
wh : 306431/307200(99.7%) : total 3m 58.73s : rest 0.59s : 2018/04/15 05:15:55
wh : 307200/307200(100.0%) : fin 3m 58.63s
frm : 300/300(100.0%) : fin 16h 50m 54.21s

確かに17時間で終了です。

2週間以上かかる予想だった頃や、 立方体と平面だけで3日もかかってた頃に比べると、 速くなりました〜 v^_^)


データ追加

そこそこ処理が速くなったところで、 変わり映えしない絵の脱却を図るべく、 データを追加してみます。

v22.patch

巨大な球の赤道に沿って円でしきった物体を用意。 その北半球部分に、ちいさな球と立方体を配置。 巨大な球のドーム屋根の内側と下側の円に動画を投影しつつ、 下側の円の表面では天井の絵をある程度反射。

$ cat v22.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball_world name=out_v22/bw2_wf wf

解像度半分、フレームレートも半分で、フルの1/8で。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball_world name=out_v22/bw2_2_15 use_srv srv_c div=2 fps=15
  :
wh : 76221/76800(99.2%) : total 1m 24.69s : rest 0.63s : 2018/04/15 21:44:58
wh : 76800/76800(100.0%) : fin 1m 24.62s
frm : 150/150(100.0%) : fin 3h 11m 18.29s

では、8倍で24時間。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball_world name=out_v22/bw2_full use_srv srv_c
  :
wh : 306434/307200(99.8%) : total 5m 42.07s : rest 0.85s : 2018/04/16 23:41:50
wh : 307200/307200(100.0%) : fin 5m 41.87s
frm : 300/300(100.0%) : fin 25h 48m 38.76s

26時間弱でした。


画像の繰り返し展開

先の追加データがいい感じなので、 もうちょっと違うバージョンを試そうと、 画像の円柱マッピング方式のデータを追加してみました。

普通の長方形の画像を円柱に巻きつけたとき、、、 例えば縦横比4:3の画像を隙間なく巻きつけるならば

h = 2*pi*r *3/4 = 4.71 * r

高さは円柱半径の5倍弱。ちょっと細すぎで使い勝手が悪いかも。

高さと半径の割合をいい感じにとると、円周方向の長さが足りず。

足りない分は、繰り返しパターンで埋める事にすればいいか。 画像や動画を繰り返し展開する機能を追加する事にします。

v23.patch

v23.patch メモ

ところで、レイトレーシングの画像を検索してみると、 判で押したように「チェスボードの上に透明の球」な画像が出てきます。

ここはひとつ、画像の繰り返し展開を試しつつも、 伝統に従ったデータを追加して試してみます。

$ cat v23.patch | ( cd rt ; patch -p1 )

$ cd rt

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=copen_rep name=out_v23/copen_rep use_srv srv_c
  :
wh : 306415/307200(99.7%) : total 2m 59.52s : rest 0.45s : 2018/04/17 11:53:31
wh : 307200/307200(100.0%) : fin 2m 59.47s
frm : 300/300(100.0%) : fin 12h 8m 4.87s

せっかくout_v23/copen_rep.mp4 が出来たのですが...

-rw-r--r--  1 kondoh  staff  3741956  4 17 11:53 copen_rep.mp4

3Mバイトを超えてしまい、 サイトのファイル1つ3Mバイトまでの制限にひっかかりました。

$ ./img.py out_v23/tmp/copen_rep out_v23/copen_rep cmd=v2i

$ ls out_v23/tmp/ | head
copen_rep00000.jpg
copen_rep00001.jpg
copen_rep00002.jpg
copen_rep00003.jpg
copen_rep00004.jpg
copen_rep00005.jpg
copen_rep00006.jpg
copen_rep00007.jpg
copen_rep00008.jpg
copen_rep00009.jpg

$ ls out_v23/tmp/ | tail
copen_rep00290.jpg
copen_rep00291.jpg
copen_rep00292.jpg
copen_rep00293.jpg
copen_rep00294.jpg
copen_rep00295.jpg
copen_rep00296.jpg
copen_rep00297.jpg
copen_rep00298.jpg
copen_rep00299.jpg

$ mkdir out_v23/tmp/hide
$ mv out_v23/tmp/copen_rep002* out_v23/tmp/hide/

ここで img.py の imgs_to_video() で python3 対応もれ
	  :
	lst = ut.cmd_exec(cmd).decode().strip().split('\n')
	#lst = ut.cmd_exec(cmd).strip().split('\n')
	  :
次回のpatchに含めておきます。

$ ./img.py out_v23/tmp/copen_rep out_v23/copen_rep2

$ ls -l out_v23/copen_rep2.mp4 
-rw-r--r--  1 kondoh  staff  1474209  4 17 21:15 out_v23/copen_rep2.mp4

ちょっと削りすぎた

$ mv out_v23/tmp/hide/copen_rep002[01234]* out_v23/tmp/
$ ./img.py out_v23/tmp/copen_rep out_v23/copen_rep2
$ ls -l out_v23/copen_rep2.mp4 
-rw-r--r--  1 kondoh  staff  2444200  4 17 21:41 out_v23/copen_rep2.mp4

まだいける

$ mv out_v23/tmp/hide/copen_rep002[567]* out_v23/tmp/
$ ./img.py out_v23/tmp/copen_rep out_v23/copen_rep2
$ ls -l out_v23/copen_rep2.mp4 
-rw-r--r--  1 kondoh  staff  3019992  4 17 21:43 out_v23/copen_rep2.mp4

超えてしまった

$ mv out_v23/tmp/copen_rep0027* out_v23/tmp/hide/
$ ./img.py out_v23/tmp/copen_rep out_v23/copen_rep2
$ ls -l out_v23/copen_rep2.mp4 
-rw-r--r--  1 kondoh  staff  2827547  4 17 21:44 out_v23/copen_rep2.mp4

OK

なんだか、定番の「チェスボードの上に透明の球」とは違って、不気味な凄み。

そして、円柱マッピングなバージョンのお試し。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=ball_world2 name=out_v23/bw2 use_srv srv_c
  :
wh : 306229/307200(99.7%) : total 4m 58.12s : rest 0.94s : 2018/04/18 13:04:59
wh : 307200/307200(100.0%) : fin 4m 58.00s
frm : 300/300(100.0%) : fin 24h 58m 25.62s

25時間でした。 えらいもんで、外側のドームへ円柱マッピングすると、中にある球も円柱な感じに見えますね。


もう少しC言語で

視線と物体の交点を求める処理を別サーバプロセスに切り出し、C言語で実装してみました。

もうちょっと先の分の処理まで。交点の法線を求めるところまで、サーバ側のC言語で面倒みさせてみます。

そして、円柱マッピングのところにあったバグも、ひっそりと改修。

v24.patch

まず従来方式

$ cat v24.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make clean
$ make
gcc -Wall   -c -o sock.o sock.c
gcc -Wall   -c -o cross.o cross.c
gcc -Wall   -c -o lstx.o lstx.c
gcc -Wall   -c -o ax.o ax.c
gcc -Wall   -c -o cylx.o cylx.c
gcc -Wall   -c -o fcx.o fcx.c
gcc -Wall   -c -o vecs.o vecs.c
gcc -Wall   -c -o line.o line.c
gcc -Wall   -c -o v.o v.c
gcc -Wall   -c -o mt.o mt.c
gcc -o sock sock.o cross.o lstx.o ax.o cylx.o fcx.o vecs.o line.o v.o mt.o -lm

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v24/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76609/76800(99.8%) : total 7m 45.16s : rest 1.15s : 2018/04/18 21:27:07
wh : 76800/76800(100.0%) : fin 7m 44.42s

サーバプロセス分離版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v24/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76579/76800(99.7%) : total 9m 39.30s : rest 1.66s : 2018/04/18 21:54:10
wh : 76800/76800(100.0%) : fin 9m 38.24s

別プロセスのところをC言語で実装版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v24/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 72244/76800(94.1%) : total 56.58s : rest 3.35s : 2018/04/18 21:57:43
wh : 76800/76800(100.0%) : fin 54.07s

別プロセスに切り出しのときの結果は

従来方式
wh : 76800/76800(100.0%) : fin 7m 48.33s

サーバプロセスに分離版
wh : 76800/76800(100.0%) : fin 9m 9.07s

サーバプロセスであんちょこファイルカンニング版
wh : 76800/76800(100.0%) : fin 1m 50.29s

別プロセスのところをC言語で実装では

wh : 76800/76800(100.0%) : fin 55.70s

まー、ほとんど以前の結果と変わらない感じですね。

法線を求める処理は、 交点を求めるときのように物体の数だけ処理するわけじゃなく、 交点の1つの物体についてだけなので、そんなところですかね。

$ ls -l out_v24/*.jpg
-rw-r--r--  1 kondoh  staff  29772  4 18 21:27 out_v24/objs_1_2.jpg
-rw-r--r--  1 kondoh  staff  29772  4 18 21:54 out_v24/objs_1_2_s.jpg
-rw-r--r--  1 kondoh  staff  29775  4 18 21:57 out_v24/objs_1_2_sc.jpg

円柱マッピングのデータで、ぐいっと接近した視点移動。 球や立方体の中に視点が入ったらどんな感じになるか試してみます。

$ ./cg.py eyep=[0,0,0],30,5 sec=10 data_name=ball_world2 name=out_v24/bw2_2_near div=2 use_srv srv_c
  :
wh : 76800/76800(100.0%) : fin 1m 55.91s
frm : 300/300(100.0%) : fin 11h 28m 25.60s

$ls -l out_v24/bw2_2_near.mp4 
-rw-r--r--  1 kondoh  staff  1849766  4 19 20:48 out_v24/bw2_2_near.mp4

11時間半。解像度半分で本来の1/4のピクセル数。フルなら44時間の予想。 接近すると、なかなか重いですね。

ちょっとぶん回し過ぎでよくわからんですね。 フレームレートを落としてみます。

$ ./img.py out_v24/bw2_2_near out_v24/bw2_2_near_fps10 fps=10 zm=2

むー。いまいち中に入ってる感がよく分かりませんね。


さらにもう少しC言語で

拡散光の強さを算出する処理のところまで、別プロセスにやらせてみます。

データの拡散の係数や、複数の光源の情報を渡さねばならなりません。 可変長なデータのやりとりが出てきて、結構な変更量となりました。

v25.patch

今回は遠目の視点で試してみます。

$ cat v25.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make clean
$ make
gcc -Wall   -c -o sock.o sock.c
gcc -Wall   -c -o rt.o rt.c
gcc -Wall   -c -o cross.o cross.c
gcc -Wall   -c -o lstx.o lstx.c
gcc -Wall   -c -o ax.o ax.c
gcc -Wall   -c -o cylx.o cylx.c
gcc -Wall   -c -o fcx.o fcx.c
gcc -Wall   -c -o vecs.o vecs.c
gcc -Wall   -c -o line.o line.c
gcc -Wall   -c -o v.o v.c
gcc -Wall   -c -o mt.o mt.c
gcc -o sock sock.o rt.o cross.o lstx.o ax.o cylx.o fcx.o vecs.o line.o v.o mt.o -lm

$ ./cg.py eyep=[0,0,0],400,10 sec=10 data_name=ball_world2 name=out_v25/bw2_long_wf wf

$ ./cg.py eyep=[0,0,0],400,10 sec=10 data_name=ball_world2 name=out_v25/bw2_long div=2 use_srv srv_c
  :
wh : 75775/76800(98.7%) : total 50.69s : rest 0.67s : 2018/04/20 06:02:32
wh : 76800/76800(100.0%) : fin 50.66s
frm : 300/300(100.0%) : fin 3h 53m 0.30s

そして、いつものデータで速度比較。

まず従来方式

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v25/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76764/76800(100.0%) : total 7m 51.10s : rest 0.22s : 2018/04/20 09:17:53
wh : 76800/76800(100.0%) : fin 7m 50.96s

  以前の
  v24  wh : 76800/76800(100.0%) : fin 7m 44.42s
  v23  wh : 76800/76800(100.0%) : fin 7m 48.33s

サーバプロセス分離版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v25/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76693/76800(99.9%) : total 9m 35.41s : rest 0.80s : 2018/04/20 09:29:17
wh : 76800/76800(100.0%) : fin 9m 34.91s

  以前の
  v24  wh : 76800/76800(100.0%) : fin 9m 38.24s
  v23  wh : 76800/76800(100.0%) : fin 9m 9.07s

別プロセスのところをC言語で実装版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v25/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 72244/76800(94.1%) : total 56.39s : rest 3.34s : 2018/04/20 09:32:56
wh : 76800/76800(100.0%) : fin 53.95s

  以前の
  v24  wh : 76800/76800(100.0%) : fin 54.07s
  v23  wh : 76800/76800(100.0%) : fin 1m 50.29s

まぁ、さほど変わらずですね。

平面マッピング版の方でも、視点接近コースをゆっくり目で走らせてみます。

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v25/bw_near_2 div=2 use_srv srv_c
  :
wh : 76463/76800(99.6%) : total 2m 2.65s : rest 0.53s : 2018/04/20 19:47:13
wh : 76800/76800(100.0%) : fin 2m 2.54s
frm : 300/300(100.0%) : fin 9h 53m 50.62s

約10時間でした。

黒っぽい球にメーター表示。 むかーし小学校の頃見た、 松本零士大先生の「ダイバーゼロ」に、 確かこんな球体が出てきた覚えがあります。


画像サーバ

画像ファイルや動画ファイルのピクセルデータを取得する処理を、 別プロセスのサーバに切り出してみます。

まぁこんな事をしたら、普通、動作は遅くなるはずです。

これは明日のための布石です。

ややこしいので、従来の交点を求めたりするサーバは、 rt server (レイトレサーバ) とでも呼んで区別するようにします。

v26.patch

$ cat v26.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make clean
$ make

いつものデータで速度比較。

まず従来方式

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v26/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 7m 31.17s

  以前の
  v25  wh : 76800/76800(100.0%) : fin 7m 50.96s
  v24  wh : 76800/76800(100.0%) : fin 7m 44.42s
  v23  wh : 76800/76800(100.0%) : fin 7m 48.33s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v26/objs_1_2_is n=1 init_sec=5 div=2 use_img_srv
  :
wh : 76800/76800(100.0%) : fin 8m 12.84s

( (8*60+12.84) / (7*60+31.17) - 1 ) * 100 = 9.23 パーセント低下

サーバプロセス分離版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v26/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76800/76800(100.0%) : fin 9m 22.96s

  以前の
  v25  wh : 76800/76800(100.0%) : fin 9m 34.91s
  v24  wh : 76800/76800(100.0%) : fin 9m 38.24s
  v23  wh : 76800/76800(100.0%) : fin 9m 9.07s

$ rm xdat

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v26/objs_1_2_s_is n=1 init_sec=5 div=2 use_srv use_img_srv
  :
wh : 76800/76800(100.0%) : fin 9m 54.44s

$ rm xdat

( (9*60+54.44) / (9*60+22.96) - 1 ) * 100 = 5.59 パーセント低下

別プロセスのところをC言語で実装版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v26/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 76800/76800(100.0%) : fin 53.40s

  以前の
  v25  wh : 76800/76800(100.0%) : fin 53.95s
  v24  wh : 76800/76800(100.0%) : fin 54.07s
  v23  wh : 76800/76800(100.0%) : fin 1m 50.29s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v26/objs_1_2_sc_is n=1 init_sec=5 div=2 use_srv srv_c use_img_srv
  :
wh : 76800/76800(100.0%) : fin 1m 6.02s

( (1*60+6.02) / 53.40 - 1 ) * 100 = 23.63 パーセント低下

まぁそうですね。 画像のピクセルデータ取得処理に比べて、 他の箇所が速いほど低下は大きくなります。

では、v25の接近パターンの続きを...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v26/bw_near_2 div=2 use_srv srv_c use_img_srv init_sec=10
  :
wh : 75840/76800(98.8%) : total 3m 3.55s : rest 2.29s : 2018/04/21 13:43:12
wh : 76444/76800(99.5%) : total 3m 3.10s : rest 0.84s : 2018/04/21 13:43:12
wh : 76800/76800(100.0%) : fin 3m 2.88s
frm : 81/300(27.0%) : total 14h 36m 51.49s : rest 10h 40m 6.39s : 2018/04/22 00:23:18
wh : 0/76800(0.0%) : not start yet
Traceback (most recent call last):
  File "./img_sock.py", line 158, in 
    f()
  File "./img_sock.py", line 137, in col
    col = img.col(*lst)
  File "/Users/kondoh/rt_wk/rt_v26/img.py", line 139, in col
    return videos.col(fn, sec, x, y, def_col, rep)
  File "/Users/kondoh/rt_wk/rt_v26/img.py", line 102, in col
    return img_sec(fn, sec)[y, x]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "./cg.py", line 304, in 
    f(data, lights, eye2g, wh2eye, sc_sz, video)
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 149, in draw
    col = get_col(data, lights, l_g, sec)
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 132, in get_col
    col_ = reflact_col()
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 129, in reflact_col
    col = get_col( data, lights, ref_l, sec, nest_rate * reflact, d )
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 132, in get_col
    col_ = reflact_col()
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 129, in reflact_col
    col = get_col( data, lights, ref_l, sec, nest_rate * reflact, d )
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 87, in get_col
    col = map_col( crs, rev, sec )
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 43, in map_col
    cols = ut.filter_lst( lambda col: col != [], cols )
  File "/Users/kondoh/rt_wk/rt_v26/ut.py", line 14, in 
    filter_lst = lambda f, lst: list( filter( f, lst ) )
  File "/Users/kondoh/rt_wk/rt_v26/rt.py", line 40, in f
    return col_func( fn, sec, int(x), int(y), [], m.get('rep') )
  File "/Users/kondoh/rt_wk/rt_v26/img_sock.py", line 79, in col
    (col_, s) = bin.unpack_col(s)
  File "/Users/kondoh/rt_wk/rt_v26/bin.py", line 27, in unpack_col
    ( col[i], s ) = unpack_i4(s)
  File "/Users/kondoh/rt_wk/rt_v26/bin.py", line 17, in 
    unpack_i4 = lambda s: ( struct.unpack_from('=i', s)[0], s[4:] )
struct.error: unpack_from requires a buffer of at least 4 bytes

!?


動画ファイルのフレーム数

先のエラーを追いかけます。

$ ls -lt out_v26/bw_near_2* | head
-rw-r--r--@ 1 kondoh  staff  416097  4 21 13:43 out_v26/bw_near_2.mp4
-rw-r--r--  1 kondoh  staff   28439  4 21 13:43 out_v26/bw_near_200080.jpg
-rw-r--r--  1 kondoh  staff   28495  4 21 13:40 out_v26/bw_near_200079.jpg
-rw-r--r--  1 kondoh  staff   28084  4 21 13:37 out_v26/bw_near_200078.jpg
-rw-r--r--  1 kondoh  staff   27962  4 21 13:34 out_v26/bw_near_200077.jpg
-rw-r--r--  1 kondoh  staff   27857  4 21 13:31 out_v26/bw_near_200076.jpg
-rw-r--r--  1 kondoh  staff   27951  4 21 13:28 out_v26/bw_near_200075.jpg
-rw-r--r--  1 kondoh  staff   27738  4 21 13:25 out_v26/bw_near_200074.jpg
-rw-r--r--  1 kondoh  staff   27942  4 21 13:22 out_v26/bw_near_200073.jpg
-rw-r--r--  1 kondoh  staff   27636  4 21 13:19 out_v26/bw_near_200072.jpg

0,1,2,... ときて 81の処理のどこかで起きてるようなので...

10 * 81 / 300 = 2.7

これで再現するだろうか

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v26/dbg div=2 use_srv srv_c use_img_srv init_sec=12.7
OpenCV: FFMPEG: tag 0x34363248/'H264' is not supported with codec id 28 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x31637661/'avc1'
frm : 0/300(0.0%) : not start yet
wh : 0/76800(0.0%) : not start yet
Traceback (most recent call last):
  File "./img_sock.py", line 158, in 
    f()
  File "./img_sock.py", line 137, in col
    col = img.col(*lst)
  File "/Users/kondoh/rt_wk/rt_v26/img.py", line 139, in col
    return videos.col(fn, sec, x, y, def_col, rep)
  File "/Users/kondoh/rt_wk/rt_v26/img.py", line 102, in col
    return img_sec(fn, sec)[y, x]
TypeError: 'NoneType' object is not subscriptable

再現する

追いかけてみるとどうも

v26.patch

diff -urN rt_v25/img.py rt_v26/img.py
  :
-	video = []
-	while vc.isOpened():
-		(ret, img) = vc.read()
-		if not ret:
-			break
-		video.append(img)
-	n = len(video)
  :
+	kd = { 'i': 1, 'w': 3, 'h': 4, 'fps': 5, 'n': 7 }
+	get_prop = lambda k: vc.get( kd.get(k) )
+	set_prop = lambda k, v: vc.set( kd.get(k), v )
+	fix_type = lambda k, v: int(v) if k != 'fps' else v
+
+	dic = dict( map( lambda k: ( k, fix_type( k, get_prop(k) ) ), kd.keys() ) )
+
+	fps = dic.get('fps')
+	n = dic.get('n')
  :

ここで以前は動画ファイルに含まれるフレームの数nを、 最後までreadしみて決定してましたが、、、

今回のv26では、vc.get(7)で返る結果を使うように変更してました。

さらに、以前は最初に全フレームをreadして保持してましたが、 さすがにメモリを使い過ぎる気がして、 必要なときに vc.set(1, xxx) でseekしてreadするよう変更してました。

どうも、取得したフレーム数 n の範囲内でも、 read に失敗する現象が出てるようです。 (IMG_3999_3.mov の 171 フレームの read で失敗してます)

というか、以前は最初にまとめてreadして、 失敗した段階でそこまでのフレーム数を、動画ファイルのフレームと思っていた。

というか、というか、今回 vc.get(7) で取得するようにした値は、 動画ファイルに含まれるフレーム数として正しい値じゃなかった!?

動画ファイルを画像ファイルに展開してみて、
どこかでエラーがでるか試してみます。

$ ./img.py
Usage: ./img.py img_name video_name fps= zm= cmd=
  fps is int value (default 30)
  zm is float value (default 1.0)
  cmd is v2i, i2v or play (default i2v)

$ ./img.py out_v26/for_dbg_3 IMG_3999_3.mov cmd=v2i
Traceback (most recent call last):
  File "./img.py", line 263, in 
    video_to_imgs(img_name, video_name)
  File "./img.py", line 233, in video_to_imgs
    cv2.imwrite(fn, imgs[i] )
TypeError: 'map' object is not subscriptable
bash-3.2$ 

って、これは違うエラー
python3 対応もれ...

とりあえず

img.py
  :
def read_video(fn):
  :
	#dic['imgs'] = lambda : map( img_idx, range(n) ) # func
	dic['imgs'] = lambda : ut.map_lst( img_idx, range(n) ) # func

で修正して
次のパッチに入れるとして


$ ./img.py out_v26/for_dbg_3 IMG_3999_3.mov cmd=v2i

エラーはでませんな...

ls -l out_v26/for_dbg_3*
-rw-r--r--  1 kondoh  staff  79995  4 22 07:58 out_v26/for_dbg_300000.jpg
-rw-r--r--  1 kondoh  staff  78942  4 22 07:58 out_v26/for_dbg_300001.jpg
-rw-r--r--  1 kondoh  staff  78168  4 22 07:58 out_v26/for_dbg_300002.jpg
  :
-rw-r--r--  1 kondoh  staff  66253  4 22 07:58 out_v26/for_dbg_300168.jpg
-rw-r--r--  1 kondoh  staff  64499  4 22 07:58 out_v26/for_dbg_300169.jpg
-rw-r--r--  1 kondoh  staff  67107  4 22 07:58 out_v26/for_dbg_300170.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300171.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300172.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300173.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300174.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300175.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300176.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300177.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300178.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300179.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300180.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300181.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300182.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300183.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300184.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300185.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300186.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300187.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300188.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300189.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300190.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300191.jpg

ほれー!
171フレーム以降が 0 バイトになってる

さてどうしたものか

$ python
Python 3.6.5 (default, Mar 30 2018, 06:41:49) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

>>> import cv2

>>> vc = cv2.VideoCapture('IMG_3999_3.mov')

>>> vc.get(7)
192.0

>>> list(map( vc.get, range(8) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0]

>>> list(map( vc.get, range(9) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0, 0.0]

>>> list(map( vc.get, range(10) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0, 0.0, 0.0]

>>> list(map( vc.get, range(11) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0, 0.0, 0.0, 0.0]

確かに192を返してて、他に171を返すようなプロパティは無さそうですね...

全部メモリに保持するのは避けるとして、 時間はかかりますが、最初に空読みしてread可能なフレーム数だけ調べる対応にしてみますかな。

img.py

  :
def read_video(fn):
	vc = cv2.VideoCapture(fn)

	kd = { 'i': 1, 'w': 3, 'h': 4, 'fps': 5, 'n': 7 }
	get_prop = lambda k: vc.get( kd.get(k) )
	set_prop = lambda k, v: vc.set( kd.get(k), v )
	fix_type = lambda k, v: int(v) if k != 'fps' else v

	dic = dict( map( lambda k: ( k, fix_type( k, get_prop(k) ) ), kd.keys() ) )

	fps = dic.get('fps')
	n = dic.get('n')

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
  :
		if 'img' not in dic:
			( ret, dic['img'] ) = vc.read()
			if not ret:
				sys.stderr.write( 'err i/n={}/{}\n'.format( dic.get('i'), dic.get('n') ) )
		return dic.get('img')

これならどうか? 解像度を落としてエラーチェック。

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
  :
wh : 300/300(100.0%) : fin 0.80s
frm : 65/300(21.7%) : total 4m 8.20s : rest 3m 14.42s : 2018/04/22 09:52:53
wh : 0/300(0.0%) : not start yet
wh : 300/300(100.0%) : fin 0.80s
wh : 0/300(0.0%) : not start yet
err i/n=171/178
Traceback (most recent call last):
  File "./img_sock.py", line 158, in 
    f()
  File "./img_sock.py", line 137, in col
    col = img.col(*lst)
  File "/Users/kondoh/rt_wk/rt_v27/img.py", line 149, in col
    return videos.col(fn, sec, x, y, def_col, rep)
  File "/Users/kondoh/rt_wk/rt_v27/img.py", line 112, in col
    return img_sec(fn, sec)[y, x]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):

vc.get(7)のプロパティは192が返って、 最初にまとめてread()すると177まで成功するので178フレーム分あると思ってたら、 seekしてやり読み直してると171のreadでエラー!?。

	n = dic.get('n')

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)

seekで先頭に戻してもう一度最初からreadしてみると...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
  :
err i/n=171/178
  :

n=178表示は1回しか出ないので、2回目の171もreadできてる様子?

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	for i in [171, 170, 172]:
		set_prop('i', i)
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 0)

さらに171にseekしてread、170にseekしてread、172にseekしてreadを試すと...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
171 ret=False
170 ret=True
172 ret=False
  :

連続してreadすると177までOKで、seekしてreadするときは170まではOKとかなのだろうか?

  :
	for i in [171, 170, 172]:
		set_prop('i', i)
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 170)
	for i in range(3):
		i += 170
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 0)

170read成功のあと、seekなしで連続でreadしてみると...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
171 ret=False
170 ret=True
172 ret=False
170 ret=True
171 ret=False
172 ret=False

いちどerrが出てしまうと、もうだめとか?

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	#for i in [171, 170, 172]:
	#	set_prop('i', i)
	#	(ret, _) = vc.read()
	#	sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 170)
	for i in range(3):
		i += 170
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 0)

いかに?

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
170 ret=True
171 ret=False
172 ret=False

seek 0のあとは178までread OKで、 seek 170すると171でNG ? 確認

$ python
>>> import cv2
>>> vc = cv2.VideoCapture('IMG_3999_3.mov')
>>> vc.set(1, 170)
True
>>> vc.read()
(True, array([[[ 10,  15,  15],
        [ 10,  15,  15],
        [ 10,  15,  15],
        ...,
        [ 21,  24,  29],
  :
>>> vc.get(1)
171.0
>>> (ret,_) = vc.read()
>>> ret
False

>>> vc.set(1, 0)
True
>>> vc.get(1)
0.0

>>> for i in range(169):
... 	(ret, _) = vc.read()
... 

>>> (ret, _) = vc.read()
>>> ret
True
>>> (ret, _) = vc.read()
>>> ret
True
>>> (ret, _) = vc.read()
>>> ret
True
>>> vc.get(1)
172.0

確かに、0から連続してreadしてる分には171もread OK

さてどうしたものか。

最初連続readで上限を取得しておいて、 つかってて途中でエラーでたら、 seek 0で先頭からreadしてなんとかするとか。

	def err_recover():
		if vc.isOpened():
			vc.open(fn)
			if 'img' in dic:
				dic.pop('img')
		if get_prop('i') != 0:
			set_prop('i', 0)

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			break
	err_recover()

	def img_idx(i):
		i %= n # loop
		if 'img' in dic and dic.get('i') != i:
			dic.pop('img')
			if dic.get('i')+1 != i:
				set_prop('i', i)
			dic['i'] = i
		if 'img' not in dic:
			( ret, dic['img'] ) = vc.read()
			if not ret:
				err_recover()
				for j in range(i+1):
					( ret, dic['img'] ) = vc.read()
					if not ret:
						sys.stderr.write( 'err {} {}/{}\n'.format( fn, j, dic.get('n') ) )
				dic['i'] = i
		return dic.get('img')
	dic['img_idx'] = img_idx

これでトライ

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
  :
wh : 300/300(100.0%) : fin 0.67s
frm : 300/300(100.0%) : fin 3m 38.28s

一応エラーではとまらなくなりました。

パッチを作っておいて。

v27.patch

それでは、前回のリベンジ。

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_2 div=2 use_srv srv_c use_img_srv init_sec=10
  :
wh : 76530/76800(99.6%) : total 2m 44.76s : rest 0.57s : 2018/04/23 04:08:46
wh : 76800/76800(100.0%) : fin 2m 44.74s
frm : 300/300(100.0%) : fin 14h 22m 30.00s

とりあえずエラーでとまらずに10秒の動画を生成できました。 14時間半。画像サーバに切り出す前は確か11時間半。

では、いつものデータで速度比較。

まず従来方式

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 7m 45.41s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 7m 31.17s
  v25  wh : 76800/76800(100.0%) : fin 7m 50.96s
  v24  wh : 76800/76800(100.0%) : fin 7m 44.42s
  v23  wh : 76800/76800(100.0%) : fin 7m 48.33s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_is n=1 init_sec=5 div=2 use_img_srv
  :
wh : 76800/76800(100.0%) : fin 8m 19.96s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 8m 12.84s

レイトレ用のサーバプロセス分離版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76800/76800(100.0%) : fin 9m 18.49s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 9m 22.96s
  v25  wh : 76800/76800(100.0%) : fin 9m 34.91s
  v24  wh : 76800/76800(100.0%) : fin 9m 38.24s
  v23  wh : 76800/76800(100.0%) : fin 9m 9.07s

$ rm xdat

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_s_is n=1 init_sec=5 div=2 use_srv use_img_srv
  :
wh : 76800/76800(100.0%) : fin 9m 53.40s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 9m 54.44s

$ rm xdat

レイトレ用のサーバプロセスをC言語で実装版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 76800/76800(100.0%) : fin 53.66s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 53.40s
  v25  wh : 76800/76800(100.0%) : fin 53.95s
  v24  wh : 76800/76800(100.0%) : fin 54.07s
  v23  wh : 76800/76800(100.0%) : fin 1m 50.29s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_sc_is n=1 init_sec=5 div=2 use_srv srv_c use_img_srv
  :
wh : 76800/76800(100.0%) : fin 1m 8.39s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

まぁ、変わらず。


サーバとの通信の処理を整理

rt_sock.py と img_sock.py が同じようなコードなので、sock.py として括り出してまとめてみました。

あと、あんちょこファイルのsave, loadは、もう役目を終えたようなので廃止しておきます。

v28.patch

$ cat v28.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make clean
$ make

まず、いつものデータで速度比較。

まず従来方式

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v28/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 7m 42.09s

  以前の
  v27  wh : 76800/76800(100.0%) : fin 7m 45.41s
  v26  wh : 76800/76800(100.0%) : fin 7m 31.17s
  v25  wh : 76800/76800(100.0%) : fin 7m 50.96s
  v24  wh : 76800/76800(100.0%) : fin 7m 44.42s
  v23  wh : 76800/76800(100.0%) : fin 7m 48.33s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v28/objs_1_2_is n=1 init_sec=5 div=2 use_img_srv
  :
wh : 76800/76800(100.0%) : fin 8m 5.03s

  以前の
  v27  wh : 76800/76800(100.0%) : fin 8m 19.96s
  v26  wh : 76800/76800(100.0%) : fin 8m 12.84s

サーバプロセス分離版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v28/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76800/76800(100.0%) : fin 9m 34.23s

  以前の
  v27  wh : 76800/76800(100.0%) : fin 9m 18.49s
  v26  wh : 76800/76800(100.0%) : fin 9m 22.96s
  v25  wh : 76800/76800(100.0%) : fin 9m 34.91s
  v24  wh : 76800/76800(100.0%) : fin 9m 38.24s
  v23  wh : 76800/76800(100.0%) : fin 9m 9.07s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v28/objs_1_2_s_is n=1 init_sec=5 div=2 use_srv use_img_srv
  :
wh : 76800/76800(100.0%) : fin 10m 9.66s

  以前の
  v27  wh : 76800/76800(100.0%) : fin 9m 53.40s
  v26  wh : 76800/76800(100.0%) : fin 9m 54.44s

別プロセスのところをC言語で実装版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v28/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 76800/76800(100.0%) : fin 1m 3.32s

  以前の
  v27  wh : 76800/76800(100.0%) : fin 53.66s
  v26  wh : 76800/76800(100.0%) : fin 53.40s
  v25  wh : 76800/76800(100.0%) : fin 53.95s
  v24  wh : 76800/76800(100.0%) : fin 54.07s
  v23  wh : 76800/76800(100.0%) : fin 1m 50.29s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v28/objs_1_2_sc_is n=1 init_sec=5 div=2 use_srv srv_c use_img_srv
  :
wh : 76800/76800(100.0%) : fin 1m 18.06s

  以前の
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

肝心の「別プロセスのところをC言語で実装版」で、大きく低下してるのがまずいですね。 サーバからの結果を受け取ってからの処理に手を入れてる影響かもしれません。 1度listにしてからdictにしてるので、もうちょっと良く考えるべしですね。

v25で生成した動画と、その続きとしてv27で生成した動画をつないでおきます。

$ ./img.py tmp/a1 out_v25/bw_near_2.mp4 cmd=v2i
$ ./img.py tmp/a2 out_v27/bw_near_2.mp4 cmd=v2i
$ ./img.py tmp/a out_v28/bw_near_2.mp4

$ ls -l out_v28/bw_near_2.mp4 
-rw-r--r--  1 kondoh  staff  2437277  4 24 01:20 out_v28/bw_near_2.mp4

セーフ。まだ3Mバイトを超えてません。


「値」サーバ追加で実装を大幅に更新

コードの整理をどんどん進めてるうちに、結構手が入ってしまいました。

まず「値」サーバなるものを追加しました。

これはファイルパスの文字列を整数値で扱いたいために考えたサーバで、 パス文字列をサーバの中のリストに登録追加していって、 サーバに登録済みの文字列を渡すとリスト中のインデックスの整数が返り、 整数を渡すと対応する文字列が返る。

というような動作をするものでした。

まず、この登録文字列に対して「インデックス以外の整数でも返せるようにしたいなぁ」 というところから、プロパティの値を別に持たせるようにしようと。

ならば、値は整数だけじゃなく他の値も返してくれたら便利では?

プロパティの型情報も保持させなきゃ。

サーバとの通信で値を流すなら、ちゃんとシリアライズできねば。

可変長のリストもプロパティの値として扱いたいなぁ。

どうせなら辞書の形式も。

どんどん妄想が膨らんでしまい、bin.py pks.py sock.py val.py の辺りに落ち着きました。

あと、画像、動画を参照するサーバもかなり手を入れました。 結局動画の読み込みは、1つのフレームの画像を辞書で保持してメモリ使いまくり方式に。 そして呼び出して使う側もサーバを使う前提固定に。

v29.patch

$ cat v29.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make clean
$ make

色々と整理したところで、 とりあえず交点を求める処理のサーバだけC言語版を実装して、 とりあえず処理速度の測定。

別プロセスのところをC言語で実装版でイメージ用のサーバプロセスを使う場合

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v29/objs_1_2_sc n=1 init_sec=5 div=2 use_srv
  :
wh : 76800/76800(100.0%) : fin 1m 44.72s

  以前の
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

一方、処理速度はじわじわ低下する一方。

サーバ群は起動しっぱなしなので、落としたい場合はとりあえずkillコマンドで。

$ ps ax | grep ' boot$'
20739   ??  Ss     0:04.73 /usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python ./val.py boot
20750   ??  Ss     0:18.00 /usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python ./img.py boot
20765   ??  SNs    0:15.19 ./cross boot

$ ps ax | grep ' boot$' | cut -d ' ' -f1
20739
20750
20765

$ ps ax | grep ' boot$' | cut -d ' ' -f1 | xargs kill

$ ps ax | grep ' boot$'
$ 


徐々にC言語実装で侵略

rt.pyの主要部分のコードを徐々にrt.cに移して試してますが... 1つの壁は「get_col()の再帰」です。

このあたりでpython側とC側を行ったり来たりしていては、 結局もたもたして速度が上がりませぬ。

という事でget_col()ごとゴッソリとrt.cへ持って行ってみました。

v30.patch

$ cat v30.patch | ( cd rt ; patch -p1 )

$ cd rt

$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v30/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 38.39s

  以前の
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

大幅に短縮! よしよし v^_^)

そして起動しっぱなしのサーバを落とすには、

$ ps ax | grep ' boot$' | cut -d ' ' -f1 | xargs kill

そのうち、もうちょっと簡単な方法を用意せねば...

例のデータでも試してみます。 まずは荒い解像度で。

$ ./cg.py eyep=[100,0,0],100,5 sec=10 data_name=ball_world name=out_v30/bw_tst div=32
  :
frm : 300/300(100.0%) : fin 1m 39.28s

解像度フルでの予想時間は

( (60+39.28) *32*32 ) /60/60 = 28.23 時間

div=2の設定ならば

( (60+39.28) *16*16 ) /60/60 = 7.05 時間

ここらで手を打ってみましょう。

$ ./cg.py eyep=[100,0,0],100,5 sec=10 data_name=ball_world name=out_v30/bw_2 div=2
  :
wh : 76800/76800(100.0%) : fin 1m 5.33s
frm : 300/300(100.0%) : fin 5h 22m 17.99s

予想より若干短く5時間半弱。

視点移動の中心をちょっとずらしたので、5秒あたりで真上からのアングルになった瞬間、 カメラの姿勢がロール方向に180度回転。 一瞬で球と立方体の位置関係が入れ替わって見えてます。


他のマシンでお手伝い

おうちに転がってるネットブックな非力マシン ASUS Eee PC 901。

このマシンでもレイトレーシングの計算のお手伝いをさせてみようかと。

久しぶりに起動。

$ uname -a
Linux kondoh-901 3.13.0-66-generic #108-Ubuntu SMP Wed Oct 7 15:21:40 UTC 2015 i686 i686 i686 GNU/Linux

$ cat /etc/issue
Ubuntu 14.04.3 LTS \n \l

$ dmesg | grep 'CPU.*Intel'
[    0.082673] smpboot: CPU0: Intel(R) Atom(TM) CPU N270   @ 1.60GHz (fam: 06, model: 1c, stepping: 02)

$ ps ax | grep sshd | grep -v grep
$ 

とりあえず sshd をば

$ sudo apt-get update
$ sudo apt-get install openssh-server
$ ps ax | grep sshd | grep -v grep
 5932 ?        Ss     0:00 /usr/sbin/sshd -D
$ 

$ ifconfig -a
  :
wlan0     Link encap:イーサネット  ハードウェアアドレス 00:15:af:e7:01:99  
          inetアドレス:192.168.1.9  ブロードキャスト:192.168.1.255  マスク:255.255.255.0
  :

では、Macからlogin

$ ssh 192.168.1.9
The authenticity of host '192.168.1.9 (192.168.1.9)' can't be established.
ECDSA key fingerprint is SHA256:OH5rwJOAh88nkIFuP9r8jdvYOgIZ9ewRBGS5qioQjp4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.9' (ECDSA) to the list of known hosts.
kondoh@192.168.1.9's password: 
[パスワード入力]

Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-66-generic i686)

 * Documentation:  https://help.ubuntu.com/
  :
$ 

pythonは?

$ which python
/usr/bin/python

$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D
$ 

numpy はインストールした覚えがないのでとりあえず入れておきます。

$ sudo apt-get install python-numpy
$ python
 :
>>> import numpy
>>> 
>>> ^D
$ 
$ exit

Mac側に戻って今回のパッチ。

v31.patch

$ cat v31.patch | ( cd rt ; patch -p1 )

$ mkdir ttt
$ tar cf - rt | tar xf - -C ttt
$ mv ttt/rt rt_v31
$ rmdir ttt

$ cd rt_v31

$ make clean
$ make

今回は都合でディレクトリ名は rt/ じゃなくて rt_v31/ にしてます。

そして、ちょっとやばそうなツールを作ってみました。 まぁ自分のマシンで勝手に試すだけなので...

$ ./cmd_send.py host=192.168.1.9
cmd="tar cf - cmd.py | ssh -R55511:localhost:55511 -R55512:localhost:55512 \
-L55520:localhost:55510 -L55523:localhost:55513 -L55524:localhost:55514 \
-L55525:localhost:55515 -L55526:localhost:55516 -L55527:localhost:55517 \
-L55528:localhost:55518 -L55529:localhost:55519 192.168.1.9 "cd /tmp ; tar xf - ; ./cmd.py port=55510""
kondoh@192.168.1.9's password: 
[ パスワード入力 ]

sshでMacから192.168.1.9のネットブックに接続しますが、 -R と -L オプションでポートフォワーディングの設定をしてます。

Macのポート55511, 55512 をネットブックに見せつつ、 ネットブックのポート 55510, 55513, 55514 ... 55519 を、 Macの55520, 55523, 55524 ... 55529 として、 +10したポートで見えるようにしてます。

Macで起動するsshの標準入力に cmd.py をtarでアーカイブしたデータをながしつつ、

tar cf - cmd.py | ssh ...

ssh でログインした先のネットブック上では、 /tmp に移動して、そこに標準入力から cmd.py のアーカイブデータを展開。 展開したての cmd.py をオプション port=55510 を指定して実行します。

... cd /tmp/ ; tar xf - ; ./cmd.py port=55510

cmd_send.py コマンドは起動したままにして、sshの接続を保っておきます。 これでネットブックへのバックドアの仕込み完了。 ネットブックのポート55510でコマンドを受け付ける口をあけてます。 このポートはsshの経路を通して、Mac側のポート55520として見えてます。

Mac側から

$ echo hostname | nc localhost 55520
kondoh-901
$ 

$ echo pwd | nc localhost 55520
/tmp
$ 

つづいて、Macからソースコードを送りつけてビルドします。

$ make clean

$ ./src_send.py path=../rt_v31
d=.. n=rt_v31
rmt_cmd=echo "nc localhost -l 55519 | tar xf -" | nc localhost 55520
lcl_cmd=tar cf - -C .. rt_v31 | nc localhost 55529
wait
cmd=echo "cd rt_v31 ; make clean ; make" | nc localhost 55520
$ 

$ echo ls | nc localhost 55520
cmd.py
config-err-cLU0tn
rt_v31
$ 

$ make

やってる事は

ここで使うポートの決め事を少々

srvs.py に記録してる
  :
base_port = 55500
  :

55500 からの 100ポート分を予約です。

10の位の値は
1: 起動してるサーバが使用
2: ネットブック側で起動してるサーバをMac側から見えるポート

下1桁の割り振りは
0: コマンド実行用
1: 値サーバ val.py
2: 画像サーバ img.py
3: レイトレサーバ rt
4: 交点を求める用のサーバ cross (今回使わず)
  :
9: ファイルコピー時にデータを流す用

Mac側で実行してる、値サーバ 55511 と 画像サーバ 55512 は、 ネットブック側でも ssh のポートフォワーディングで、 同じ 55511, 55512 として見えてます。

srvs.py の infs = [ ... ] で設定している 'srv.rt-2' がネットブック側で実行するレイトレサーバの設定になります。

base_port = 55500

infs = [
	( 'srv.val', { 'port': base_port + 11, 'cmd': './val.py boot', 'pid': -1 } ),
	( 'srv.img', { 'port': base_port + 12, 'cmd': './img.py boot', 'pid': -1 } ),
	( 'srv.rt', { 'port': base_port + 13, 'cmd': './rt boot', 'pid': -1 } ),
	( 'srv.cross', { 'port': base_port + 14, 'cmd': './cross boot', 'pid': -1 } ),

	( 'srv.rt-2', { 'port': base_port + 23, 'cmd': 'echo "cd rt_v31 ; ./rt boot" | nc localhost {}'.format(base_port + 20), 'pid': -1 } ),
]

'srv.rt-2' を起動するためのコマンド文字列が、 "cd rt_v31 ; ./rt boot" という文字列を、ポート55520 にncで送り込むコマンドになってます。

これでネットブックの55513ポートを使うレイトレサーバrtがネットブックで起動しますが、 ssh の設定でMac側の55523ポートとして見えることになります。

仕込みが完了したところでお試し実行。

Mac側から

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v31/objs_1_2_sc n=1 init_sec=5 div=2
  :
conn srv.val 55511 ... err
[Errno 61] Connection refused
boot ./val.py boot
conn srv.val 55511 ... ok
boot ./img.py boot
conn srv.val 55511 ... ok
conn srv.img 55512 ... ok
OpenCV: FFMPEG: tag 0x34363248/'H264' is not supported with codec id 28 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x31637661/'avc1'
frm : 0/1(0.0%) : not start yet
wh : 0/76800(0.0%) : not start yet
boot ./rt boot
boot echo "cd rt_v31 ; ./rt boot" | nc localhost 55520
conn srv.rt 55513 ... conn srv.rt-2 55523 ... ok
ok
wh : 2/76800(0.0%) : total 10h 58m 28.06s : rest 10h 58m 27.03s : 2018/05/07 03:31:42
wh : 4925/76800(6.4%) : total 31.63s : rest 29.60s : 2018/05/06 16:33:45
wh : 9829/76800(12.8%) : total 23.66s : rest 20.63s : 2018/05/06 16:33:37
  :
wh : 74467/76800(97.0%) : total 41.29s : rest 1.25s : 2018/05/06 16:33:55
wh : 76800/76800(100.0%) : fin 40.50s

conn srv.val 55511 ... err

については、まず値サーバに接続を試してみて失敗すれば、起動を試みるようにしてるので想定通りです。

  以前の
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

なんと、ネットブックに手伝ってもらった方が足を引っ張られてる感じでしょうか。

rt.py の

			:
	def func(cli):
		while True:
			lock.acquire()
			run = cnt.up(show)
			(ix, iy) = cnt.cur()
			#print('{} {} {}'.format(cli.name, ix, iy))
			lock.release()
			if not run:
				break
			:

コメントアウトしてるprint()を有効にしてみると

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v31/objs_1_2_sc n=1 init_sec=5 div=2 > log1

$ head log1
conn srv.val 55511 ... ok
conn srv.img 55512 ... ok
frm : 0/1(0.0%) : not start yet
wh : 0/76800(0.0%) : not start yet
srv.rt 0 0
srv.rt-2 1 0
conn srv.rt 55513 ... conn srv.rt-2 55523 ... ok
ok
srv.rt 2 0
srv.rt 3 0
$ 

$ grep '^srv.rt-2 ' log1 | head
srv.rt-2 1 0
srv.rt-2 11 1
srv.rt-2 173 1
srv.rt-2 58 2
srv.rt-2 243 2
srv.rt-2 112 3
srv.rt-2 319 3
srv.rt-2 173 4
srv.rt-2 47 5
srv.rt-2 249 5

$ grep '^srv.rt ' log1 | wc -l
   76332

$ grep '^srv.rt-2 ' log1 | wc -l
     470


320*240 = 76800
なので

470/76800*100 = 0.61

ネットブックは0.61パーセントしか力になってませんでしたか...

起動したままのサーバを落としておきます。

Mac側から

$ ps ax | grep boot | grep -v grep
33484   ??  Ss     0:08.91 /usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python ./val.py boot
33491   ??  Ss     0:29.44 /usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/Resources/Python.app/Contents/MacOS/Python ./img.py boot
33498   ??  SNs    0:16.92 ./rt boot
33499   ??  SNs    0:00.01 /bin/sh -c echo "cd rt_v31 ; ./rt boot" | nc localhost 55520

$ ./srvs.py kill_all
conn srv.val 55511 ... ok
kill srv.img pid=33491
kill srv.rt pid=33498
kill srv.rt-2 pid=33499
kill srv.val pid=33484

$ ps ax | grep boot | grep -v grep
$ 

$ echo "ps ax | grep boot | grep -v grep" | nc localhost 55520
12862 ?        S      0:00 /bin/sh -c cd rt_v31 ; ./rt boot
12863 ?        S      0:01 ./rt boot

$ ./kill_str.py 'rt boot'
ps ax | grep 'rt boot' | grep -v grep | sed 's/^ *//' | cut -d ' ' -f1 | xargs kill

$ ./kill_str.py 'rt boot' | nc localhost 55520

$ echo "ps ax | grep boot | grep -v grep" | nc localhost 55520
$ 


Macで複数のサーバをあげてみると

1つのMacの中で複数のレイトレサーバを起動して試してみます。

v32.patch

$ mv rt_v31 rt_v32
$ cat v32.patch | ( cd rt_v32 ; patch -p1 )
$ cd rt_v32
$ make clean
$ make

rt.py の names の箇所

  :
names = [ 'srv.rt', 'srv.rt-14', 'srv.rt-15', 'srv.rt-16', 'srv.rt-17', 'srv.rt-18' ]
names = names[:2]
  :

この箇所で起動するレイトレサーバの数を調整します。

まずサーバ1つの設定に書き換えて

  :
names = names[:1]
  :
$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v32/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 39.83s
  以前の
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

サーバ数を2に戻して

  :
names = names[:2]
  :
$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v32/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 26.84s

サーバ数を3,4,5,6で順次試すと

names = names[:3]
wh : 76800/76800(100.0%) : fin 28.43s

names = names[:4]
wh : 76800/76800(100.0%) : fin 28.81s

names = names[:5]
wh : 76800/76800(100.0%) : fin 29.13s

names = names[:6]
wh : 76800/76800(100.0%) : fin 29.35s

サーバ2つが最速でじわじわ劣化していきますね。 いづれにせよ1つの処理プロセスだけでは、コアを使い切れてなかったのは確かなようです。

サーバ2つの最速設定に戻して、 時間のかかっていた以前のデータで描画を試してみます。

names = names[:2]

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v32/objs_tst fps=1
  :
wh : 307200/307200(100.0%) : fin 2m 9.34s
frm : 10/10(100.0%) : fin 19m 35.11s

fps=30での予想時間は

( 19*60+35.11 ) * 30 / 60/60 = 9.79時間
$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v32/objs
  :
wh : 305132/307200(99.3%) : total 2m 14.94s : rest 0.90s : 2018/05/07 08:15:45
wh : 307200/307200(100.0%) : fin 2m 14.93s
frm : 300/300(100.0%) : fin 10h 2m 5.22s

$ ./cg.py eyep=[0,0,0],50,10 sec=10 data_name=objs name=out_v32/objs_near
  :
wh : 307200/307200(100.0%) : fin 2m 4.99s
frm : 300/300(100.0%) : fin 10h 27m 29.13s

$ ./srvs.py kill_all

ちとアングルが近過ぎた。


バグ修正

ワイヤーフレーム表示を永らく試してなかったら、まともに動かなくなってました。 修正しておきます。

ball_world2のデータを試そうとすると、マシンによってはmalloc()しすぎのエラーで落ちてました。

この現象の原因は、円柱マッピングのデータを受け取るC言語側の構造体が、 マシンによってはパディングが入り、後続のデータ解釈にズレが出るためでした。 修正しておきます。

サーバを終了してから数分待たずに同じアドレスでbindしようとすると

OSError: [Errno 48] Address already in use

が出てしまうのがアレなので、setsockopt で SO_REUSEADDR を設定するようにしてみました。

あと、src_send.py からの nc と tar によるファイルコピーで、 サイズが大きいと落ちる現象が出てしまうのですが、、、 原因が突き止められてません(>_<)

v33.patch

$ mv rt_v32 rt_v33
$ cat v33.patch | ( cd rt_v33 ; patch -p1 )
$ cd rt_v33
$ make clean
$ make
$ ./cg.py eyep=[100,0,0],100,5 sec=10 data_name=ball_world2 name=out_v33/bw2_wf wf
  :
frm : 300/300(100.0%) : fin 42.18s

$ ./cg.py eyep=[100,0,0],100,5 sec=10 data_name=ball_world2 name=out_v33/bw2
  :
wh : 307200/307200(100.0%) : fin 3m 14.75s
frm : 300/300(100.0%) : fin 14h 43m 10.50s

$ ./srvs.py kill_all
-rw-r--r--  1 kondoh  staff  3394859  5  8 20:41 bw2.mp4

おっと3Mバイト制限!

動画ファイルをほどいて、 適当に3Mになるまで末尾を削る機能を img.py に追加しておきます。(パッチは次回に含めます)


動画書き込みも画像サーバの機能に追加

まず、前回で未解決だった問題。

あと、src_send.py からの nc と tar によるファイルコピーで、 サイズが大きいと落ちる現象が出てしまうのですが、、、 原因が突き止められてません(>_<)

判りました。

色々試してみると、 sshでリモートマシンに手動でログインして、手動でcmd.pyを起動した場合では何の問題も出ず。 cmd_send.py で cmd.py をリモートマシンに送りつけて起動すると、問題が出ます。 怪しい。

cmd_send.py の

	rmt_cmd = 'cd /tmp ; tar xf - ; ./cmd.py port={}'.format( get_port(lcl_base, cmd) )
	cmd = 'tar cf - cmd.py | ssh {} {} {} "{}"'.format(opt_exp, opt_fwd, rmt_host, rmt_cmd)

ここ!

ここで、リモートマシン側で ./cmd.py を起動するときの標準入力が、 sshを経由して、接続元のローカルマシンで流し込んだ 「tarデータの終端」が見えてる状態になってました。

cmd.pyから起動する全てのコマンドの標準入力は、この状態を受け継いでました。 ファイルコピー用のデータを受け取るncコマンドも例外やおまへん。 ncは標準入力から読み込み、接続先のソケットに送信しようとしますが、 標準入力では「データの終端」EOFが見えてるので、終了してしまう。

さてどうしたものか。 発想を切り替えて、ファイルコピー時はローカルマシン側でncをサーバに、 リモートマシン側でncをクライアントにして対応してみました。

ローカルマシン側でncサーバをたてて、誰かが繋ぎにきたらtarデータを送りつけます。

リモートマシン側ではssh経由でncクライアントを起動して、ローカルマシンに繋ぎにいきます。 繋がるとtarデータが流れてくるので、tarコマンドで受けて展開します。

これでsshの標準入力にtarデータを流さずともよろしかろうと。

あと明日への布石のため、動画を書き込む処理を画像サーバに含めてしまいました。

こうしておけば、複数のレイトレサーバが個別にピクセルを処理した後、 おのおのが画像サーバに結果を渡せるようになるはずです。 レイトレサーバに一度に複数のピクセルを処理させるための変更が、楽になりそうです。

v34.patch

$ mv rt_v33 rt_v34
$ cat v34.patch | ( cd rt_v34 ; patch -p1 )
$ cd rt_v34
$ make clean
$ make

では、速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v34/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 38.47s
  以前の
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

速度的には後退。 まぁそうなりますよね。 明日への布石という事で。

視点移動の高さ方向を抑制してみてobjsのデータを眺めてみます。

$ ./cg.py eyep=[0,0,12],[100,100,10],10 sec=10 data_name=objs name=out_v34/objs_tst div=4 fps=1
  :
wh : 18860/19200(98.2%) : total 14.25s : rest 0.25s : 2018/05/09 01:29:10
wh : 19200/19200(100.0%) : fin 14.27s
frm : 10/10(100.0%) : fin 2m 10.59s

( 2*60+10.59 ) * 4 * 4 * 30 / 60 / 60 = 17.41 時間の予想
$ ./cg.py eyep=[0,0,12],[100,100,10],10 sec=10 data_name=objs name=out_v34/objs
  :
wh : 307200/307200(100.0%) : fin 3m 41.64s
frm : 300/300(100.0%) : fin 17h 46m 26.78s

ほぼ予想時間通りでした。

ベンチマークでさんざんしがんだデータでも、視点を変えてみるとまた違った味わいがありますね。


サーバで一度に複数のピクセルを処理

レイトレサーバに一度に複数のピクセルを処理させるための変更をしてみました。

また、画像サーバ側も一度に複数のピクセルを書き込めるように変更しております。

rt.py
  :
names = names[:2]
  :
rates = map( lambda name: 0.2 / len(names), names )
  :

1つのrtサーバが一度に処理するピクセル数は、 1枚の画面のピクセル数の20パーセントを、rtサーバ数で割った数にしてみました。

同じくらいの処理能力のrtサーバならば、 1つのrtサーバあたり5回まわれば、画面が1つ描画されます。

v35.patch

$ mv rt_v34 rt_v35
$ cat v35.patch | ( cd rt_v35 ; patch -p1 )
$ cd rt_v35
$ make clean
$ make

では、速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v35/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 16.16s
  以前の
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

最速出ました v^_^)

(1 - 16.16/26.84) * 100 = 39.79

過去の最速から約40パーセント短縮です。

今回は裏面も見えるようにズラしてみます。 まず荒削り設定で。

$ ./cg.py eyep=[0,0,-5],[100,100,10],10 sec=10 data_name=objs name=out_v35/objs_tst div=4 fps=10
  :
wh : 19200/19200(100.0%) : fin 4.67s
frm : 100/100(100.0%) : fin 8m 42.26s

(8*60+42.26) *4*4*3 /60/60 = 6.96 時間の予想

このデータ、初期の頃では10秒の動画を作るのに、15日くらいかかる予想が出てたはずです。 荒削りで10分未満、フルでも7時間の予想。本当だろうか?

$ ./cg.py eyep=[0,0,-5],[100,100,10],10 sec=10 data_name=objs name=out_v35/objs
  :
wh : 307200/307200(100.0%) : fin 1m 19.09s
frm : 300/300(100.0%) : fin 8h 7m 5.64s

8時間でした。

-rw-r--r--  1 kondoh  staff  2722444  5  9 19:45 out_v34/objs.mp4
-rw-r--r--  1 kondoh  staff  1889074  5 10 07:11 out_v35/objs.mp4

3M制限もOK。 同じ10秒の動画でも、フレームごとの単純さとフレーム間の変化の具合で、ファイルサイズはバラバラです。

さらに、続きの10秒を。

$ ./cg.py eyep=[0,0,-5],[100,100,10],10 sec=10 data_name=objs name=out_v35/objs_s10 init_sec=10
  :
wh : 307200/307200(100.0%) : fin 55.07s
frm : 300/300(100.0%) : fin 7h 17m 34.75s

$ ./srvs.py all_kill
-rw-r--r--  1 kondoh  staff  1711910  5 11 03:44 out_v35/objs_s10.mp4

繋ぎ合わせてみます。

$ (cd out_v35 ; ls objs_s10*.jpg | sed -e 's/objs_s10\(.*\)/mv objs_s10\1 objs1\1/' | sh)

$ ls out_v35 | head
objs100001.jpg
objs100002.jpg
objs100003.jpg
objs100004.jpg
objs100005.jpg
objs100006.jpg
objs100007.jpg
objs100008.jpg
objs100009.jpg
objs100010.jpg

$ ./img.py out_v35/objs out_v35/objs.mp4 cmd=v2i

$ ls -l out_v35/objs_sum.mp4 
-rw-r--r--  1 kondoh  staff  3484415  5 11 04:08 out_v35/objs_sum.mp4

20秒だとさすがに3M超えました。

3M分のフレームまでにするツールの処理を、
バイナリサーチを使うように改良したので試しておきます。

$ ./img.py dmy out_v35/objs_sum.mp4 cmd=3M

$ ls -l v.mp4 
-rw-r--r--  1 kondoh  wheel  3144776  5 11 04:15 v.mp4

OK 3M以内。

$ cp v.mp4 out_v35/objs_sum_3m.mp4


物体の繰り返しコピー配置

同じ物体をコピーして繰り返し位置をズラして配置できるようにしてみました。

もともと立方体(cube)などのデータは、dat.py d_setup(d) 関数で、 6つの四角平面(square)に展開されます。

d_setup(d)の呼び出し元の dat.py setup()では、

	data = sum( map( d_setup, data ), [] )

などとしており、これは data = [ d, d, d, ... ] から d を取り出して d_setup(d) を呼び出してます。 d_setup(d) では d に処理を施し、結果を [ d ]、 あるいは複数に展開した後の [ d, d, d ... ] を返します。

それら [ [ d, d, ... ], [ d, d, ... ] ... ] を sum( ..., [] ) で連結して、 [ d, d, ... ] にしてます。

この仕組みを使って、d に「繰り返しコピー展開」の属性'rxs'があると、 d_setup(d) の中で複数のデータに展開して返すようにしてみました。

お試しデータは

dat.py
  :
rxs_test = [ {
	'kind': 'ball',
	'rtd': rtd,
	'm2g': [ ax.zoom_all(10), ax.slide([-15*4,-15*3,-15*2]) ],
	'rxs': [
		( 'm2g', 'append', 4, [ ax.slide_x(30) ] ),
		( 'm2g', 'append', 3, [ ax.slide_y(30) ] ),
		( 'm2g', 'append', 2, [ ax.slide_z(30) ] ),
	]
},{
	'kind': 'ball',
	'rtd': { 'diff': 0.3 },
	'maps': [ { 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 'r': 0.3, 'rep': (1,1) } ],
	'm2g': [ ax.zoom_all(r) ],
} ]
  :

rxs はリストで複数の rx を格納します。

rx とは

(コピーするときの変換位置, そこへの挿入か追加の指定, コピー数, 繰り返し施す変換)

そもそも物体のデータの構造は次のようになってます。

{
	kind: 球(中心原点で半径1)とか、立方体(中心原点で一辺が-1から+1)とか

	l2m: 変換のリスト。上記のローカル座標系を、マッピング座標系に変換するためのもの
	m2g: 変換のリスト。マッピング座標系をグローバル座標系に変換するためのもの

	l2g: ローカル座標系からグローバル座標系に変換するためのもの
	     (なければ d_setup() でl2m と m2g を連結して作られる)
	  :             
}

物体を配置するときの処理を概念的に説明すると、次のような感じです。

例えば半径1の球は、l2m で拡大、縮小、変形などの変換をした後、画像を照射するマッピングのステージにあげます。

そこで画像データを、平行光線のように照射したり、点光源からの光のように照射したり、 はたまた直線を軸とする光源から円柱状に照射したりして、対象の球に画像を照らして焼き付けます。

画像が焼き付けられた球は、さらにm2gで変換してグローバル座標のステージに配置されます。

で、で、繰り返しコピー展開 rx に戻って、 (コピーするときの変換位置, そこへの挿入か追加の指定 ... ) は、 どの変換の段階でコピーするのかを指定します。

そして、指定の処理位置でコピー数分のコピーをとると、 rx の最後の指定の「繰り返し施す変換」をかけます。

最初の1つめのコピーは、変換なしのオリジナル。 次のコピーは「繰り返し施す変換」を1回かけたもの、 さらに次のコピーは「繰り返し施す変換」を2回かけたもの、...という具合。

まぁ処理の概念的には上記の通りですが、 実際の処理は、指定の変換リスト(l2mやm2g)の先頭か末尾に、 「繰り返し施す変換」を追加していくだけです。

そのrxによるコピーで1つのデータが複数になり。 rxs のリストから次の rx を取り出して処理すると、そのデータ群がコピーされて複数のデータ群に。 などと、rxs のリストが空になるまでコピーが繰り返されます。

dat.py のパッチは短いコードですが、かなり深い内容です。

+	if 'rxs' in d:
+		rxs = d.get('rxs')
+		if not rxs:
+			d.pop('rxs')
+			return d_setup(d)
+		(targ_k, i_a, n, lx) = rxs.pop(0)
+
+		targ = d.get(targ_k, [])
+		ds = []
+		for i in range(n):
+			d_ = d.copy()
+			d_['rxs'] = rxs[:] # !
+			d_[targ_k] = targ[:]
+			ds.append(d_)
+			targ = lx + targ if i_a == 'insert' else targ + lx
+		return sum( map( d_setup, ds ), [] )
+	###
+

今回のその他の変更

画像サーバの中に動画書き込みの機能を入れ込んでましたが、 サーバの中でコネクションスレッドごとのロックとかがあり、 微妙に待ちが生じるので、別のサーバに分離しました。

ソースコードはimg.pyの中に一緒に入ってますが、 別のsrv.vwtサーバとして./img.py boot_vwt で起動します。

また、レイトレサーバに複数のピクセル位置をリストで渡していましたが、、、 ピクセル位置は連続保証にしたので、 「先頭のx,y位置」と「続く個数」的な情報で渡し、 可変長を嫌って固定長にしました。

あと、 遅いマシンでもrtサーバを上げて手伝ってもらう時でも、 あまり足をひっぱらないよう、割り当てるピクセル数を動的に調整する仕組みを入れてみました。

計算が終わってサーバを落とすとき用に、skill.sh を追加しました。 リモートマシンにも手伝ってもらう場合は、 skill.sh の後半部分のコメントを外すなり、変更して使います。

pythonのロックのメソッドacquire(), release()がイマイチしっくりこないので、 ut.py にラッパを用意して lock(), unlock() にしてみました。

v36.patch

$ mv rt_v35 rt_v36
$ cat v36.patch | ( cd rt_v36 ; patch -p1 )
$ cd rt_v36
$ make clean
$ make

ではまず、速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v36/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 15.13s

$ ./skill.sh
  以前の
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

(1-15.13/16.16)*100 = 6.37 パーセントの改善

ではお試しデータで、ワイヤーフレームから

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=rxs_test name=out_v36/rxs_test_wf wf
  :
frm : 298/300(99.3%) : total 4m 30.31s : rest 1.80s : 2018/05/11 06:43:42
frm : 300/300(100.0%) : fin 4m 30.22s
-rw-r--r--  1 kondoh  staff  3588838  5 11 06:44 rxs_test_wf.mp4

3M超えてました。

$ ./img.py dmy out_v36/rxs_test_wf.mp4 cmd=3M
$ cp v.mp4 out_v36/rxs_test_wf_3m.mp4 

まぁ無事コピー展開できてる様子。 しかしワイヤーフレームでも10秒の動画生成に4分半、、、

荒削り設定で

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=rxs_test name=out_v36/rxs_test_es div=8 fps=10
  :
wh : 4800/4800(100.0%) : fin 1.89s
frm : 100/100(100.0%) : fin 4m 39.54s

(4*60+39.54)*8*8*3/60/60 = 14.90 時間の予想
$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=rxs_test name=out_v36/rxs_test
  :
wh : 276167/307200(89.9%) : total 2m 22.12s : rest 14.35s : 2018/05/11 21:53:32
wh : 307200/307200(100.0%) : fin 2m 7.84s
frm : 300/300(100.0%) : fin 14h 53m 45.76s

ls -l out_v36/*.mp4
-rw-r--r--  1 kondoh  staff  12170547  5 11 21:53 out_v36/rxs_test.mp4
-rw-r--r--  1 kondoh  staff   1763037  5 11 06:58 out_v36/rxs_test_es.mp4
-rw-r--r--  1 kondoh  staff   3588838  5 11 06:43 out_v36/rxs_test_wf.mp4

予想時間通りではありますが、いやいや 12Mバイト! 3Mバイトに切り捨てるだけじゃなくて、3Mごとに分割するツールを作らねば。 (パッチは次回に含めます)

$ ./img.py dmy out_v36/rxs_test.mp4 cmd=div3M
(n, fps)=(300, 30.0)
  :
./v.mp4 86/300
(n, fps)=(214, 30.0)
  :
./v.mp4 86/214
(n, fps)=(128, 30.0)
./v.mp4 86/128

$ ls -l out_v36/
total 61680
-rw-r--r--  1 kondoh  staff  12170547  5 11 22:08 rxs_test.mp4
-rw-r--r--  1 kondoh  staff   3145698  5 12 00:26 rxs_test_1.mp4
-rw-r--r--  1 kondoh  staff   3107714  5 12 00:27 rxs_test_2.mp4
-rw-r--r--  1 kondoh  staff   3113627  5 12 00:28 rxs_test_3.mp4
-rw-r--r--  1 kondoh  staff   2214859  5 12 00:28 rxs_test_4.mp4
  :

しかし、同じVGA fps=30の3Mバイトの動画でも

このワイヤーフレームの二値の線画で8秒

色数も多いこれが18秒

これが2秒

そういうものなのだろうか。なんかどこかで間違ってたりしないだろうか?


チェスボードの上に透明の球

繰り返しコピーができるようになったところで、 私が勝手に思い込んでいるレイトレーシングの伝統的なパターン 「チェスボードの上に透明の球」を再度試してみます。

v37.patch

$ mv rt_v36 rt_v37
$ cat v37.patch | ( cd rt_v37 ; patch -p1 )
$ cd rt_v37
$ make clean
$ make

物体の繰り返しコピー配置 では、 コピーを作る位置についてごたくを並べてたのですが、 バグがあり意図した動作になってませんでした。

物体データをコピーしても、データが指してるマッピング情報が、皆同じものを指していました。 コピーした最後のデータに行ったマッピングの処理が、 全部のコピーしたデータに影響してしまっておりました。

パッチの

dat.py
  :
 			d_ = d.copy()
 			d_['rxs'] = rxs[:] # !
 			d_[targ_k] = targ[:]
+			d_['maps'] = ut.map_lst( lambda m: m.copy(), d.get('maps') ) # !
 			ds.append(d_)
 			targ = lx + targ if i_a == 'insert' else targ + lx
 		return sum( map( d_setup, ds ), [] )
  :

この箇所で、マッピング情報もコピーするように修正しておきます。

今回のその他の変更

毎回、フルサイズで走らせたときの予想時間を手で計算してましたが、 荒削り設定のときに最後に表示するようにしてみました。

生成した動画ファイルが3Mバイトを超えてた場合、 適当に分割する処理を追加していみました。

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v37/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 15.49s

$ ./skill.sh
  以前の
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

まぁ同程度。

ではパッチに追加した「伝統パターン」をば。

  :
+rxs_check = [ {
+	'kind': 'square',
+	'rtd': { 'diff': 0.8 },
+	'maps': [ { 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(4) ] } ],
+	'm2g': [ ax.slide([-8,-8,-2]) ],
+	'rxs': [
+		( 'm2g', 'insert', 2, [ ax.slide([2,2,0]) ] ),
+		( 'm2g', 'insert', 4, [ ax.slide_x(4) ] ),
+		( 'm2g', 'insert', 4, [ ax.slide_y(4) ] ),
+	]
+},{
+	'kind': 'ball',
+	'rtd': rtd,
+	'm2g': [ ax.zoom_all(2) ],
+} ]
+
+rxs_check2 = [ {
+	'kind': 'square',
+	'rtd': { 'diff': 0.8 },
+	'maps': [ { 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(40) ] } ],
+	'l2m': [ ax.slide([-8,-8,-2]) ],
+	'rxs': [
+		( 'l2m', 'insert', 2, [ ax.slide([2,2,0]) ] ),
+		( 'l2m', 'insert', 4, [ ax.slide_x(4) ] ),
+		( 'l2m', 'insert', 4, [ ax.slide_y(4) ] ),
+	]
+},{
+	'kind': 'ball',
+	'rtd': rtd,
+	'm2g': [ ax.zoom_all(2) ],
+} ]

rxs_check はコピーする位置が画像を焼き付けた後、 rxs_check2 はコピーする位置が画像を焼き付ける前になってます。

前者のデータで、まずは荒削り設定で

$ ./cg.py eyep=[0,0,0],[40,40,10],10 sec=10 data_name=rxs_check name=out_v37/rxs_check_tst fps=10 div=4
  :
wh : 14959/19200(77.9%) : total 1.33s : rest 0.29s : 2018/05/12 17:29:50
wh : 19200/19200(100.0%) : fin 1.31s
frm : 100/100(100.0%) : fin 2m 20.23s
estimated 1.87 hour at 640*480 30fps

$ ls -lt out_v37/ | head
-rw-r--r--  1 kondoh  staff  887006  5 12 17:29 rxs_check_tst.mp4
  :

1.87時間の予想。いかに?

$ ./cg.py eyep=[0,0,0],[40,40,10],10 sec=10 data_name=rxs_check name=out_v37/rxs_check
  :
wh : 278581/307200(90.7%) : total 29.79s : rest 2.77s : 2018/05/12 20:32:56
wh : 307200/307200(100.0%) : fin 27.08s
frm : 300/300(100.0%) : fin 2h 33m 32.76s

$ ./skill.sh

$ ls -lt out_v37/ | head
-rw-r--r--  1 kondoh  staff  2994817  5 12 21:31 rxs_check_1.mp4
-rw-r--r--  1 kondoh  staff  3165126  5 12 20:32 rxs_check.mp4
  :

実際には2時間半でした。

生成した動画サイズが微妙に3Mを超えてたので、 自動的に3Mバイトに分割する処理が入りましたが、、、 展開してまとめなおしただけで、ファイルサイズが3M以内になったようです。

jpegで不可逆な圧縮をしてると、まぁそういう事もあるのかな。

続いて画像をマッピングする前にコピーする方式の場合。

$ ./cg.py eyep=[0,0,0],[40,40,10],10 sec=10 data_name=rxs_check2 name=out_v37/rxs_check2_tst fps=10 div=4
  :
wh : 15251/19200(79.4%) : total 1.31s : rest 0.27s : 2018/05/12 17:35:16
wh : 19200/19200(100.0%) : fin 1.32s
frm : 100/100(100.0%) : fin 2m 23.70s
estimated 1.92 hour at 640*480 30fps

$ ls -lt out_v37/ | head
-rw-r--r--  1 kondoh  staff  536321  5 12 17:35 rxs_check2_tst.mp4
  :

$ ./cg.py eyep=[0,0,0],[40,40,10],10 sec=10 data_name=rxs_check2 name=out_v37/rxs_check2
  :
wh : 278271/307200(90.6%) : total 28.55s : rest 2.68s : 2018/05/13 00:13:13
wh : 307200/307200(100.0%) : fin 25.93s
frm : 300/300(100.0%) : fin 2h 34m 43.47s

$ ./skill.sh

$ ls -lt out_v37/ | head
-rw-r--r--  1 kondoh  staff  1404207  5 13 00:13 rxs_check2.mp4
  :

こちらも予想時間を超えて2時間半程度でした。

ファイルサイズは1.5M弱。 こういう画像(?)だと、動画ファイルのサイズが小さくなるようです。

では、続きの10秒

$ ./cg.py eyep=[0,0,0],[40,40,10],10 sec=10 data_name=rxs_check2 name=out_v37/rxs_check2_2 init_sec=10
  :
wh : 270609/307200(88.1%) : total 17.28s : rest 2.05s : 2018/05/13 06:31:52
wh : 307200/307200(100.0%) : fin 15.30s
frm : 300/300(100.0%) : fin 2h 28m 55.50s

$ ./skill.sh

$ ls -l out_v37/ | head
-rw-r--r--  1 kondoh  staff  1182585  5 13 06:31 rxs_check2_2.mp4
  :

合わせて20秒に

$ rm -rf /tmp/img_wk/ttt
$ mkdir -p /tmp/img_wk/ttt
$ ./img.py /tmp/img_wk/ttt/a0 out_v37/rxs_check2.mp4 cmd=v2i
$ ./img.py /tmp/img_wk/ttt/a1 out_v37/rxs_check2_2.mp4 cmd=v2i
$ ./img.py /tmp/img_wk/ttt/a out_v37/rxs_check2_sum.mp4 cmd=i2v

$ ls -l out_v37/rxs_check2_sum.mp4 
-rw-r--r--  1 kondoh  staff  2348114  5 13 09:37 out_v37/rxs_check2_sum.mp4

20秒でも余裕でファイルサイズ3Mバイト以内でした。


1つのレイトレサーバに複数のスレッドで繋ぎにいってみる

複数のrtサーバプロセスを起動して試してましたが、 1つのrtサーバでもスレッドセーフな作りにしておけば、 複数のスレッドから繋げて使えるはず。

などと思って試してみると、色々とスレッドセーフになってなくて いっぱい修正が入ってしまいました。

とくに「値サーバ」。 例えば、複数のスレッドが値サーバに他のサーバの起動状況の情報を取りにいって、 起動してなかったらそのスレッドがサーバを起動しようとします。

ここで、複数のスレッドがほぼ同時に、あるサーバが起動してないと判定してしまって、 同じサーバをほぼ同時に複数起動してしまうという問題が起こってしまいました。

そこで「値サーバ」の中に、ちょっとした不可分なロックのしくみを追加して、 srvs.py の boot() からのサーバ起動確認処理で、使うようにしてみました。

v38.patch

$ mv rt_v37 rt_v38
$ cat v38.patch | ( cd rt_v38 ; patch -p1 )
$ cd rt_v38
$ make clean
$ make

では、いつもの速度確認。

まず以前の通りrtサーバは2つ起動して使う設定のままで。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v38/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 18.49s
  以前の
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

うーむ。

rt.py

#names = [ 'srv.rt' ] * 2
この行のコメントアウトを外して

names = [ 'srv.rt' ] * 2
にします。

これで、1つのrtサーバに2つのスレッドでから繋いでみます。

$ ./skill.sh 

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v38/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 15.44s

まぁあんまり変わらんですが、 この方式の方がちょっとだけの差で最速。

rt.py
例の箇所を

#names = [ 'srv.rt' ] * 2
names = [ 'srv.rt', 'srv.rt', 'srv.rt-15', 'srv.rt-15' ]

2つのrtサーバに、それぞれ2つのスレッドをつないで、 合計4つのスレッドで走らせてみます。

$ ./skill.sh

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v38/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 15.09s

これもまぁ誤差範囲かもですが、一応最速。

rt.py

#names = [ 'srv.rt' ] * 2
names = [ 'srv.rt' ] * 4 + [ 'srv.rt-15' ] * 4

8スレッドではいかに?

$ ./skill.sh

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v38/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 19.80s

まぁ2サーバ * 2スレッドで 4スレッドにしておきますかね。

rt.py

names = [ 'srv.rt' ] * 2 + [ 'srv.rt-15' ] * 2

今回のその他の変更

rtサーバの中にval.pyから取得したデータのキャッシュを持ったままになっていて、 違うデータの指定で cg.py を起動しても、前回取得したデータのまま処理が進む不具合がありました。

まぁデータを変更するたび ./skill.sh でサーバを落として何とかしてたわけですが、 さすがに面倒なので cg.py の起動時にrtサーバの中のデータのキャッシュをクリアできるようにしてみました。

では、伝統パターンで視点の動きをちょっと変えてみて試してみます。

$ ./cg.py eyep=[0,0,-20],[40,40,5],10 sec=20 data_name=rxs_check2 name=out_v38/rxs_check2_tst div=8 fps=15
  :
frm : 300/300(100.0%) : fin 3m 19.42s
estimated 7.09 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,-20],[40,40,5],10 sec=20 data_name=rxs_check2 name=out_v38/rxs_check2
  :
wh : 267968/307200(87.2%) : total 23.57s : rest 3.01s : 2018/05/17 06:11:42
wh : 307200/307200(100.0%) : fin 21.47s
frm : 600/600(100.0%) : fin 5h 32m 53.51s

$ ls -lt out_v38/ | head
-rw-r--r--  1 kondoh  staff  2836688  5 17 06:11 rxs_check2.mp4
  :

3MバイトOK


既存のデータを取り込んで配置

既存のデータを指定して、変換をかけたり画像のマッピングを追加したりして、 配置できるようにしてみました。

v39.patch

$ mv rt_v38 rt_v39
$ cat v39.patch | ( cd rt_v39 ; patch -p1 )
$ cd rt_v39
$ make clean
$ make

では、いつもの速度確認。

$ ./skill.sh

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v39/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 15.37s
  以前の
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

追加したデータを試してみます。

dat.py
  :
export_test = [ {
	'kind': 'export',
	'export': 'copen',
	'm2g': [ ax.zoom_all(0.5), ax.slide([-60,-60,0]) ],
	'rxs': [
		( 'm2g', 'append', 3, [ ax.slide_x(60) ] ),
		( 'm2g', 'append', 3, [ ax.slide_y(60) ] ),
	]
} ]
  :

以前のデータ'copen'とは

maps_copen = [ { 'fn': 'copen-090419.jpg', 't2m': [ ax.zoom_all(2) ] } ]

copen = [ {
	'kind': 'square',
	'l2m': [],
	'maps': maps_copen,
	'm2g': [ ax.zoom_all(50) ],
},{
	'kind': 'ball',
	'def_col': [128,0,0],
	'l2m': [ ax.zoom_all(20.0/50) ],
	'maps': maps_copen,
	'm2g': [ ax.zoom_all(50) ],
} ]

四角の平面と球で、copenの画像を貼り付けたデータです。

exportの指定では、 この'copen'データを新たなlocal座標系のデータとして扱います。

取り込んだ'copen'データ全体に対してさらに、 'm2g'の位置の変換で0.5倍に縮小して、(-60,-60,0)の平行移動。

'rxs'の指定でさらに'm2g'の変換位置の後ろで分身させてます。 まず3つ分身させてx方向に60つづ平行移動。 そのセットに対して、さらに3つ分身させてy方向に60づつ平行移動。

$ ./cg.py eyep=[0,0,0],[200,200,20],10 sec=10 data_name=export_test name=out_v39/exp_tst div=4 fps=10
  :
wh : 12411/19200(64.6%) : total 3.17s : rest 1.12s : 2018/05/18 21:50:18
wh : 19200/19200(100.0%) : fin 2.29s
frm : 100/100(100.0%) : fin 2m 29.03s
estimated 1.99 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[200,200,20],10 sec=10 data_name=export_test name=out_v39/exp
  :
wh : 289365/307200(94.2%) : total 37.80s : rest 2.19s : 2018/05/19 00:42:37
wh : 307200/307200(100.0%) : fin 35.65s
frm : 300/300(100.0%) : fin 2h 13m 31.11s

$ ls -lt out_v39/ | head
-rw-r--r--  1 kondoh  staff  1376197  5 19 00:42 exp.mp4
  :

画像のマッピングの追加も試しておきます。

dat.py
  :
export_test2 = [ {
	'kind': 'export',
	'export': 'copen',
	'm2g': [ ax.zoom_all(0.5), ax.slide([-60,-60,0]) ],
	'rxs': [
		( 'm2g', 'append', 3, [ ax.slide_x(60) ] ),
		( 'm2g', 'append', 3, [ ax.slide_y(60) ] ),
	],
	'maps': [ { 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(100) ] } ],
} ]
  :
$ ./cg.py eyep=[0,0,0],[200,200,20],10 sec=10 data_name=export_test2 name=out_v39/exp_tst2 div=4 fps=10
  :
wh : 19200/19200(100.0%) : fin 3.83s
frm : 100/100(100.0%) : fin 4m 51.80s
estimated 3.89 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[200,200,20],10 sec=10 data_name=export_test2 name=out_v39/exp2
  :
wh : 307200/307200(100.0%) : fin 1m 0.54s
frm : 300/300(100.0%) : fin 4h 13m 11.92s

$ ls -lt out_v39/ | head
-rw-r--r--  1 kondoh  staff  1531099  5 20 20:26 exp2.mp4
  :

dat.py
  :
export_test3 = [ {
	'kind': 'export',
	'export': 'copen',
	'l2m': [ ax.zoom_all(0.5), ax.slide([-60,-60,0]) ],
	'rxs': [
		( 'l2m', 'append', 3, [ ax.slide_x(60) ] ),
		( 'l2m', 'append', 3, [ ax.slide_y(60) ] ),
	],
	'maps': [ { 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(500) ] } ],
} ]

'rxs'による分身の位置を'l2m'の直後に移動して、 マッピングステージに上がる直前に変更してみます。

$ ./cg.py eyep=[0,0,0],[200,200,20],10 sec=10 data_name=export_test3 name=out_v39/exp_tst3 div=4 fps=10
  :
wh : 19200/19200(100.0%) : fin 3.71s
frm : 100/100(100.0%) : fin 4m 54.74s
estimated 3.93 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[200,200,20],10 sec=10 data_name=export_test3 name=out_v39/exp3
  :
wh : 307200/307200(100.0%) : fin 1m 3.22s
frm : 300/300(100.0%) : fin 4h 18m 37.56s

$ ls -lt out_v39/ | head
-rw-r--r--  1 kondoh  staff  1131620  5 20 15:00 exp3.mp4
  :


分身関連のバグ修正

物体の繰り返しコピー配置 を、 立方体で試してみて不具合が発覚しました。

立方体を複数にコピーして配置しようとしたのですが、 1つの四角い平面しか分身してくれません。

立方体は6つの四角い平面に展開されて処理されるのですが、 そこで分身した全てのデータが、コピー元の同じ'rxs'データを指していたのが原因でした。

これまで試した球や単独の四角い平面では、複数の物体に展開される事が無かったので、 不具合が潜んだままでした。

他にも例えば円柱の場合、側面の筒と上下のフタの円に分身する時に同じ問題が出ます。

深いコピー用の関数を用意して、まとめて対処しておきます。

v40.patch

$ mv rt_v39 rt_v40
$ cat v40.patch | ( cd rt_v40 ; patch -p1 )
$ cd rt_v40
$ make clean
$ make

最初のデータ展開箇所だけの問題で、速度には影響ないということで、 いつもの速度確認は省略。

とりあえず、展開で問題になった立方体を円形にコピー配置して確認しておきます。

$ ./cg.py eyep=[0,0,0],[200,200,40],10 sec=10 data_name=colosseum name=out_v40/col_t fps=2 div=4
  :
wh : 19200/19200(100.0%) : fin 6.32s
frm : 20/20(100.0%) : fin 2m 24.46s
estimated 9.63 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[200,200,40],10 sec=10 data_name=colosseum name=out_v40/col
  :
wh : 307200/307200(100.0%) : fin 1m 29.51s
frm : 300/300(100.0%) : fin 9h 10m 30.63s

$ ls -lt out_v40/ | head
-rw-r--r--  1 kondoh  staff  2283827  5 22 08:10 col.mp4
  :

$ ./cg.py eyep=[0,0,0],[300,300,60],10 sec=10 data_name=colosseum2 name=out_v40/col2_t fps=5 div=16

wh : 1200/1200(100.0%) : fin 2.41s
frm : 50/50(100.0%) : fin 2m 3.29s
estimated 52.61 hour at 640*480 30fps

予想時間は52時間!

$ ./cg.py eyep=[0,0,0],[300,300,60],10 sec=10 data_name=colosseum2 name=out_v40/col2_one n=1 init_sec=10 show_sec=-1
  :
wh : 288756/307200(94.0%) : total 8m 56.84s : rest 32.23s : 2018/05/22 09:38:24
wh : 307200/307200(100.0%) : fin 8m 24.65s
frm : 1/1(100.0%) : fin 9m 13.41s

$ ls -lt out_v40/ | head
-rw-r--r--  1 kondoh  staff    14244  5 22 09:38 col2_one.mp4
-rw-r--r--  1 kondoh  staff   100076  5 22 09:37 col2_one00001.jpg
  :

1コマ10分として 10*300/60 = 50時間

物体の数が多過ぎという事ですかね。div=2 で妥協して50/4=12.5時間で。

$ ./cg.py eyep=[0,0,0],[300,300,60],10 sec=10 data_name=colosseum2 name=out_v40/col2 div=2
  :
wh : 76800/76800(100.0%) : fin 2m 8.56s
frm : 300/300(100.0%) : fin 11h 37m 39.34s
estimated 46.51 hour at 640*480 30fps

$ ls -lt out_v40/ | head
-rw-r--r--  1 kondoh  staff  2506857  5 22 21:31 col2.mp4
  :


物体データをデータファイルに分離

データファイル dat.yaml を新規追加して、 dat.py に書いていた物体のデータを移しました。

デフォルトで dat.yaml をロードするようにしてますが、 cg.py起動時のコマンド引数で yaml=xxx.yaml 指定で変更出来ます。

v41.patch

$ mv rt_v40 rt_v41
$ cat v41.patch | ( cd rt_v41 ; patch -p1 )
$ cd rt_v41
$ make clean
$ make

追加したデータファイル dat2.yaml を試してみます。

pillar:
- kind: cube
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ ax.zoom_z(10) ]
- kind: pipe
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ 'ax.zoom([2,2,1])', ax.slide_z(-11) ]
  rxs:
  - [ m2g, append, 2, [ ax.slide_z(22) ] ]

big_ball:
- kind: ball
  rtd: { diff: 0.3 }
  maps:
  - fn: IMG_3999_4.mov
    fn_r: IMG_3999_4.mov
    t2m: [ ax.zoom_all(2), ax.rot_x(90) ]
  - fn: IMG_3999_4.mov
    fn_r: IMG_3999_4.mov
    t2m: [ ax.zoom_all(2), ax.rot_x(90), ax.rot_z(180) ]
  m2g: [ ax.zoom_all(500) ]

colosseum:
- kind: export
  export: pillar
  m2g: [ ax.slide_x(20) ]
  rxs:
  - [ m2g, append, 16, [ ax.rot_z(360.0/16) ] ]
- kind: pipe
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ 'ax.zoom([24,24,1])', ax.slide_z(-13) ]
  rxs:
  - [ m2g, append, 2, [ ax.slide_z(26) ] ]
- kind: ball
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ ax.zoom_all(6) ]
- kind: export
  export: big_ball

# EOF

ワイヤーフレームも意外と時間もかかってファイルも大きくなるので、 フレームレート粗めで。

./cg.py eyep=[0,0,0],[300,300,100],10 sec=10 yaml=dat2.yaml data_name=colosseum name=out_v41/col_wf wf fps=5
  :
frm : 50/50(100.0%) : fin 2m 58.58s
estimated 0.30 hour at 640*480 30fps

粗削りお試し。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=10 yaml=dat2.yaml data_name=colosseum name=out_v41/col_tst div=4 fps=1
  :
wh : 19200/19200(100.0%) : fin 9.63s
frm : 10/10(100.0%) : fin 1m 44.46s
estimated 13.93 hour at 640*480 30fps

適当に5秒の時刻の1コマを。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=10 yaml=dat2.yaml data_name=colosseum name=out_v41/col_ init_sec=5 n=1 show_sec=-1
  :
wh : 307200/307200(100.0%) : fin 1m 42.06s
frm : 1/1(100.0%) : fin 2m 14.52s

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=10 yaml=dat2.yaml data_name=colosseum name=out_v41/col
  :
wh : 307200/307200(100.0%) : fin 2m 5.81s
frm : 300/300(100.0%) : fin 14h 8m 10.53s

$ ls -lt out_v41/ | head
-rw-r--r--  1 kondoh  staff  3087005  5 25 14:27 col.mp4
  :

ギリギリ3Mバイト

ほぼ粗削りのときの予想時間通りで14時間でした。

ここでちょと番外編のデータを。

$ wget http://kondoh2.html.xdomain.jp/rt/dat3.yaml
$ wget http://kondoh2.html.xdomain.jp/rt/noshi.jpg

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=dat3.yaml data_name=colosseum name=out_v42/noshi
  :
frm : 600/600(100.0%) : fin 28h 52m 21.35s
Traceback (most recent call last):
  File "./cg.py", line 68, in 
    img.video_to_d1v3M(fn)
  :

しまった3M分割の箇所でtypoが... 次回のパッチで修正しておきます。

$ ls -lt out_v41/
-rw-r--r--@ 1 kondoh  staff  9224643  5 29 08:04 noshi.mp4
  :

9Mバイト

$ ./img.py dmy out_v41/noshi.mp4 cmd=div3M
  :
で3Mバイトに分割


画像サーバからのデータをクライアント側でキャッシュ

色々とデータを試してみると、画像のマッピングが無しだと何だかすごく処理速度が速い感じ。

これは画像サーバとのやりとりが、かなり重いのでは?

という事で、レイトレサーバから画像サーバに要求を出して動画のピクセルを取得する箇所で、 クライアントであるレイトレサーバの中で、一コマ分の画像全体をまとめて取得しキャッシュするようにしてみました。 メモリ使いまくりです。

これで最初のピクセルの取得時に、画像全体のデータをまとめて取得しますが、 以降は次のコマに時間が進むまでは、画像サーバへのアクセスは発生しません。

画像サーバには、動画のフレーム数やフレームレートを取得するAPIと、 ある時刻の一コマ分の画像全体を取得するAPIを追加しました。 クライアント側はC言語の実装の方だけ、キャッシュの機構を追加しています。

マッピングしても、画像全体のピクセルを使う訳じゃないので、 初回の取得時間が無駄になる場合があるかも知れませんが、 毎回プロセス間通信する方が、やっぱり負担が大きいでしょう。

v42.patch

$ mv rt_v41 rt_v42
$ cat v42.patch | ( cd rt_v42 ; patch -p1 )
$ cd rt_v42
$ make clean
$ make

では、さっそくいつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v42/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 3.16s
  以前の
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

!!! まじでか。こんなに効果ありなん?

前回のデータで、ちょっと上下の移動を増やしてみて、分割はdiv=2程度で試してみます。

$ ./cg.py eyep=[0,0,0],[300,300,200],10 sec=10 yaml=dat2.yaml data_name=colosseum name=out_v42/col2 div=2
  :
wh : 72895/76800(94.9%) : total 7.92s : rest 0.40s : 2018/05/29 22:36:59
wh : 76800/76800(100.0%) : fin 7.53s
frm : 300/300(100.0%) : fin 46m 39.03s
estimated 3.11 hour at 640*480 30fps

$ ls -lt out_v42/ | head
-rw-r--r--  1 kondoh  staff  1868348  5 29 22:37 col2.mp4
  :

やはり速い。予想時間3.11時間になってます。 v41では同じdat2.yamlで14時間かかってました。

では、せっかくなので20秒で。

$ ./cg.py eyep=[0,0,0],[300,300,200],10 sec=20 yaml=dat2.yaml data_name=colosseum name=out_v42/col
  :


画像データキャッシュのバグ修正

バグってました。

ローカル変数のアドレスをハッシュテーブルのキーとして使ってました。 よって、関数を抜けるとキーの値は不定。

テーブルに登録したはずのキーが見つからず、何度も画像をサーバから引っ張ってきて登録しなおした末に、 メモリを使いきってしまい、swap領域が不足だの、ディスクが不足だのとエラーが出て落ちてました。

修正します。

また、rt.cのサーバプロセスにはcg.pyからの複数のスレッドが接続してくるので、 rt.cから呼び出すimg.c img_col_cache()の中で、 キャッシュの操作箇所にロックをかけてスレッドセーフにしてみました。

ut.[ch] の ut_cache_tbl のあたり、少々複雑になってきたのでメモを残しておきます。

struct ut_cache_tbl{
	int n;
	/* tbl[] の要素数を指定する */

	int chain_max_n; /* <= 0 : no limit */
	/*
	tbl[i] --> の先にチェーンで連結する ut_cache の個数の上限を指定する
	値が0以下なら制限なし
	*/

	struct ut_cache **tbl;
	/* tbl[] 本体 */

	int (*geti)(void *k, int ksz);
	/*
	k, ksz からテーブルの添字の整数、
	つまり tbl[i] の i の元になる整数を返す関数を指定する
	関数が返した整数をさらに % n で n未満の値にして i として使う
	*/
};

/*
ソースコード上と順を入れ換えて
tbl[i] --> の先にチェーンで連結する ut_cache
*/
struct ut_cache{
	void *k;
	/* キーのデータ */

	int ksz;
	/* キーのデータのサイズ */

	void *v;
	/* 値のデータ */

	struct ut_cache *next;
	/* チェーンの次の ut_cache (NULLで終端) */
};

void ut_cache_alloc(struct ut_cache_tbl *t);
/*
	t->n を設定した状態で呼び出す
	t->tbl[] をアロケート
	t->tbl[i] の指してる先は、NULL(終端)で初期化
*/

struct ut_cache **ut_cache_get_pptr(struct ut_cache_tbl *t, void *k, int ksz);
/*
	主に内部で使用するので略
*/

struct ut_cache *ut_cache_get_ptr(struct ut_cache_tbl *t, void *k, int ksz);
/*
	主に内部で使用するので略
*/

void *ut_cache_get(struct ut_cache_tbl *t, void *k, int ksz);
/*
	キー情報 k, ksz から t->tbl[] の添字を求めて
	チェーンをたぐって一致するキー情報がある ut_cache を探す
	見つかれば ut_cache の値のデータ v を返す
	見つからなければ NULL を返す
*/

void *ut_cache_set(struct ut_cache_tbl *t, void *k, int ksz, void *v);
/*
	ut_cache をアロケートして値 v をセット
	キー情報 k, ksz から t->tbl[] の添字を求めて、
	チェーンに追加する

	チェーンの長さが t->chain_max_n の上限にひっかかると、
	末尾のut_cacheを削除する

	削除したut_cacheがあれば、その値のデータ v を返す
	削除したut_cacheがなければ、NULLを返す
*/

void *ut_cache_del(struct ut_cache_tbl *t, void *k, int ksz);
/*
	キー情報 k, ksz から t->tbl[] の添字を求めて
	チェーンをたぐって一致するキー情報がある ut_cache を探す

	見つかれば ut_cache をチェーンから外し、
	その ut_cache のメモリを解放し、
	設定されていた値のデータ v を返す

	見つからなければ NULL を返す
*/

void ut_cache_free(struct ut_cache_tbl *t);
/*
	t->tbl[] の全てのチェーンについて、
	ut_cache のメモリを解放し、
	ut_cache_alloc() でアロケートした t->tbl[] のメモリも解放する

	解放する各 ut_cache の値のデータ v は解放もせず、
	値も返さないので注意
*/

int ut_cache_geti_int(void *k, int ksz);
/*
	キーを整数として使うときの geti 関数
	ut_cache_tbl t の t->geti に指定して使う
*/

int ut_cache_geti_str(void *k, int ksz);
/*
	キーを文字列として使うときの geti 関数
	ut_cache_tbl t の t->geti に指定して使う
*/

v43.patch

$ mv rt_v42 rt_v43
$ cat v43.patch | ( cd rt_v43 ; patch -p1 )
$ cd rt_v43
$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v43/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 2.96s
  以前の
  v42  wh : 76800/76800(100.0%) : fin 3.16s
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

念のため粗削り設定で

$ ./cg.py eyep=[0,0,0],[300,300,200],10 sec=20 yaml=dat2.yaml data_name=colosseum name=out_v43/col4 div=4
  :
wh : 19200/19200(100.0%) : fin 1.05s
frm : 600/600(100.0%) : fin 21m 35.86s
  :
estimated 5.76 hour at 640*480 30fps

$ ls -lt out_v43/ | head
-rw-r--r--  1 kondoh  staff  3141783  5 30 22:03 col4_1.mp4
-rw-r--r--  1 kondoh  staff    27952  5 30 22:03 col4_2.mp4
-rw-r--r--  1 kondoh  staff  3417683  5 30 22:00 col4.mp4
  :

予想時間は6時間弱。

では、リベンジの20秒データで。

$ ./cg.py eyep=[0,0,0],[300,300,200],10 sec=20 yaml=dat2.yaml data_name=colosseum name=out_v43/col
  :
wh : 307200/307200(100.0%) : fin 19.01s
frm : 600/600(100.0%) : fin 6h 4m 18.08s
(n, fps)=(600, 30.0)
  :
./v.mp4 382/600

$ ls -lt out_v43/ | head
-rw-r--r--  1 kondoh  staff  1607345  5 31 04:14 col_2.mp4
-rw-r--r--  1 kondoh  staff  3142862  5 31 04:14 col_1.mp4
-rw-r--r--  1 kondoh  staff  5193725  5 31 04:12 col.mp4
  :

ほぼ6時間でした。


物体データの展開処理順の修正

先のデータの一部を拡大して配置して、再帰風にして試してみたところ...

$ cat dat4.yaml
pillar:
- kind: cube
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ ax.zoom_z(10) ]
- kind: pipe
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ 'ax.zoom([2,2,1])', ax.slide_z(-11) ]
  rxs:
  - [ m2g, append, 2, [ ax.slide_z(22) ] ]

big_ball:
- kind: ball
  rtd: { diff: 0.3 }
  maps:
  - fn: IMG_3999_4.mov
    fn_r: IMG_3999_4.mov
    t2m: [ ax.zoom_all(2), ax.rot_x(90) ]
  - fn: IMG_3999_4.mov
    fn_r: IMG_3999_4.mov
    t2m: [ ax.zoom_all(2), ax.rot_x(90), ax.rot_z(180) ]
  m2g: [ ax.zoom_all(1000) ]

colosseum:
- kind: export
  export: pillar
  m2g: [ ax.slide_x(20) ]
  rxs:
  - [ m2g, append, 16, [ ax.rot_z(360.0/16) ] ]
- kind: pipe
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ 'ax.zoom([24,24,1])', ax.slide_z(-13) ]
  rxs:
  - [ m2g, append, 2, [ ax.slide_z(26) ] ]

all:
- kind: export
  export: colosseum
  rxs:
  - [ m2g, append, 2, [ ax.zoom(6) ] ]
- kind: ball
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ ax.zoom_all(6) ]
- kind: export
  export: big_ball

# EOF
$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=dat4.yaml data_name=all name=out_v44/wf_ wf
  :
frm : 599/600(99.8%) : total 57m 15.79s : rest 5.72s : 2018/06/01 17:11:39
frm : 600/600(100.0%) : fin 57m 15.88s
(n, fps)=(600, 30.0)

$ ls -lt out_v44/ | head
-rw-r--r--  1 kondoh  staff  3016737  6  1 17:14 wf__3.mp4
-rw-r--r--  1 kondoh  staff  3144875  6  1 17:14 wf__2.mp4
-rw-r--r--  1 kondoh  staff  3140882  6  1 17:12 wf__1.mp4
-rw-r--r--  1 kondoh  staff  9270739  6  1 17:11 wf_.mp4
  :

ん?

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=dat4.yaml data_name=all name=out_v44/all
  :
wh : 307200/307200(100.0%) : fin 1m 20.94s
frm : 600/600(100.0%) : fin 27h 47m 53.95s
(n, fps)=(600, 30.0)
  :

$ ls -lt out_v44/ | head
-rw-r--r--  1 kondoh  staff  2207354  6  1 13:29 all_2.mp4
-rw-r--r--  1 kondoh  staff  3145411  6  1 13:29 all_1.mp4
-rw-r--r--  1 kondoh  staff  5764500  6  1 13:27 all.mp4
  :

28時間ほどもかかったけど、やっぱり「ん?」

拡大した方の外側の「柱」の上下の円柱部分。 天井と床部の円柱部分の配置が意図した通りになってません。
dat.py
  :
def d_setup(d):
  :
	if 'rxs' in d:
		rxs = d.get('rxs')
		if not rxs:
			d.pop('rxs')
			return d_setup(d)
		(targ_k, i_a, n, lx) = rxs.pop(0)

		targ = d.get(targ_k, [])
		ds = []
		for i in range(n):
			d_ = d_copy(d)
			d_[targ_k] = targ[:]
			ds.append(d_)
			targ = lx + targ if i_a == 'insert' else targ + lx
		return sum( map( d_setup, ds ), [] )
  :
	if kind == 'export' and 'export' in d:
		data = ydic.get( d.get('export'), [] )
		data = ut.map_lst( d_copy, data )
		ds = []
		for d_ in data:
			d_['m2g'] = d_.get('m2g', []) + d.get('l2g')
			ds += d_setup(d_)
		for d_ in ds:
			d_['maps'] = d_.get('maps', []) + d.get('maps', [])
		return ds
  :

この処理の順番に問題が。

dat4.yaml
  :
all:
- kind: export
  export: colosseum
  rxs:
  - [ m2g, append, 2, [ ax.zoom(6) ] ]
  :

例えばここの展開で、 先にrxsの拡大の結果がm2gの末尾に追加されて、 そのあとでexportの処理で、

colosseum:
  :
- kind: pipe
  rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  m2g: [ 'ax.zoom([24,24,1])', ax.slide_z(-13) ]
  rxs:
  - [ m2g, append, 2, [ ax.slide_z(26) ] ]
  :

こちらの'm2g'。このzoomとslide_z。ここまではオッケー。export の

			d_['m2g'] = d_.get('m2g', []) + d.get('l2g')

allのrxsのm2gに追加された拡大は、d.get('l2g') に反映されているので、 zoomとslide_zのm2gよりも後方に追加されます。

問題はそのあと
			d_['m2g'] = d_.get('m2g', []) + d.get('l2g')
			ds += d_setup(d_)

このd_setup(d)から

  rxs:
  - [ m2g, append, 2, [ ax.slide_z(26) ] ]

が展開されて、m2gのさらに後ろ側に、このslize_z()が追加されます。

なんともややこしい...

そもそも、exportの展開を先に処理して、export元のrxs展開込みで済ませた状態で、 外側のrxsのコピー展開を処理するのが自然かと。

ということで、exportとrxsの処理順を入れ替えてみました。

そのままではダメ。一部rxs展開がうまく成されません。抜けがありました。 なかなか手強い。

以前の処理順では、exportの展開時にはrxsは展開済みの前提で、 データ中にrxsが現れる事はありませんでした。

処理順を変えたので、前提が崩れます。 exportの展開時でもrxsがあれば展開先に追加するように修正します。

v44.patch

$ mv rt_v43 rt_v44
$ cat v44.patch | ( cd rt_v44 ; patch -p1 )
$ cd rt_v44
$ make clean
$ make

視点移動はちょっと引き気味にしてみます。

$ ./cg.py eyep=[0,0,0],[450,450,150],10 sec=20 yaml=dat4.yaml data_name=all name=out_v44/wf_tst wf fps=2
  :
frm : 39/40(97.5%) : total 3m 51.44s : rest 5.78s : 2018/06/03 05:26:49
frm : 40/40(100.0%) : fin 3m 51.59s
estimated 0.96 hour at 640*480 30fps

円柱部分の配置OK

$ ./cg.py eyep=[0,0,0],[450,450,150],10 sec=20 yaml=dat4.yaml data_name=all name=out_v44/rough fps=4 div=8
  :
frm : 79/80(98.8%) : total 2m 22.23s : rest 1.77s : 2018/06/03 05:31:27
wh : 0/4800(0.0%) : not start yet
wh : 4800/4800(100.0%) : fin 0.98s
frm : 80/80(100.0%) : fin 2m 21.78s
estimated 18.91 hour at 640*480 30fps

それでは19時間で。

$ ./cg.py eyep=[0,0,0],[450,450,150],10 sec=20 yaml=dat4.yaml data_name=all name=out_v44/fix
  :
wh : 307200/307200(100.0%) : fin 1m 4.04s
frm : 600/600(100.0%) : fin 18h 52m 12.35s
(n, fps)=(600, 30.0)
  :
./v.mp4 315/600

$ ls -lt out_v44/ | head
-rw-r--r--  1 kondoh  staff  2762196  6  4 00:31 fix_2.mp4
-rw-r--r--  1 kondoh  staff  3141307  6  4 00:31 fix_1.mp4
-rw-r--r--  1 kondoh  staff  6391237  6  4 00:29 fix.mp4
  :

ほぼ19時間でした。

手前の大きな角柱の横切りが、あまりにチラチラするのでもう一丁。 dat5.yaml で試してみます。

$ diff -u dat4.yaml dat5.yaml
--- dat4.yaml	Sun Jun  3 04:12:50 2018
+++ dat5.yaml	Mon Jun  4 04:58:05 2018
@@ -36,7 +36,7 @@
 - kind: export
   export: colosseum
   rxs:
-  - [ m2g, append, 2, [ ax.zoom(6) ] ]
+  - [ m2g, append, 2, [ ax.zoom(16) ] ]
 - kind: ball
   rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
   m2g: [ ax.zoom_all(6) ]
$ 

外側の柱の拡大率をアップして、視点移動の引きも元に戻してみます。

$ wget http://kondoh2.html.xdomain.jp/rt/dat5.yaml

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=dat5.yaml data_name=all name=out_v44/dat5
  :
wh : 290062/307200(94.4%) : total 1m 18.96s : rest 4.40s : 2018/06/05 02:43:04
wh : 307200/307200(100.0%) : fin 1m 14.59s
frm : 600/600(100.0%) : fin 21h 35m 6.34s

$ ls -lt out_v44/ | head
-rw-r--r--  1 kondoh  staff  2559915  6  5 02:45 dat5_2.mp4
-rw-r--r--  1 kondoh  staff  3132526  6  5 02:45 dat5_1.mp4
-rw-r--r--  1 kondoh  staff  6155197  6  5 02:43 dat5.mp4
  :

21時間半。 視点の開始位置と終了位置がちょうど、柱に埋まった位置になってました。


物体を動かす

そろそろ物体を動かしてみます。

一旦効率は無視。 時刻とともに ax.xxx() のアフィン変換のパラメータを変化出来るようにして、 時刻の更新ごとにデータをyamlファイルのロードからやりなおします。

元々yamlファイル中のax.xxx()箇所は全て文字列として扱っていて、 ロード後にeval()でアフィン変換のオブジェクトを生成してます。

ソースコード中のeval()の箇所で変数secの時刻を用意しておけば、 文字列中でsecを参照する式が利用できるはずです。 (ああ、インタープリタは便利)

さすがに、以前の静止したyamlファイルのデータを使うときに、 フレーム毎に同じデータをロードしなおすのも何なので、 安易にyamlファイル中に単語 sec が含まれているかどうかで判定するようにしておきます。

v45.patch

$ mv rt_v44 rt_v45
$ cat v45.patch | ( cd rt_v45 ; patch -p1 )
$ cd rt_v45
$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v45/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 3.00s
  以前の
  v43  wh : 76800/76800(100.0%) : fin 2.96s
  v42  wh : 76800/76800(100.0%) : fin 3.16s
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

追加したdat6.yaml

$ diff -u dat5.yaml dat6.yaml
--- dat5.yaml	2018-06-04 04:58:05.000000000 +0900
+++ dat6.yaml	2018-06-04 23:11:09.000000000 +0900
@@ -1,7 +1,7 @@
 pillar:
 - kind: cube
   rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
-  m2g: [ ax.zoom_z(10) ]
+  m2g: [ 'ax.zoom_z( way.get([(10,2),(1,2)], sec, True) )' ]
 - kind: pipe
   rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
   m2g: [ 'ax.zoom([2,2,1])', ax.slide_z(-11) ]
@@ -28,7 +28,7 @@
   - [ m2g, append, 16, [ ax.rot_z(360.0/16) ] ]
 - kind: pipe
   rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
-  m2g: [ 'ax.zoom([24,24,1])', ax.slide_z(-13) ]
+  m2g: [ 'ax.zoom([24, way.get([(24,4),(1,4)], sec, True), 1])', ax.slide_z(-13) ]
   rxs:
   - [ m2g, append, 2, [ ax.slide_z(26) ] ]
 
@@ -39,6 +39,7 @@
   - [ m2g, append, 2, [ ax.zoom(16) ] ]
 - kind: ball
   rtd: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
+  l2m: [ 'ax.slide( way.liss([-3,-3,-3],[3,3,3],[3,4,5])(sec) )' ]
   m2g: [ ax.zoom_all(6) ]
 - kind: export
   export: big_ball

角柱の高さを2秒間隔で伸び縮み、 天井と床の円柱を4秒間隔でひしゃげさせて、 中央の球(6倍に拡大する前の半径1の段階)を(-3,-3,-3)から(3,3,3)の範囲を、 (3,4,5)秒の周期で適当にリサージュ図形の軌跡で移動させてみます。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=dat6.yaml data_name=all name=out_v45/dat6_tst div=8 fps=8
  :
wh : 4800/4800(100.0%) : fin 1.11s
frm : 160/160(100.0%) : fin 4m 57.65s
estimated 19.84 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=dat6.yaml data_name=all name=out_v45/dat6
  :
wh : 291116/307200(94.8%) : total 1m 20.21s : rest 4.19s : 2018/06/05 20:15:31
wh : 307200/307200(100.0%) : fin 1m 16.05s
frm : 600/600(100.0%) : fin 14h 7m 18.73s

$ ls -lt out_v45/ | head
-rw-r--r--  1 kondoh  staff  3047631  6  5 20:17 dat6_2.mp4
-rw-r--r--  1 kondoh  staff  3144862  6  5 20:17 dat6_1.mp4
-rw-r--r--  1 kondoh  staff  6587792  6  5 20:15 dat6.mp4

14時間ほどでした。 確かに、ねらい通り動いてます。


時間をズラして分身

ワイヤーフレームのプログラム 円を追加

このスピンするコインのような動きを再現してみます。

wf_15.patch あたりのパッチの wf_ex.c の箇所

diff -urN wf-/wf_ex.c wf/wf_ex.c
--- wf-/wf_ex.c	2015-12-24 00:00:00.000000000 +0900
+++ wf/wf_ex.c	2015-12-30 00:00:00.000000000 +0900
@@ -64,6 +64,9 @@
  :
@@ -301,9 +305,9 @@
 		    { type_slide_way, &(struct way){ .n=2, .ps=(pos_t[]){{0,-20,0},{0,20,0}}, .ts=(double[]){4.5,4.5}, .vs=NULL, .dg=3} },
 		    { type_slide_way, &(struct way){ .n=2, .ps=(pos_t[]){{-20,0,0},{20,0,0}}, .ts=(double[]){3,3}, .vs=NULL, .dg=3} },
 		    { type_rot_way, &(struct rot_way){
-		      .l={D3_O,D3_I}, .deg_way={ .n=2, .vs=(double[]){0,360*2}, .ts=(double[]){1,1}, .ps=NULL, .dg=3} } },
+		      .l={D3_O,D3_I}, .deg_way=WAY_V2(0,360*2, 1,1, 3) } },
 		    { type_end } }},
-		  .data = &(data_t){ type_cube }
+		  .data = &(data_t){ type_circle, &(struct circle){ .r=1,.n=10*4 } }
 		}};
 
 		data.type = type_arr;

確か下から上へと読むはずでした。

wf_13.patch あたりのパッチの wf_ex.c の箇所

diff -urN wf-/wf_ex.c wf/wf_ex.c
--- wf-/wf_ex.c	2015-12-22 00:00:00.000000000 +0900
+++ wf/wf_ex.c	2015-12-23 00:00:00.000000000 +0900
  :
+		data_t *lissajours = &(data_t){ type_op_data_set, &(struct op_data_set){
+		  .op = &(data_t){ type_arr, (data_t[]){
+		    { type_copy_timeshift, &(struct copy_timeshift){
+		      .n=10, .init_sec=0, .step_sec=-0.3, .init=D3_O, .step=D3_O } },
  :

という事で、 時間をズラしながらコピーして分身させる機能を追加してみました。

yamlファイルをロードして、 文字列をeval()してしまうと時刻のパラメータは消えてしまうので、 その前に付け焼き刃的にゴニョゴニョしてます。

あと、 yamlファイル中に何度も同じ長い記述が現れるのもなんなので、 defsのキーの箇所で定義して展開できるように、 ut.py に defs_eval() を追加してみました。

v46.patch

$ mv rt_v45 rt_v46
$ cat v46.patch | ( cd rt_v46 ; patch -p1 )
$ cd rt_v46
$ make clean
$ make

データロード時の処理が増えてますが、 いつもの速度確認のデータは、 そもそも時刻依存なしなので省略。

視線の先に何か目標物が欲しかったので、 球を平たくつぶした円盤を配置してみました。 (UFOと子機のよう)

$ ./cg.py eyep=[0,0,0],[200,200,100],10 sec=20 yaml=spin_coin.yaml data_name=all name=out_v46/coin_wf wf
  :
frm : 599/600(99.8%) : total 9m 41.48s : rest 0.96s : 2018/06/05 22:56:41
frm : 600/600(100.0%) : fin 9m 41.57s

$ ls -lt out_v46/ | head
-rw-r--r--  1 kondoh  staff  1616074  6  5 22:56 coin_wf.mp4
  :

$ ./cg.py eyep=[0,0,0],[200,200,100],10 sec=20 yaml=spin_coin.yaml data_name=all name=out_v46/coin_tst div=8 fps=8
  :
wh : 4800/4800(100.0%) : fin 0.06s
frm : 160/160(100.0%) : fin 37.33s
estimated 2.49 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[200,200,100],10 sec=20 yaml=spin_coin.yaml data_name=all name=out_v46/coin
  :
wh : 307200/307200(100.0%) : fin 9.03s
frm : 600/600(100.0%) : fin 1h 50m 12.39s

$ ls -lt out_v46/ | head
-rw-r--r--  1 kondoh  staff   581794  6  6 05:26 coin_2.mp4
-rw-r--r--  1 kondoh  staff  3142385  6  6 05:26 coin_1.mp4
-rw-r--r--  1 kondoh  staff  3964603  6  6 05:24 coin.mp4
  :

子機のコインは意図した通りに動いてるようですが、、、 どうも、添え物的に配置してみた母艦のUFOに目がいってしまいますね。


物体のAND演算

球や立方体などの単純な物体ですが、 アフィン変換で並行移動、拡大縮小、回転などできます。

でも、もうちょっと複雑な図形を表示してみたいところ、、、

複数の物体の領域のAND、つまり論理積というか「かつ」な領域を表示してみます。

視線の直線と物体の表面との交点を求めるのが、レイトレーシングの基本ですが、 そこからもうちょっと頑張って、視線の直線を物体にぶすりと串刺しにしてみます。

おのおのの単純な物体に、視線という名の串を突き刺して、串に物体の内部の範囲の印をつけていきます。

範囲の印のついた串を束ねてANDをとって、全ての串に存在してる領域を割り出します。 視線の裏側の部分は除外して、一番手前にぶちあたる領域の開始位置、あるいは終了位置から交点を算出します。

(夜中に目をこすりながらデバッグして文章を書いてるので、 コードも文章もかなりとっちらかってしまいました...)

なんとかANDが出来たので、NOTも少し手を出してみましたが、 平面に展開される系の物体は未だ完成できず...

とりあえず球のNOTだけは確認できました。

v47.patch

$ mv rt_v46 rt_v47
$ cat v47.patch | ( cd rt_v47 ; patch -p1 )
$ cd rt_v47
$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v47/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 3.04s
  以前の
  v45  wh : 76800/76800(100.0%) : fin 3.00s
  v43  wh : 76800/76800(100.0%) : fin 2.96s
  v42  wh : 76800/76800(100.0%) : fin 3.16s
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

まぁ、AND指定のデータじゃないのでさほど変わらず。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=and.yaml data_name=all name=out_v47/and_wf div=8 fps=8 wf
  :
frm : 160/160(100.0%) : fin 32.02s
estimated 2.13 hour at 640*480 30fps

ワイヤーフレームはAND未対応です。 単に球と立方体が並んでるだけです。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=and.yaml data_name=all name=out_v47/and_tst div=8 fps=8
  :
wh : 4800/4800(100.0%) : fin 0.10s
frm : 160/160(100.0%) : fin 33.08s
estimated 2.21 hour at 640*480 30fps

そう、これ。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=and.yaml data_name=all name=out_v47/and
  :

残念。メモリリークで落ちてました。 デバッグ前に、ディスクに残っている画像をできるだけ救済しておきます。

$ ./img.py out_v47/and out_v47/and_ng


球と立方体のAND,OR,XOR演算

まず前回のメモリリークの原因から。

--- cross.c-	2018-06-08 23:53:48.000000000 +0900
+++ cross.c	2018-06-08 23:54:19.000000000 +0900
@@ -456,6 +456,7 @@
 		n = cross_cross_one( d->kind, &d->l2g, prev_idx == j, l_g, &ret_ );
 
 		if( n == 0 && ( is_pl || !d->not ) ){
+			my_area_free(&area, NULL);
 			return 0;
 		}
 		ret_.idx = j;

ここでした。 見事にfree抜けてました。

v47で、この修正だけ入れてみて無事20秒の動画を生成できました。

$ ./skill.sh

$ make

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=and.yaml data_name=all name=out_v47/and
  :
wh : 293787/307200(95.6%) : total 9.49s : rest 0.41s : 2018/06/08 11:31:06
wh : 307200/307200(100.0%) : fin 9.10s
frm : 600/600(100.0%) : fin 1h 51m 59.51s

$ ls -lt out_v47/ | head
-rw-r--r--  1 kondoh  staff  1416918  6  8 11:33 and_2.mp4
-rw-r--r--  1 kondoh  staff  3139752  6  8 11:33 and_1.mp4
-rw-r--r--  1 kondoh  staff  4862955  6  8 11:31 and.mp4
  :

では本題。

v47のコードを整理しつつ、 物体のAND演算の箇所でOR演算でもXOR演算でも指定できるように作り変えてみました。

物体といっても対応してるのは未だ「球」と「立方体」だけです。

物体のOR演算は、素の状態で普通にORになってるはずです。 ですが、あえてOR演算として指定すると、OR演算した結果のNOTが取れたりできるので、 複雑な演算の指定をするときは便利かと。

(OR結果のNOTなら、ドモルガンの定理で展開して 個別にNOTしてANDすれば良いのでは? などと突っ込んではいけません)

視線で串刺しにして領域の演算をするときに使う area.[ch]は、 できるだけシンプルにまとめなおしてみました。

それを使う側の cross.c 。 「とっちらかり」を押さえ込んでシンプルに。 まとめなおしてふと気づくと、 先のv47のメモリリークの対応箇所は、原因の箇所自体が消えてしまいました。

サンプルデータは op.yaml を追加してます。

前回同様ワイヤーフレーム側の処理は、物体の演算には対応してません。 というか、しません。

v48.patch

$ mv rt_v47 rt_v48
$ cat v48.patch | ( cd rt_v48 ; patch -p1 )
$ cd rt_v48
$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v48/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 3.11s
  以前の
  v47  wh : 76800/76800(100.0%) : fin 3.04s
  v45  wh : 76800/76800(100.0%) : fin 3.00s
  v43  wh : 76800/76800(100.0%) : fin 2.96s
  v42  wh : 76800/76800(100.0%) : fin 3.16s
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s
それでは op.yaml で試してみます。
$ cat op.yaml
defs:
  rtd_A: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  mov4: IMG_3999_4.mov

big_ball:
- kind: ball
  rtd: { diff: 0.3 }
  maps:
  - fn: '*mov4'
    fn_r: '*mov4'
    t2m: [ ax.zoom_all(2), ax.rot_x(90) ]
  - fn: '*mov4'
    fn_r: '*mov4'
    t2m: [ ax.zoom_all(2), ax.rot_x(90), ax.rot_z(180) ]
  m2g: [ ax.zoom_all(1000) ]

all:
- kind: export
  export: big_ball

- kind: or
  args:
  - kind: ball
    rtd: '*rtd_A'
  - kind: cube
    rtd: '*rtd_A'
    m2g: [ 'ax.zoom_all(0.8)' ]
  m2g: [ 'ax.slide([-1,-1,0])', 'ax.zoom_all(20)' ]

- kind: and
  args:
  - kind: cube
    rtd: '*rtd_A'
    m2g: [ 'ax.zoom_all(0.8)' ]
  - kind: ball
    not: True
    rtd: '*rtd_A'
  m2g: [ 'ax.slide([1,-1,0])', 'ax.zoom_all(20)' ]

- kind: and
  args:
  - kind: ball
    rtd: '*rtd_A'
  - kind: cube
    not: True
    rtd: '*rtd_A'
    m2g: [ 'ax.zoom_all(0.8)' ]
  m2g: [ 'ax.slide([-1,1,0])', 'ax.zoom_all(20)' ]

- kind: and
  args:
  - kind: ball
    rtd: '*rtd_A'
  - kind: cube
    rtd: '*rtd_A'
    m2g: [ 'ax.zoom_all(0.8)' ]
  m2g: [ 'ax.slide([1,1,0])', 'ax.zoom_all(20)' ]

# EOF

になってます。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op.yaml data_name=all name=out_v48/op_tst div=4 fps=15
  :
wh : 19200/19200(100.0%) : fin 0.83s
frm : 300/300(100.0%) : fin 6m 57.12s
estimated 3.71 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op.yaml data_name=all name=out_v48/op
  :
wh : 260050/307200(84.7%) : total 20.16s : rest 3.09s : 2018/06/09 04:46:31
wh : 307200/307200(100.0%) : fin 17.19s
frm : 600/600(100.0%) : fin 4h 10m 41.01s

$ ls -lt out_v48/ | head
-rw-r--r--  1 kondoh  staff  3098316  6  9 04:48 op_2.mp4
-rw-r--r--  1 kondoh  staff  3142561  6  9 04:48 op_1.mp4
-rw-r--r--  1 kondoh  staff  6684097  6  9 04:46 op.mp4
  :


円柱と円すいもAND,OR,XOR演算

円柱と円すいも「物体の論理演算」に対応してみました。

v49.patch

$ mv rt_v48 rt_v49
$ cat v49.patch | ( cd rt_v49 ; patch -p1 )
$ cd rt_v49
$ make clean
$ make

結構変更を入れたので、一応前回v48のデータで軽く確認をば。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op.yaml data_name=all name=out_v49/opck fps=0.25
  :
2018-06-10 22:52:49.994 Python[20334:447913] mMovieWriter.status: 3. Error: Cannot Save
frm : 5/5(100.0%) : fin 2m 28.28s
estimated 4.94 hour at 640*480 30fps

$ ls -lt out_v49/ | head
-rw-r--r--  1 kondoh  staff   110069  6 10 22:52 opck00005.jpg
-rw-r--r--  1 kondoh  staff    93857  6 10 22:52 opck00004.jpg
-rw-r--r--  1 kondoh  staff   115985  6 10 22:51 opck00003.jpg
-rw-r--r--  1 kondoh  staff   104601  6 10 22:51 opck00002.jpg
-rw-r--r--  1 kondoh  staff    78305  6 10 22:50 opck00001.jpg
  :

なにやらエラーが出てますが、 たぶんfpsの指定が小さい値過ぎて動画へのフレームの追加が出来なかったとかでしょうか? 静止画のファイルは出来ているのでOK。

では、円柱と円すいのデータop2.yamlをば。

$ cat op2.yaml
defs:
  rtd_A: { base: 0.1, diff: 0, reflect: 0.5, reflact: 0.5, density: 2 }
  mov4: IMG_3999_4.mov

big_ball:
- kind: ball
  rtd: { diff: 0.3 }
  maps:
  - fn: '*mov4'
    fn_r: '*mov4'
    t2m: [ ax.zoom_all(2), ax.rot_x(90) ]
  - fn: '*mov4'
    fn_r: '*mov4'
    t2m: [ ax.zoom_all(2), ax.rot_x(90), ax.rot_z(180) ]
  m2g: [ ax.zoom_all(1000) ]

all:
- kind: export
  export: big_ball

- kind: and
  args:
  - kind: ball
    not: True
    rtd: '*rtd_A'
    m2g:
    - ax.zoom_all(0.5)
    - ax.slide_x(1)
  - kind: cube
    not: True
    rtd: '*rtd_A'
    m2g:
    - ax.zoom_all(0.5)
    - ax.slide_x(-1)
  - kind: xor
    args:
    - kind: pipe
      rtd: '*rtd_A'
      m2g:
      - 'ax.slide( way.liss([-1,-1,-1],[1,1,1],[3,4,5])(sec) )'
    - kind: cone
      rtd: '*rtd_A'
      m2g:
      - ax.zoom_z(2)
      - ax.slide_z(-1)
      - 'ax.slide( way.liss([1,1,1],[-1,-1,-1],[4,5,6])(sec) )'
  m2g:
  - ax.rot_x(90)
  - ax.zoom_all(20)

# EOF
$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op2.yaml data_name=all name=out_v49/op2_tst div=4 fps=15
  :
wh : 19200/19200(100.0%) : fin 0.24s
frm : 300/300(100.0%) : fin 2m 27.70s
estimated 1.31 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op2.yaml data_name=all name=out_v49/op2
  :
wh : 307200/307200(100.0%) : fin 9.04s
frm : 600/600(100.0%) : fin 1h 50m 22.71s

$ ls -lt out_v49/ | head
-rw-r--r--  1 kondoh  staff  1627032  6 10 22:24 op2_2.mp4
-rw-r--r--  1 kondoh  staff  3145033  6 10 22:24 op2_1.mp4
-rw-r--r--  1 kondoh  staff  5046499  6 10 22:22 op2.mp4
  :

円柱と円すいは、リサージュで適当に移動させてXORをとってます。 視線の先あたりに「球のNOT」と「立方体のNOT」が隠れていて、それらとANDをとってます。

もはや完成像の想像がついていけず、、、 これが正しい結果なのか、全然判りません。

ちょっと、円柱と円すいを角柱と角すいに置き換えてためしてみます。

$ cp op2.yaml op3.yaml

op3.yaml を編集して...

$ diff -u op2.yaml op3.yaml
--- op2.yaml	2018-06-10 20:28:40.000000000 +0900
+++ op3.yaml	2018-06-10 23:29:55.000000000 +0900
@@ -34,11 +34,13 @@
     - ax.slide_x(-1)
   - kind: xor
     args:
-    - kind: pipe
+    - kind: poly_n_prism
+      n: 16
       rtd: '*rtd_A'
       m2g:
       - 'ax.slide( way.liss([-1,-1,-1],[1,1,1],[3,4,5])(sec) )'
-    - kind: cone
+    - kind: poly_n_pyramid
+      n: 16
       rtd: '*rtd_A'
       m2g:
       - ax.zoom_z(2)

などと16角柱と16角すいに置き換え

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op3.yaml data_name=all name=out_v49/op3
  :
wh : 307200/307200(100.0%) : fin 19.84s
frm : 600/600(100.0%) : fin 5h 11m 24.03s

$ ls -lt out_v49/ | head
-rw-r--r--  1 kondoh  staff    853892  6 11 04:57 op3_4.mp4
-rw-r--r--@ 1 kondoh  staff   3140471  6 11 04:57 op3_3.mp4
-rw-r--r--  1 kondoh  staff   3145345  6 11 04:56 op3_2.mp4
-rw-r--r--  1 kondoh  staff   3129328  6 11 04:55 op3_1.mp4
-rw-r--r--@ 1 kondoh  staff  10784054  6 11 04:53 op3.mp4
  :

円柱1つのところが16*3個の三角形の平面に展開されるので、 さすがに時間がかかりますね。

これは、、、円すいの時と比べて、明らかに角すいの表示がおかしいです(>_<)

底面の多角形が、同じ平面上にある複数の三角形に展開されるところが、 領域の演算の処理的には問題が出てるのかも知れません...


角柱と角すいもAND,OR,XOR演算

前回のv49で、角柱と角すいで試したときの変な挙動を修正してみました。

結構根深い問題で、根本的に勘違いしてる箇所がありました。

例えば、球の表面の法線ベクトルは、中心から表面までのベクトルの方向にしてます。

アフィン変換で球をひしゃげさせたとしても、 法線ベクトルにも同じアフィン変換をかけて、ひしゃげさせて使ってます。

球は、おそらくこれで問題ないでしょう。

角すいの斜めな側面の平面について。

平面上の表面の法線ベクトルは、平面の法線の方向にしてます。

アフィン変換で角すいをひしゃげさせたとき、 球の時と同様に法線ベクトルにも同じアフィン変換をかけて、ひしゃげさせておりました。

              /
  .         /
    .     /
      . /
      /
    /
  /
/

つまり、こういう斜めの直線と、直行する法線ベクトルがあったとして 横方向に押しつぶすと

       /
 .    /
  .  /
   ./
   /
  /
 /
/

直行じゃなくなります。

       /
      /
..   /
  ../
   /
  /
 /
/

図がアレですが、押しつぶした後も直線に対して直行してほしいのです。

という理屈からすると、 円柱や円すいの側面も、例えば回転で斜めに倒してから上下に押しつぶすようなアフィン変換をかけると、 まずい気がします...

ですが、とりあえず、平面の場合だけ修正して試してみます。

v50.patch

$ mv rt_v49 rt_v50
$ cat v50.patch | ( cd rt_v50 ; patch -p1 )
$ cd rt_v50
$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v50/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 3.03s
  以前の
  v48  wh : 76800/76800(100.0%) : fin 3.11s
  v47  wh : 76800/76800(100.0%) : fin 3.04s
  v45  wh : 76800/76800(100.0%) : fin 3.00s
  v43  wh : 76800/76800(100.0%) : fin 2.96s
  v42  wh : 76800/76800(100.0%) : fin 3.16s
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

まずはop2.yamlの円柱、円すいで

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op2.yaml data_name=all name=out_v50/op2ck fps=1
  :
wh : 307200/307200(100.0%) : fin 11.04s
frm : 20/20(100.0%) : fin 4m 32.99s
estimated 2.27 hour at 640*480 30fps

$ ls -lt out_v50/ | head
-rw-r--r--  1 kondoh  staff  668844  6 12 00:17 op2ck.mp4
-rw-r--r--  1 kondoh  staff   63314  6 12 00:17 op2ck00020.jpg
-rw-r--r--  1 kondoh  staff   67621  6 12 00:17 op2ck00019.jpg
-rw-r--r--  1 kondoh  staff   63586  6 12 00:17 op2ck00018.jpg
-rw-r--r--  1 kondoh  staff  101408  6 12 00:16 op2ck00017.jpg
-rw-r--r--  1 kondoh  staff   93465  6 12 00:16 op2ck00016.jpg
-rw-r--r--  1 kondoh  staff   73056  6 12 00:16 op2ck00015.jpg
-rw-r--r--  1 kondoh  staff   88722  6 12 00:16 op2ck00014.jpg
-rw-r--r--  1 kondoh  staff   50637  6 12 00:16 op2ck00013.jpg

大丈夫そうですね。

そしてop3.yamlの16角柱、16角柱すい

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op3.yaml data_name=all name=out_v50/op3
  :
wh : 289697/307200(94.3%) : total 21.32s : rest 1.21s : 2018/06/12 06:06:35
wh : 307200/307200(100.0%) : fin 20.15s
frm : 600/600(100.0%) : fin 5h 40m 58.89s

$ ls -lt out_v50/ | head
-rw-r--r--  1 kondoh  staff  1560496  6 12 06:08 op3_2.mp4
-rw-r--r--  1 kondoh  staff  3142729  6 12 06:08 op3_1.mp4
-rw-r--r--  1 kondoh  staff  5021109  6 12 06:06 op3.mp4
  :

5時間40分。OKです。

今回のパッチに、しれっと視点移動の箇所に変更というか、指定の追加を入れてみました。

物体の方でリサージュな動きをさせたときに、視点も合わせて追随しやすいかと。

コインのデータで試してみます。

最後尾のコインのさらに後ろに11個目のコインがある場合の位置に視点をもってきて、 最後尾のコインを目標にして視線を設定してみました。

ジェットコースターの後からの眺めを狙ってみましたが、はたして?

$ ./cg.py ep=[-20,-20,-20],[20,20,20],[3,4,5],-3 et=[-20,-20,-20],[20,20,20],[3,4,5],-2.7 sec=20 yaml=spin_coin.yaml data_name=all name=out_v50/coin
  :
wh : 290213/307200(94.5%) : total 9.08s : rest 0.50s : 2018/06/12 11:10:49
wh : 307200/307200(100.0%) : fin 8.62s
frm : 600/600(100.0%) : fin 1h 44m 44.07s

ls -lt out_v50/ | head
-rw-r--r--  1 kondoh  staff  3049291  6 12 11:11 coin_1.mp4
-rw-r--r--  1 kondoh  staff  3170370  6 12 11:10 coin.mp4
  :

1時間45分。
久々に、3Mバイトを超えてるけど、展開してまとめなおすと3Mバイトに収まるパターンでした。

ジェットコースター。激し過ぎて何が何やら、、、


スポーツカー

アフィン変換でひしゃげたときの「法線ベクトル問題」について、 円柱や円すいの側面の場合も対応してみました。

平面の場合も前回のv50のやり方では、-1倍の拡大をかけた場合とか 意図しないケースで法線が反転していたので、 方式を変更しておきました。

デバッグしやすいように、 dat.py でのデータの展開結果について、 なるべく文字列の段階で展開を終えさせて、 展開結果の表示が見易いようにしてみました。 文字列からバイナリへの変換は、後段でまとめて処理するようにしてみました。

v51.patch

$ mv rt_v50 rt_v51
$ cat v51.patch | ( cd rt_v51 ; patch -p1 )
$ cd rt_v51
$ make clean
$ make

では、いつもの速度確認。

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v51/objs_1_2_sc n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 3.09s
  以前の
  v49  wh : 76800/76800(100.0%) : fin 3.03s
  v48  wh : 76800/76800(100.0%) : fin 3.11s
  v47  wh : 76800/76800(100.0%) : fin 3.04s
  v45  wh : 76800/76800(100.0%) : fin 3.00s
  v43  wh : 76800/76800(100.0%) : fin 2.96s
  v42  wh : 76800/76800(100.0%) : fin 3.16s
  v39  wh : 76800/76800(100.0%) : fin 15.37s
  v38  wh : 76800/76800(100.0%) : fin 15.09s
  v37  wh : 76800/76800(100.0%) : fin 15.49s
  v36  wh : 76800/76800(100.0%) : fin 15.13s
  v35  wh : 76800/76800(100.0%) : fin 16.16s
  v34  wh : 76800/76800(100.0%) : fin 38.47s
  v32  wh : 76800/76800(100.0%) : fin 26.84s
  v31  wh : 76800/76800(100.0%) : fin 40.50s
  v30  wh : 76800/76800(100.0%) : fin 38.39s
  v29  wh : 76800/76800(100.0%) : fin 1m 44.72s
  v28  wh : 76800/76800(100.0%) : fin 1m 18.06s
  v27  wh : 76800/76800(100.0%) : fin 1m 8.39s
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

op2.yamlの円柱、円すいのデータで反射の絵柄の違いを見てみます。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=op2.yaml data_name=all name=out_v51/op2ck fps=0.25
  :
wh : 307200/307200(100.0%) : fin 9.30s
frm : 5/5(100.0%) : fin 1m 6.17s
estimated 2.21 hour at 640*480 30fps

$ ls -lt out_v51/ | head
-rw-r--r--  1 kondoh  staff     144  6 14 01:45 op2ck.mp4
-rw-r--r--  1 kondoh  staff  101409  6 14 01:45 op2ck00005.jpg
-rw-r--r--  1 kondoh  staff   50623  6 14 01:45 op2ck00004.jpg
-rw-r--r--  1 kondoh  staff   73917  6 14 01:44 op2ck00003.jpg
-rw-r--r--  1 kondoh  staff   68342  6 14 01:44 op2ck00002.jpg
-rw-r--r--  1 kondoh  staff   55695  6 14 01:44 op2ck00001.jpg

fpsの指定が小さいときは、動画の1コマ追加でエラーが出てたので、 fpsが1未満なら動画はあきらめる対策を入れてみました。;-p)

右となりに対応するv50の絵を並べてみます。

間違い探し? どこが違うかよく判りませんね。

では、AND,OR,NOTを駆使して、 スポーツカーっぽい物体のデータを cool_car.yaml に作ってみました。

$ ./cg.py eyep=[0,0,0],[300,300,100],10 sec=20 yaml=cool_car.yaml data_name=all name=out_v51/car
  :
wh : 256259/307200(83.4%) : total 11.62s : rest 1.92s : 2018/06/14 04:18:10
wh : 307200/307200(100.0%) : fin 10.26s
frm : 600/600(100.0%) : fin 2h 12m 4.18s

$ ls -lt out_v51/ | head
-rw-r--r--  1 kondoh  staff   484681  6 14 04:20 car_2.mp4
-rw-r--r--  1 kondoh  staff  3142210  6 14 04:20 car_1.mp4
-rw-r--r--  1 kondoh  staff  3885846  6 14 04:18 car.mp4
  :

2時間12分。 視点がそう少し近い方が良かったかな。 ボデー表面で屈折光ましましにしたので、透けてる感が強いです。

良さげな静止画を選んでみました。


カットモデル

モーターショーの展示のような、 断面むき出しの「カットモデル」な表示を目指してみました。

v52.patch

$ mv rt_v51 rt_v52
$ cat v52.patch | ( cd rt_v52 ; patch -p1 )
$ cd rt_v52
$ make clean
$ make

まずは前回悔やまれた視点接近バージョンをば。

$ diff -u cool_car.yaml cool_car_r.yaml
--- cool_car.yaml	2018-06-14 00:58:53.000000000 +0900
+++ cool_car_r.yaml	2018-06-15 21:57:23.000000000 +0900
@@ -144,5 +144,6 @@
   export: cool_car
   m2g:
   - ax.zoom_all(0.2)
+  - ax.rot_z(180)
 
 # EOF

前後の向きだけちょっと変更。

$ ./cg.py eyep=[0,0,0],[130,90,30],10 sec=20 yaml=cool_car_r.yaml data_name=all name=out_v52/car_
  :
wh : 307200/307200(100.0%) : fin 17.96s
frm : 600/600(100.0%) : fin 3h 44m 43.21s

$ ls -lt out_v51/ | head
-rw-r--r--  1 kondoh  staff   451544  6 14 13:37 car__2.mp4
-rw-r--r--  1 kondoh  staff  3142600  6 14 13:37 car__1.mp4
-rw-r--r--  1 kondoh  staff  3846140  6 14 13:35 car_.mp4
  :

さて、カットモデル用のデータ cool_car_cut.yaml。

元々ボデーの曲面は、 球に対してx,y,z方向に異なる割合で拡大をかけた楕円体を3つ用意して、 ANDをとった領域になってます。

固形石鹸のような感じになってしまうかと思ったら、 案外いい感じの曲面になってます。

まずは、この3つの楕円体を移動させつつ、 楕円体そのものも薄い透明で表示させてみました。

さらにカットモデルにするため、 カット用の立方体を用意して、 スポーツカー本体とANDをとって断面を見せてみます。

$ ./cg.py eyep=[0,0,0],[300,300,100],20 sec=20 yaml=cool_car_cut.yaml data_name=all name=out_v52/cut_tst div=8 fps=3
  :
wh : 4800/4800(100.0%) : fin 0.23s
frm : 60/60(100.0%) : fin 46.13s
estimated 8.20 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[300,300,100],20 sec=20 yaml=cool_car_cut.yaml data_name=all name=out_v52/cut
  :
wh : 307200/307200(100.0%) : fin 12.50s
frm : 600/600(100.0%) : fin 5h 58m 1.88s

$ ls -lt out_v52/ | head
-rw-r--r--  1 kondoh  staff  3139682  6 16 04:32 cut_1.mp4
-rw-r--r--  1 kondoh  staff    97864  6 16 04:32 cut_2.mp4
-rw-r--r--  1 kondoh  staff  3479803  6 16 04:30 cut.mp4
  :

$ ./cg.py eyep=[0,0,0],[130,90,30],10 sec=20 yaml=cool_car_cut.yaml data_name=all name=out_v52/cut2_tst div=8 fps=3
  :
wh : 4800/4800(100.0%) : fin 0.59s
frm : 60/60(100.0%) : fin 1m 36.82s
estimated 17.21 hour at 640*480 30fps

$ ./cg.py eyep=[0,0,0],[130,90,30],10 sec=20 yaml=cool_car_cut.yaml data_name=all name=out_v52/cut2_
  :
wh : 307200/307200(100.0%) : fin 24.96s
frm : 600/600(100.0%) : fin 15h 10m 20.77s

$ ls -lt out_v52/ | head
total 198360
-rw-r--r--  1 kondoh  staff   930308  6 16 22:45 cut2__2.mp4
-rw-r--r--  1 kondoh  staff  3139356  6 16 22:45 cut2__1.mp4
-rw-r--r--  1 kondoh  staff  4354425  6 16 22:43 cut2_.mp4

15時間ちょい。


工事中