frontendBaby

公開日

- 13 分で読めます

第6話 「世界を切り取る魔法のレンズ! PerspectiveCameraの謎」


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

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


登場人物紹介

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

第6話:世界を切り取る魔法のレンズ! PerspectiveCameraの謎

カエル君: 「先生、こんにちはケロ! 今日は『カメラ』について教えてほしいんだケロ!」

三郎先生: 「ほう、カメラかい。new THREE.PerspectiveCamera(...) の一行だね。いいところに目をつけた。」

カエル君: 「そうなんだケロ。シーンがあって、オブジェクトがあっても、カメラがなければ何も見えない。そう考えると、カメラってすごく大事な役割なんだなって思ったんだケロ。でも、あの呪文みたいな引数… (75, window.innerWidth / window.innerHeight, 0.1, 1000) …これが一体何なのか、さっぱりなんだケロ。」

三郎先生: 「うむ。あの4つの数字は、3D世界をどのように『切り取って』2Dの画面に映し出すかを決める、魔法のレンズの設計図なんだ。一つずつ解き明かしていこう。」

魔法のレンズの設計図

三郎先生: 「まず、我々が使っている PerspectiveCamera は、日本語で言うと透視投影(とうしとうえい)カメラだ。これは、我々の普段の物の見方と同じで、遠くにあるものは小さく、近くにあるものは大きく見えるカメラだ。」

カエル君: 「ふむふむ。自然な見え方だケロね。」

三郎先生: 「そうだね。ちなみに、これとは別に OrthographicCamera(平行投影カメラ)というものもある。これは遠近感がなく、遠くのものも近くのものも同じ大きさで描画される。建物の設計図や、一部のパズルゲームなどで使われる特殊なカメラだ。だが、基本は PerspectiveCamera だと思っていい。」

三郎先生: 「さて、本題の4つの引数だ。」

1. fov (Field of View) - 視野角

三郎先生: 「最初の 75 という数字。これは fov、つまり視野角だ。単位は『度』。カメラがどれだけ広い範囲を見渡せるか、レンズの広角具合を決めるんだ。」

カエル君: 「視野角…? 望遠鏡とのぞき穴の違いみたいなものケロ?」

三郎先生: 「まさにその通り! この値を小さくすれば、望遠鏡でのぞいたように、遠くのものが大きく見える(ズームイン)。逆に大きくすれば、魚眼レンズのように、より広い範囲が歪んで見えるようになる。75 というのは、人間の視野に近くて、一般的によく使われる値なんだ。」

2. aspect - アスペクト比

三郎先生: 「二つ目の window.innerWidth / window.innerHeight。これは aspect、つまりアスペクト比だ。描画する画面の『横幅と高さの比率』をカメラに教えている。」

カエル君: 「どうしてそんなことを教える必要があるんだケロ?」

三郎先生: 「もしこれを正しく設定しないと、世界が歪んでしまうからさ。例えば、正方形のものを描きたいのに、横長の画面に無理やり表示したら、横に伸びた長方形になってしまうだろう? カメラに『今から映す画面はこういう比率だよ』と伝えておくことで、オブジェクトの比率が正しく保たれるんだ。」

3. near & 4. far - クリッピング平面

三郎先生: 「そして最後の二つ、0.11000。これはそれぞれ nearfar と呼ばれ、カメラが描画する『奥行きの範囲』を決めている。near は『これより手前は映さない』という距離、far は『これより奥は映さない』という距離だ。」

カエル君: 「えっ!? どうしてそんな制限が必要なんだケロ? 全部映してくれればいいじゃないかケロ!」

三郎先生: 「良い疑問だ。これには二つ、重要な理由がある。一つは、パフォーマンスの問題だ。はるか遠くにあって、どうせ見えないくらい小さなオブジェクトまで律儀に計算していたら、GPUが疲れてしまうだろう? 『ここからここまでしか見ない』と決めておくことで、無駄な計算を省いているんだ。」

カエル君: 「なるほど! 賢い手抜きなんだケロね!」

