Processingの3DCGにおけるライティングの扱い方
Processing の3Dグラフィックスにおけるライティング(光源や反射)の扱い方が自分の知ってるモデルとちょっと異なり、調査が必要になったのでメモ。デフォルメで不正確になっているところがあるかもしれないがご容赦。
基本概念
光源
Processingで作れる光源には directionalLight, pointLight, spotLight, ambientLight がある。便宜上、後述の反射が関係する「普通の光源」と、関係しない「特殊な光源」に分けてみた。
反射
面と光の入射角から(見る方向と無関係に)明るさが決まる乱反射と、見る方向と光の反射角との一致により明るさが決まる全反射がある。
全反射にはshininess (シャイニーネス)というパラメタがあり、この値が大きいほど散乱が少ない(見る方向が光の反射角と一致したときの光が強く、ずれると急激に弱くなる)ことを表す。
Processingにおける取り扱い
考え方
つや消しの質感やメタリックな質感を描くために、「物体の材質と色を設定する」のようにストレートに言えればいいのだけれど、そうはいかない。代わりにこんな考え方になっている。
- 乱反射を再現するには、光源に「乱反射の光」を、物体に「乱反射の色」を設定する。
- 全反射を再現するには、光源に「全反射の光」を、物体に「全反射の色」を設定する。
- 乱反射と全反射が別々にシミュレートされ、それらを足し合わせることで最終的な描画色が決まる。
現実世界では光は光でしかなく、乱反射・全反射は物体表面で起こる現象の違いなので、このように「乱反射・全反射で別々の光がある」かのような世界観にはちょっと違和感がある。しかしコンピューターの立場からするとこれらは計算方法が異なる現象なので、パラメータを別々にしてそれぞれ計算して足し合わせると楽だ!というのは理解できる。
Processing関数との対応
さて、上のコンセプトを実際にProcessingの関数に対応付けてみると、こうなる。
物体側の設定 | 光源側の設定 | |||
---|---|---|---|---|
乱反射 | fill() | 色 | directionalLight() pointLight() spotLight() ambientLight() |
色・位置・方向等 |
全反射 | specular() shininess() |
色 shininess |
lightSpecular() | 色 |
光沢のないつや消しの質感を描くのであれば、上の「乱反射」の行の関数だけを使って「乱反射の色」や「乱反射の光」を設定すればよい。光沢のある質感を描く場合には、「全反射」の行の関数を追加で呼んで「全反射の色」や「全反射の光」を混ぜる必要がある。特に lightSpecular() は directionalLight()/pointLight()/spotLight() の前に呼ぶことで全反射の光が光源に混ざるという変わった性質を持つので注意。
実シナリオでの使い方
つや消しの質感を描く場合
次のように「乱反射の光と色」だけ扱えばよい。これは簡単。
- ambientLight() で環境光を設定する。暗闇の表現には小さい値を、明るい場所の表現には大きな値を設定する。
- directionalLight()/pointLight()/spotLight() で「乱反射の光」を作る。
- fill()で「乱反射の色」を設定して物体を描く。
ソースコード例と実行結果1(つや消し):
光沢のある質感を描く場合
上の手順に加えて、「全反射の光と色」も扱うようにする。
- 光源作成前(directionalLight()/pointLight()/spotLight() を呼ぶ前)に lightSpecular() を呼んで「全反射の光」を混ぜる。「乱反射の光」と色合いが違いすぎると不自然(あるいは特殊効果)になるので、まずは「乱反射の光」と同じ値、あるいは白を指定して、結果を見ながら調整するのがよい。
- 物体を描く際に(fill() を呼んでいるあたりで)、specular() で「全反射の色」を、shininess()で光沢の強さを設定する。光源のときと同様、「乱反射の光」と色合いが違いすぎると不自然になるので、まずは同じ値や白から出発するとよい。
- 全反射の色と乱反射の色のバランスで質感を調整する。次のソースコード例では、1つ目は全反射の方が弱いボーリング球のような質感、2つ目は思い切って全反射を強く・乱反射を弱くしたメタリックな質感となっている。
ソースコード例と実行結果2(ボーリング球):
ソースコード例と実行結果3(メタリック):
おまけ
- 物体を描く際に emissive() を呼ぶと、物体自らの発光(蛍光効果)を加えられる。
- lightFalloff() で光の距離による減衰の度合いを設定できる。
- ambientLight() に位置を指定するオプショナルパラメータがあって意味ねーじゃんと思ったら、lightFalloff() と組み合わせて「環境光が強いエリアを部分的に作り出す」ことができるらしい。
- 物体を描く際に ambient() を呼ぶと、環境光に対する色を設定できる。ただし有効な使い道は思い付かない。(わざわざ fill() で設定したものと違う色にすることで何がうれしいのか?)