太陽をつかんで動かせるようにした
固定の光では惑星の全部を照らせないと分かり、カメラを光源にする案を作って測って捨て、読む人が太陽を動かす形にたどり着いた。追従率41%から100%への話も。
#Three.js#ライティング#計測
このサイトのトップにある惑星には、太陽があります。惑星を少し遠くから眺める「俯瞰」の高さに引くと、画面の右上あたりに光る玉が見えて、それをつかんで好きな場所へ動かせます。動かすと、惑星の昼と夜の境目が動き、建物の影の向きが変わります。
この「太陽を手で動かせる」は、最初から考えていた機能ではありません。「どうすれば惑星の全部を明るく照らせるか」を考えて、一度別の案を作り、測って、見た目で捨てたあとにたどり着いた答えです。
固定の光では、どうやっても全部は照らせない
きっかけは、ライトについての素朴な疑問でした。当時の光は、惑星に対して1つの方向に固定されていました。それなら、「カメラ自身を光源(太陽)として考える」のもありなのでは、と思いついたのです。まずは、その案が解こうとしている問題が本当にあるのかを測ることにしました。
問題は、ありました。惑星の表面に立つ5つの建物のうち、光の正面にある建物は明るく、裏側にある建物は暗い。一番暗かったお問い合わせの建物は、一番明るい建物の7分の1の明るさしかありませんでした。カメラはそこを通り、画面のカードはその建物の説明をしているのに、肝心の建物がほとんど見えない。
光の向きを変えれば直るかというと、そうはいきませんでした。太陽の向きを、球の全方向から2度刻みで総当たりして(1万6,200通り)、「一番暗い建物」を少しでも明るくできる向きを探しても、1つも見つかりませんでした。5つの建物は球の上に149.5度も散らばっていて、どの方向から照らしても、必ずどれか1つは夜側に落ちます。定数をいじって解ける問題ではなかったのです。
カメラを太陽にする、という案
そこで、最初に思いついた案を実際に作ってみることにしました。カメラが見ている方向から光を当てれば、今見ている建物は必ず照らされるはずです。
ただ、カメラとまったく同じ向きから照らすと、影が建物の真裏に落ちて見えなくなり、ボクセルの立体感も消えて、惑星がスタジオで撮った置物のように見えてしまいます。そこで、視線から少しずらした角度から照らすことにしました。ずらす角度も測って決めました。20度未満だと、見えている範囲が丸ごと明るい側に入ってしまい、昼と夜の境目が画面に出てこない。いろいろ測って、28.6度にしました。
明るさの目的は、ちゃんと達成しました。一番暗かったお問い合わせの建物の前で、惑星の平均の明るさは62.1から120.7へ、約2倍になりました。
数字はよくなったのに、見た目がよくない
でも、実際に動かして見た僕の感想は「あまりよくない」でした。なんだか、ぼやぼやしている。
最初に疑ったのは、「毎フレーム光が動くせいで、ちらついているのではないか」ということでした。これははっきり否定できました。動きを止めて、5秒あけて2枚撮ると、画素は完全に一致していました。ちらつきはありません。
原因は、影が減っていたことでした。光が視線から28.6度しか離れていないと、建物の影がその建物自身の裏に隠れてしまいます。砂地の部分だけを選んで測ると、影の面積は26.9%から20.6%へ、2割以上減っていました。影がなくなると、建物の輪郭と奥行きが弱くなって、ぼやけて見える。これが「ぼやぼや」の正体でした。
もう1つ、数字にしにくい問題もありました。光がカメラと一緒に動くということは、画面から見た光の向きがいつも同じということです。惑星がどれだけ回っても、陰影がまったく変わらない。惑星が回るのを眺める楽しさの半分が、消えていました。
ちなみに、最初は画面全体の明暗のばらつき(標準偏差)で比べようとして、差が出ませんでした(35.0 と 35.2)。地面や建物の色の違いのほうがずっと大きくて、陰影の差が埋もれてしまうのです。単色の砂地を選んで測って、初めて差が見えました。何を測っているのかを取り違えると、ない差を見落とすこともある、という記録として残しています。
この案は、実装して、測って、見た目で却下しました。
だったら、読む人に太陽を動かしてもらう
次に考えたのが、太陽そのものを置いて、見ている人がつかんで動かせるようにする案です。仕様は先に決めました。
- 太陽を直接ドラッグして動かす
- 置いた場所に止まる(勝手には動かない)
- 動かせる範囲に制限はつけない
- 夜側が真っ暗にならないよう、環境の光で底上げする
「置いた場所に止まる」でいいのは、惑星のほうが回り続けるからです。太陽を止めても、惑星がツアーで回る以上、陰影は変化し続けます。カメラ追従の案が失っていた「回ると陰影が変わる」楽しさを、そのまま取り戻せる形でした。
夜側の底上げも測って決めました。底がないと、夜側の画面のうち「暗すぎて形が読めない画素」が14.1%ありました。環境の光を少し上げると、0.2%になりました。
遠くに置いた太陽は、一度も画面に映らなかった
最初の実装では、太陽をうんと遠く、カメラが届く一番遠い場所よりさらに外に置いていました。
ところが、惑星を1周させても、太陽は一度も画面に映りませんでした。理由は幾何です。このサイトのカメラは、常に惑星の中心を見ています。だから画面に映るのは、その方向を中心にした狭い範囲だけです。遠くにあるものがその範囲に入るには、惑星のほぼ真裏にいる必要があり、そこでは惑星に隠れてしまう。
そこで逆に、太陽を惑星のすぐそばに置くことにしました。近づけると、太陽は惑星の脇、つまり余白のある場所に見えるようになります。ちょうどいい距離の窓は、思ったより狭いものでした。近すぎると地表の建物にめり込み、遠すぎるとまた画面から外れる。使える範囲は、惑星の半径のおよそ2倍から2.4倍の間しかありませんでした。
途中で一度、「太陽がカメラの手前に来たら、透明にして消す」処理を入れたのですが、これは僕の判断で外しました。太陽は、見えなくなる必要はありません。代わりに、太陽が出て動かせるのは、惑星から少し離れた「俯瞰」の高さのときだけにしました。近い高さでは、太陽が画面の角に切れて、1周の3分の1ほどしか映らなかったからです。
指に追いつかない太陽
最後に残ったのが、ドラッグの手触りでした。最初は、マウスを動かした量に比例して太陽を回す、というよくあるやり方で作りました。ところが、測ってみると、太陽はマウスの動きの**41%**しかついてきませんでした。
これも幾何の問題でした。太陽は惑星を中心にした球の上を動きます。その球を外から見ると、太陽は多くの場所で、画面の奥や手前に向かって動いていて、画面上ではほとんど動きません。カメラの正面あたりでは、太陽の動きの94.7%が視線の方向でした。補正の係数を掛ければいいかというと、必要な係数は場所によって変わり、球の縁では無限大に発散します。
そこで、やり方を変えました。マウスのある位置から画面の奥へ向かって線を伸ばし、その線が太陽の球とぶつかった点に、太陽を置く。これなら係数そのものが要りません。測り直すと、ポインタが286ピクセル動いたとき、太陽は287ピクセル動きました。**追従率100%**です。
捨てた案も、残しておく
振り返ると、カメラを太陽にする案は、捨てて正解でした。でも、作って測ったことは無駄ではありませんでした。「固定の光では原理的に全部は照らせない」という結論は、この案を試したから出てきたものです。そして、この結論があったから、「読む人が太陽を動かす」という答えに迷わずたどり着けました。
このサイトでは、試して捨てた案を、捨てた理由と数字と一緒に記録しています。同じ案を、しばらくたってからもう一度思いついてしまったときに、同じ実験をやり直さずに済むからです。
