カメラとコントロール
カメラはシーンをどこから見るかを決める。種類は基本的に 透視(Perspective)と並行(Orthographic)の2つ。 ユーザー操作で動かす場合は addons のコントロール群が便利。
PerspectiveCamera
現実の遠近感を再現する。3D シーンの9割はこれ。
const camera = new THREE.PerspectiveCamera(
fov, // 視野角(度)
aspect, // 画面のアスペクト比
near, // 近接クリッピング距離
far, // 遠方クリッピング距離
)
パラメータの目安
- FOV: 35〜60 度が普通。シネマティック=35、ゲーム=60〜75、超広角=90。
- near: 0.1 が無難。0 にしない。小さいほど深度精度が落ちる。
- far: 必要最小限。far/near の比が大きいほど Z-fighting が起きやすい。
位置・向き
camera.position.set(3, 2, 5)
camera.lookAt(0, 0, 0) // ある点を見る
camera.lookAt(target.position) // オブジェクトを見る
リサイズ対応(必須)
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix() // 必ず呼ぶ
renderer.setSize(window.innerWidth, window.innerHeight)
})
OrthographicCamera
並行投影。遠近感がない。2D 表示・ミニマップ・等角ビューに。
const aspect = window.innerWidth / window.innerHeight
const d = 5
const camera = new THREE.OrthographicCamera(
-d * aspect, d * aspect, // left, right
d, -d, // top, bottom
0.1, 100, // near, far
)
camera.position.set(0, 0, 5)
camera.lookAt(0, 0, 0)
リサイズ時は4辺すべて再計算:
window.addEventListener("resize", () => {
const aspect = window.innerWidth / window.innerHeight
camera.left = -d * aspect
camera.right = d * aspect
camera.top = d
camera.bottom = -d
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
})
ズーム
Orthographic は camera.zoom でサイズを変える。updateProjectionMatrix() を忘れない。
その他のカメラ
CubeCamera— 6方向にレンダリングして CubeMap を取得。実時間反射に。StereoCamera— 立体視(VR)用に左右2眼カメラを生成。ArrayCamera— 複数のカメラを束ねる。マルチビューポート。
カメラを動かす方法
マウスでぐるぐる: OrbitControls
もっとも定番。これさえあればプロトタイプは事足りる。
import { OrbitControls } from "three/addons/controls/OrbitControls.js"
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 慣性
controls.dampingFactor = 0.05
controls.minDistance = 1
controls.maxDistance = 20
controls.maxPolarAngle = Math.PI / 2 // 地面下に潜らない
controls.target.set(0, 0, 0)
// loop で update を呼ぶ
renderer.setAnimationLoop(() => {
controls.update()
renderer.render(scene, camera)
})
enableDamping = trueならループでcontrols.update()が必須。enablePan= 中ボタン/Shift+ドラッグでの平行移動。商品ビューではfalse。autoRotate+autoRotateSpeedで勝手にぐるぐる。
Trackball: TrackballControls
地面の概念がない自由な回転。分子・3Dモデルビューア向き。
FPS風: PointerLockControls
カーソルをロックして視点回転。WASD等の移動は自分で書く必要がある。
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js"
const controls = new PointerLockControls(camera, renderer.domElement)
document.body.addEventListener("click", () => controls.lock())
飛行: FlyControls / FirstPersonControls
飛んだり、地表を歩くように動かしたりする標準的な操作。movementSpeed 調整。
シネマティック: CameraControls
サードパーティ(camera-controls)。
OrbitControls より豊富な API(カメラを moveTo、rotateTo、fitTo 等)。
npm install camera-controls
import CameraControls from "camera-controls"
import * as THREE from "three"
CameraControls.install({ THREE })
const cc = new CameraControls(camera, renderer.domElement)
cc.fitToBox(targetObject, true) // 対象が画面に収まる
cc.rotate(Math.PI / 4, 0, true) // 回す
cc.dollyTo(5, true) // ズーム
// loop で update を呼ぶ
const clock = new THREE.Clock()
renderer.setAnimationLoop(() => {
cc.update(clock.getDelta())
renderer.render(scene, camera)
})
カメラを追従させる
const tmp = new THREE.Vector3()
renderer.setAnimationLoop(() => {
target.getWorldPosition(tmp)
camera.position.lerp(tmp.clone().add(new THREE.Vector3(0, 2, 5)), 0.1)
camera.lookAt(tmp)
renderer.render(scene, camera)
})
カメラを切り替える
複数カメラを使い分けたい時は、render の引数を変えるだけで良い。
renderer.render(scene, currentCamera)
DragControls — オブジェクトをドラッグ
カメラそのものではないが、シーン内のメッシュをドラッグして動かしたい時のコントロール。
import { DragControls } from "three/addons/controls/DragControls.js"
const drag = new DragControls(draggables, camera, renderer.domElement)
drag.addEventListener("dragstart", (e) => controls.enabled = false)
drag.addEventListener("dragend", (e) => controls.enabled = true)
TransformControls — Blender 風ガイザー
位置/回転/スケールの矢印・リングが出るコントロール。シーンエディタを作るときに必須。
import { TransformControls } from "three/addons/controls/TransformControls.js"
const tc = new TransformControls(camera, renderer.domElement)
tc.attach(targetMesh)
tc.setMode("translate") // "translate" | "rotate" | "scale"
scene.add(tc)
// OrbitControls と併用するなら、ドラッグ中は orbit を止める
tc.addEventListener("dragging-changed", (e) => {
controls.enabled = !e.value
})
FOV を動かして緊張感を出す
ズームインや「ヒッ」とする緊張演出は fov をアニメーションするだけで作れる。
updateProjectionMatrix() を忘れずに。
camera.fov = THREE.MathUtils.lerp(camera.fov, targetFov, 0.05)
camera.updateProjectionMatrix()
急激なカメラ動きは映像酔いを引き起こす。
- スムージング(lerp)を入れる
- FPS 視点では FOV 60〜75 を維持
- ロール(z軸回転)は最小限
- prefers-reduced-motion を尊重して動きを抑える