三郎先生: 「もう一つの理由は、深度バッファの精度という、少し専門的な問題だ。コンピュータは、奥行きを判断するために『深度バッファ』というものを使う。このバッファが扱える数値の精度には限界があるんだ。もし、描画範囲が0.001から1億のように極端に広すぎると、近いオブジェクト同士の前後関係が正しく判断できなくなり、表示がチラついたり、おかしな見た目になったりすることがある。だから、描画に必要な範囲を適切に設定してやることが、綺麗な描画には不可欠なんだよ。」

カエル君: 「うーん、奥が深いケロ…。じゃあ、僕たちが設定した (75, ..., 0.1, 1000) というのは…」

三郎先生: 「『視野角75度で、画面の縦横比に合わせて、カメラから距離0.1から1000の範囲にあるものだけを映し出してくれ』という、非常に具体的なカメラの設定だった、というわけさ。このカメラが作り出す四角錐の空間(視錐台と呼ぶ)に収まったものだけが、我々の画面にレンダリングされるんだ。」

カエル君: 「視錐台! カッコいい名前だケロ! ただの一行だと思っていたのに、こんなにも世界の『見え方』を定義していたなんて…。カメラさん、奥が深すぎるケロ! ピョンピョン!」

三郎先生: 「ふふふ。そうだろう。3Dグラフィックスとは、突き詰めれば、仮想世界の情報を、いかにして2Dの画面という『窓』に正しく落とし込むか、という挑戦の歴史でもある。カメラは、その挑戦の最前線に立つ、最も重要な道具の一つなんだよ。」

カエル君: 「先生、またすごい疑問が湧いてきたんだケロ。WebGLにはシーンという概念が無かったみたいに、もしかして…WebGLには『カメラ』も無いんだケロ?」

三郎先生: 「…カエル君、君は本当に物事の核心を突くのが上手いな。その通りだ。WebGLには『カメラ』という概念も、一切存在しない。

カエル君: 「やっぱりーーーっ! じゃあ、僕たちが使っている PerspectiveCamera は、一体何者なんだケロ!? いったい何をやってくれているんだケロ!?」

三郎先生: 「Three.jsのカメラクラスの正体、それは**『行列(ぎょうれつ)ジェネレーター』**さ。もっと言うと、3D空間を2D画面に映し出すために必須となる、2つの超重要な『変換行列』を、我々の代わりに計算してくれる賢い機械なんだ。」

  1. ビュー行列 (View Matrix): 3D空間にある全てのオブジェクトを、まるごと『カメラから見た視点』に動かすための変換行列だ。カメラがどこにあって(position)、どちらを向いているか(lookAt)によって、この行列の値が決まる。

  2. プロジェクション行列 (Projection Matrix): カメラから見た光景を、最終的に2Dのスクリーンに『投影』するための変換行列だ。カエル君がさっき学んだ fovaspect, near, far といったレンズの設計図を元に、この行列が計算される。

三郎先生: 「もし、Three.jsにカメラクラスが無かったら…。我々はカメラを少し動かすたびに、これらの行列を自分で計算しなくてはならない。三角関数やベクトル、難しい線形代数の知識を総動員してね。camera.position.z = 5; という一行の裏側で、Three.jsはこれらの複雑な行列計算を全て肩代わりしてくれているんだ。」

カエル君: 「ひぇぇ…行列…。僕たちが『カメラを5うしろに下げる』って簡単に命令している裏で、そんな難しい計算が行われていたなんて…。想像もつかなかったんだケロ…。」

三郎先生: 「そうだろう。Three.jsのカメラクラスは、我々開発者を、その最も難解な数学の世界から解放してくれる。そして、『カメラをどこに置くか』『何を見るか』という、本来の創造的な作業に集中させてくれる。これこそ、カメラクラスが提供してくれる、最大の価値なんだよ。」

カエル君: 「カメラさん…あなたはただのレンズじゃなくて、僕たちを守ってくれる数学の天才だったんだケロね…。感動で、もうピョンピョンが止まらないんだケローーーッ!」


(第7話へつづく)