こんにちは、株式会社AMDlabのDDDDbox事業部で日々開発を行っている鈴木です。
弊社では3D表現の技術として、Three.jsおよびreact-three-fiberを使用することが多いのですが、最近カメラの設定周りでひっかかったところがあったため、特にOrthographic Cameraについてまとめ直してみました。
- Perspective Camera
- Orthographic Camera
- 比較
- Three.jsのOrthographic Camera
- react-three-fiber/dreiのOrthographicCamera
- OrthographicCameraの比較
- カメラにオブジェクトが映らない?
- まとめ
Perspective Camera
日本語だと透視投影カメラといいます。人間の目の見え方を模倣するように設計されているため、現実世界と同等の見せ方をしたい場合に使われます。 カメラの近くにあるオブジェクトは大きく見え、カメラより遠くにあるオブジェクトはより小さく見えます。
よくある3DのアクションゲームなどではこのPerspective Cameraを使い、現実世界の見え方に近づけて、臨場感を高めています。
なにかしらの3D表現するときは、基本的にこちらのPerspective Cameraを使うことが多いと思われます。

Orthographic Camera
日本語だと並行投影カメラや直交投影カメラといいます。人間の目の見え方を模倣することを目的とはしていません。 このカメラは、遠近感を排除し、オブジェクトの描画サイズはカメラからの距離に依存しない、平行な線は距離によらず平行なまま(収束しない)という特徴があります。
Perspective Cameraでは、カメラの近くにあるオブジェクトは大きく見え、カメラより遠くにあるオブジェクトはより小さく見えます。
一方、Orthographic Cameraでは、カメラの近くにあるオブジェクトも、カメラの遠くにあるオブジェクトも、同じ大きさで表示を行うため、正確な相対的なサイズの判断が可能です。 Perspective Cameraと異なり、平行線はずっと平行なままで描画されます。
この不思議な性質は、建築設計の業務にとっては非常にメリットがあります。
建築モデルやCADモデルの投影図ではむしろ遠近感が不要なことが多く、建物の平面図・立面図・断面図などで、壁、窓、ドアなどの建築要素の寸法を正確に比較できるという点は非常に好都合です。
AutoCADやRevitなどの主要な建築設計ソフトウェアは、Perspective CameraのようなパースビューとOrthographic Cameraのような平行投影ビューを双方ともサポートしています。
Orthographic Cameraは建築設計だけでなく、視覚的な深さが必要ないパズルゲームなどでもよく使用されますね。 DDDDboxでもこちらのOrthographic Cameraをメインで使用しています。

比較
Perspective CameraとOrthographic Cameraの違いを表でまとめます。
| 特徴 | Perspective Camera | Orthographic Camera |
|---|---|---|
| 投影方法 | 透視投影 | 平行投影 |
| 距離によるサイズ変化 | あり(近くのものは大きく、遠くのものは小さく) | なし(オブジェクトの大きさは距離に依存しない) |
| 主な用途 | 現実世界に近い表現、3Dゲーム、映像制作 | 設計図、2Dゲーム、図面表示 |
| 視錐台 | 台形ピラミッド(錐台) | 直方体 |
なお、視錐台というのは、カメラで写す有限空間のことです。 現実世界では地平線の向こうまで写すことができますが、カメラではその範囲を制限する必要があります。 その範囲形状を視錐台といい、視錐台の内部にあるオブジェクトがカメラで写されることになります。

Three.jsのOrthographic Camera
Three.jsのOrthographicCameraコンストラクタ は new THREE.OrthographicCamera(left, right, top, bottom, near, far) というシグネチャです。
これらの値は、zoom値が1のときのカメラの視錐台の左端、右端、上端、下端、近端、遠端の値を指定します。
const camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 0.1, 2000 ); scene.add( camera );
また、後述しますが、OrthographicCameraには、zoom プロパティがあり、この値を操作することでズームイン・ズームアウトを行うことができます。
Perspective Cameraの場合は遠近感があるため、カメラ位置を移動させることによってもズームイン・ズームアウトを行うことができますが、OrthographicCameraの場合はズームイン・ズームアウトを行うためには zoom プロパティを操作する必要があります。
ズームイン・ズームアウトを行うと、内部的にカメラの視錐台の左端、右端などの値が再計算されて、視錐台のサイズが変化します。
react-three-fiber/dreiのOrthographicCamera
reactの宣言的UIの手法でThree.jsを扱うには、react-three-fiberを使用します。 react-three-fiberにはさらに@react-three/dreiというサポートライブラリがあるため、宣言的な書き方のサポートが充実しています。
この中でdreiのOrthographicCameraは、Three.jsのOrthographicCameraをラップしたもので、より直感的に設定できるようになっていますが、Three.jsのOrthographicCameraとは挙動が異なるのでその点は注意が必要です(筆者はここで少しひっかかりました)。
Three.jsのOrthographicCameraは、カメラの視錐台の左端、右端、上端、下端、近端、遠端の値を明示的に指定するものでした。 dreiのOrthographicCameraは、デフォルトでは画面サイズに合わせてカメラの視錐台の左端、右端、上端、下端の値を自動調整するので、これらの値は特に設定の必要がありません。
例えばディスプレイサイズが 1920x1200 の場合、視錐台もこの大きさになります。
つまり見える範囲が広いので、例えば以下のようなサイズ1の立方体をSceneに追加したとき、zoom=1のままだと非常に小さく描画されます。
<mesh position={[0, 0, 0]}> <boxGeometry args={[1, 1, 1]} /> <meshStandardMaterial color={color} /> </mesh>

