frontendBaby

公開日

- 9 分で読めます

第9話 「新たなる探求!光の正体と影の仕組み」


Three.js ってなんだろう? カエル君と学ぶ、ブラウザ3Dの不思議な世界

この物語は、Three.jsを楽しく学ぶことを目的に、生成AIを活用して執筆されています。 技術的な情報の正確性には細心の注意を払っていますが、その内容がすべて真実であることを保証するものではありません。 あくまで学習の補助ツールとして、肩の力を抜いてお楽しみください。


登場人物紹介

  • カエル君: 最近プログラミングを学び始めた元気なカエル。まだ知らないことばかりだけど、好奇心は人一倍。「〜ケロ」という口調が特徴。
  • 三郎先生: 3DグラフィックスやWeb技術にとても詳しい物知り博士。どんな質問にも優しく答えてくれる。

第9話:

三郎先生: 「おや、カエル君じゃないか。最終講義は昨日だったはずだが、どうしたんだい? その目は、また新たな謎を見つけてしまったようだね。」

カエル君: 「先生! まさにその通りなんだケロ! ふとマテリアルをMeshBasicMaterialからMeshStandardMaterialに変えてみたんだケロ。そしたら…僕の愛しの立方体が、真っ黒になっちゃったんだケロ!」

三郎先生: 「ほうほう。」

カエル君: 「慌ててコードを見直して、試しにライトを追加してみたら…パッと立方体が見えるようになったんだケロ! この『光』ってやつは、一体何者なんだケロ!? そして、光が当たるとできる、あのリアルな『影』は、どういう仕組みなんだケロ!?」

三郎先生: 「はっはっは! 素晴らしい! それこそが、3Dグラフィックスを『リアル』たらしめる、光と影の魔法の入り口だ。よろしい、今日はその魔法の正体を解き明かしていこう。」

光の正体:それは『情報』である

三郎先生: 「まず理解すべきは、Three.jsにおける光は、目に見えるオブジェクトではない、ということだ。光とは、**『他のオブジェクトの表面の色を、どのように計算すべきか』をシェーダーに伝えるための『情報』または『データ』**なんだよ。」

カエル君: 「情報…? オブジェクトじゃないんだケロ?」

三郎先生: 「うむ。カエル君が最初に使ったMeshBasicMaterialは、光の情報を完全に無視する。『とにかくこの色でベタ塗りしてくれ』という、非常にシンプルなマテリアルだ。だからライトがなくても色が見えた。しかし、MeshStandardMaterialMeshPhongMaterialは、光の計算を行うことを前提とした、より高度なマテリアルだ。彼らは、レンダラーから光の情報が与えられない限り、どうやって自分の色を計算していいか分からず、結果として真っ黒になってしまうんだ。」

カエル君: 「なるほど! MeshStandardMaterialは、光という情報がないと仕事ができない、真面目なやつなんだケロね。」

三郎先生: 「その通り。そして、レンダラーはrender()が呼ばれると、シーンの中からライト(例えばDirectionalLightなど)を探し出し、その位置、方向、色、強さといった情報を**uniform変数**という形で、GPU上で動くシェーダーに渡す。シェーダーは、ピクセルを一つ一つ描画する際に、その情報と、ピクセルの面の向き(法線)を使って、複雑な光の反射計算を行う。この計算によって、光が当たっている面は明るく、そうでない面は暗くなり、我々が知っている『陰影』、つまり立体感が生まれるんだ。」

影の仕組み:Shadow Mappingという名のトリック

カエル君: 「陰影の仕組みは分かったんだケロ。でも、オブジェクトが別のオブジェクトに落とす、あのくっきりした『影』は、もっと複雑そうだケロ!」

三郎先生: 「カエル君、よくぞ気づいた。影は、陰影のように自動で生まれるものではない。あれは、レンダラーが裏側でこっそり行っている、非常に巧妙かつ高コストな**『トリック』なんだ。専門用語でシャドウマッピング(Shadow Mapping)**と言う。」

カエル君: 「トリック!? どういうことだケロ?」

三郎先生: 「影を描画するために、レンダラーは通常の描画の前に、もう一つの特別なレンダリングパスを実行する。その手順はこうだ。」

  1. 準備: まず、我々がrenderer.shadowMap.enabled = true;のように影を有効にし、光を放つライトにlight.castShadow = true;、影を受けるオブジェクトにmesh.receiveShadow = true;、影を落とすオブジェクトにmesh.castShadow = true;と設定しておく必要がある。

  2. 秘密のレンダリング: 準備が整っていると、レンダラーは本番の描画の前に、一度**『光源の視点』**からシーンをレンダリングする。カメラからではなく、ライトから世界がどう見えるかを描くんだ。

  3. シャドウマップ生成: この秘密のレンダリングでは、色は一切描画しない。代わりに、光源からオブジェクトまでの『距離』だけを、**深度マップ(シャドウマップ)**という一枚のテクスチャに記録していく。

  4. 本番レンダリングと比較: そして、いよいよ我々のカメラから見た本番のレンダリングが始まる。シェーダーは、地面のピクセルなどを描画する際に、こう自問するんだ。『このピクセルは、光源から見て、先ほど作ったシャドウマップに記録されている距離よりも遠くにあるか?』とね。

カエル君: 「もし、シャドウマップの距離より遠かったら…?」

三郎先生: 「その通り! それは、そのピクセルと光源の間に、何か別のオブジェクト(シャドウマップに記録された、より近いオブジェクト)が存在する、ということになる。つまり、そのピクセルは**『影の中にある』**と判断され、暗く描画されるんだ。」

カエル君: 「うわあああ! なんて巧妙なトリックなんだケロ! 一度ライトの視点から距離の地図を作っておいて、本番でその地図と見比べて影かどうかを決めてるんだケロね! 高コストだっていう理由もよく分かったんだケロ…! もう一回レンダリングしてるようなものだケロ!」

三郎先生: 「その通りだ、カエル君。光がなければ、世界はただの色の集合に過ぎない。だが、光と、その光が遮られて生まれる影が加わることで、世界は深みとリアリティを持つ。この二つを理解し、使いこなすことこそ、3Dの世界に『命』を吹き込むということなんだよ。」

カエル君: 「先生…僕、また一つ、創造者の神髄に近づいた気がするんだケロ! 冒険はまだまだ終わらないんだケローッ!」


(第10話へつづく)