人口十万人でフレームが二コマまで落ちた。原因は建物の数ではなく、描画を呼ぶ回数だった
遠くの建物を簡単な形にしても、まったく速くならなかった。
見ていた場所が間違っていた。
日本進化シミュレーション第2回 / 全16回目次
「人口一万人を超えると、さすがに重くなりますね」
そう言われて、自分でも試してみた。
たしかに重い。
カメラを動かすと、画面がついてこない。
ここから直しきるまでに二回まわり道をしている。
一回目は効かず、二回目でようやく効いた。
その差がどこにあったのかが、今回の話になる。
まず、どのくらい重いのかを測った
感覚で「重い」と言っていても直しようがないので、先に数字にした。
デバッグ用の関数を叩いて、道路と住居をひたすら置き続ける。
そうやって建物二千七百棟ほど、人口でいうと十万人規模の街を機械的に作った。
そのうえで、一フレームにかかる時間を測る。
結果はひどいものだった。
街に寄った視点で、一秒あたり二コマ。
引いた視点でも十二コマ。
二コマというのは、もう動いていないのと変わらない。
一回目:遠くの建物を簡単にする
建物が増えると重くなるのだから、遠くの建物を簡単な形にすればいい。
そう考えて、Three.jsに用意されている仕組みをそのまま使った。
ひとつの建物に「近くで見るときの詳しい形」と「遠くで見るときの簡単な箱」を持たせておく。
カメラとの距離に応じて、勝手に切り替わる。
建物を作る関数は二十三種類あったが、そこには一行も手を入れずに済んだ。
できあがった建物を、あとから包むだけでよかったからだ。
きれいに入った、と思った。
測ってみたら、ほとんど変わっていなかった。
効かなかった理由
しばらく分からなかったが、考えてみれば当たり前の話だった。
画面に絵を出すとき、コンピューターは「これを描いて」という指示を一回ずつ出している。
建物が二千七百棟あれば、その指示も二千七百回ぶん出ることになる。
僕がやったのは、一回ぶんの中身を軽くすることだった。
複雑な形を、単純な箱に置き換えた。
でも、指示の回数そのものは一回も減っていない。
重かったのは中身ではなく、回数のほうだった。
そこを触っていなかったのだから、効かないのは当然だ。
二回目:まとめて一回で描く
Three.jsには、同じ形をまとめて一回で描く仕組みがある。
位置と大きさと色だけを一つずつ変えて、指示は一回で済ませる。
これに書き換えた。
遠くの建物は、全部この共有のかたまりに書き込む。
二千七百棟ぶんの指示が、一回になった。
今度は効いた。
引いた視点で、一フレーム四ミリ秒。
毎秒二百コマ以上の計算になる。
それでも近くが速くならなかった
ところが、街に寄った視点はまだ重いままだった。
調べてみて、これも当たり前だった。
「カメラから八十より遠い建物をまとめる」という書き方をしていたのだが、街に寄ると、八十以内に建物がぎっしり入る。
数えたら、二千六百七十三棟のうち千八百六十五棟が「近い」と判定されていた。
七割が近いのだから、まとめる意味がない。
そこで、距離ではなく件数で切ることにした。
カメラから近い順に並べて、上位三百棟だけを詳しい形のままにする。
それより後ろは、どんなに近くても共有のかたまりへ送る。
これで、街に寄った視点も二コマから十二コマになった。
今から振り返ると、この「近い順に何棟まで」という決め方が、今回いちばん効いた一手だった。
距離という条件は、密度が変わると意味が変わってしまう。
件数で切れば、どんなに建て込んでも上限は上限のままだ。
建物が全部、真っ黒になった
最後にひとつ、間抜けな話を書いておく。
まとめて描く方式では、建物ごとの色を別に指定する。
そのとき、色を使うのだからと思って、マテリアルに「頂点ごとの色を使う」という設定を足した。
遠くの建物が、全部真っ黒になった。
原因は、簡略化した箱にはそもそも頂点ごとの色が入っていないことだった。
存在しない色を掛け算していたので、答えがゼロになっていた。
つまり黒だ。
その設定を消したら直った。
建物ごとの色は、何も指定しなくても最初から効く仕組みになっていた。
足したものが、そのまま邪魔をしていた。
数字のまとめ
同じ街、同じ端末で測った結果を並べておく。
- 街に寄った視点:毎秒二コマ → 毎秒十二コマ
- 引いた視点:毎秒十二コマ → 一フレーム四ミリ秒(毎秒二百コマ相当)
- 建物数:約二千七百棟(人口十万人規模)
直したあとに書き出すと、たった数行の話に見える。
実際には、一回目の見当違いに時間を使った。
重いと感じたときに、何が重いのかを取り違えると、いくら手を動かしても数字が動かない。
今回でいえば、見ていたのは形の複雑さで、効いたのは指示の回数だった。
測って、外して、測り直す。
それしかなかったと思う。
※運営者が個人で書いているnoteです。