この場合は、zoom値を上げることにより、視錐台の大きさが再調整され、サイズ1の立方体が画面に全体が映る様になります。
また、manualモードを使用すると、自動調整を無効化し、明示的にカメラの視錐台の左端、右端、上端、下端の値を設定することもできます。
この場合は 視錐台のパラメータの変更後に updateProjectionMatrix を都度呼ぶ必要があります。
OrthographicCameraの比較
| 比較項目 | Three.js | @react-three/drei |
|---|---|---|
| 使用方法 | 手続き的 | 宣言的 |
| カメラ作成 | new (left, right, top, bottom, near, far) |
<OrthographicCamera makeDefault /> のようなJSX構文 |
| 視錐台 | 明示的に left, right, top, bottom, near, far を指定 | 自動で画面サイズに応じて調整(manual オプションで明示的指定も可能) |
| ズームの実現方法 | camera.zoom を操作後に updateProjectionMatrix() を呼ぶ必要あり |
zoom をpropsで渡すだけでよい(自動で updateProjectionMatrix() が呼ばれる) |
| 画面サイズへの対応 | 自動ではない。リサイズ時は手動で再計算が必要 | 自動で追従 |
カメラにオブジェクトが映らない?
react-three-fiberを使用してアプリケーションを開発していると、しばしばカメラにオブジェクトが映らないという問題に遭遇します。 OrthographicCamera使用時にも起こりがちな、代表的な原因と対策をまとめます。
近端 / 遠端(near / far)による描画範囲外
視錐台の値の中で、近端 / 遠端(near / far)による描画範囲外は意識から外れやすいです。 たとえば、camera.near = 1 の場合カメラから1未満の距離にあるオブジェクトは見えませんし、camera.far = 2000 ならそれより遠い距離にあるオブジェクトは見えません。 特にThree.jsのデフォルトでは near=0.1, far=2000 なので、大規模なシーンではfarを大きくする必要があります。
また、オブジェクトの一部でもnear/farクリップ外にある場合、そのオブジェクトは描画されないことがあります。 カメラを極端に近づけすぎて、オブジェクトを通り過ぎてしまった場合などです。
描画されない問題が発生したときは、対象オブジェクトの位置や大きさを確認し、視錐台の範囲に収まっているか確認しましょう。

カメラの向き
単純な原因ですが、カメラがオブジェクトとは全く別の方向を向いていると、オブジェクトが映りません。
react-three-fiberを使っていると、マウス操作でPanやズームなどをいい感じに行ってくれる dreiのOrbitControls を使うことが多いかと思いますが、 OrbitControlsはカメラの向きの制御も行っています。
もしこのOrbitControlsがないとカメラの向きが原点以外に設定される可能性があります。 デフォルトではz軸の負の方向にカメラが向きます。
makeDefault
react-three-fiber環境では、複数のカメラを配置できるため、どのカメラを使うのか明示する必要があります。
makeDefault を明示的に指定し、描画するカメラを指定します。
もしシーン内で複数カメラを切り替える場合は、状態管理やrefを用いてsetDefaultCamera(camera)相当の処理を行う必要があります。
適切にカメラがアクティブになっているか確認しましょう。
ズーム値が合っていない
先に述べた特にOrthographic Cameraの場合、zoom値をある程度大きくするか、オブジェクトをある程度大きく描画しないと、デフォルトでは画面にちゃんと映るくらいの大きさでは描画されません。
とりあえずは <OrbitControls /> をSceneに追加してあげて、ズームすることで対象オブジェクトを画面に映すことができるか確認するのがよさそうです。
camera.updateProjectionMatrix()
manualモードにしている場合、camera.updateProjectionMatrix() を呼び忘れている可能性があります。
カメラにオブジェクトが映らない場合はまずクリップ面の設定と視野の範囲、カメラの向き・位置、アクティブなカメラの設定を疑うと良さそうです。
まとめ
Three.jsのカメラの基本的な概念と、react-three-fiberでの使用方法、OrthographicCameraの設定方法、オブジェクトが映らない場合の原因と対処方法をまとめました。
3D表現のプログラミングにはカメラの設定が欠かせないため、この記事がお役に立てば幸いです。
AMDlabでは、開発に力を貸していただけるエンジニアさんを大募集しております! 少しでもご興味をお持ちいただけましたら、カジュアルにお話するだけでも大丈夫ですのでお気軽にご連絡ください!
中途求人ページ: https://www.amd-lab.com/recruit-list/mid-career
カジュアル面談がエントリーフォームからできるようになりました。 採用種別を「カジュアル面談(オンライン)」にして必要事項を記載の上送信してください!
エントリーフォーム: https://www.amd-lab.com/entry