196 lines
4.8 KiB
Markdown
196 lines
4.8 KiB
Markdown
# Cameras
|
|
|
|
Define viewpoint and projection for rendering.
|
|
|
|
## Perspective Camera
|
|
|
|
Realistic camera with field of view (most common):
|
|
|
|
```javascript
|
|
const camera = new THREE.PerspectiveCamera(
|
|
fov, // field of view in degrees (typically 45-75)
|
|
aspect, // width / height
|
|
near, // near clipping plane (typically 0.1)
|
|
far // far clipping plane (typically 1000)
|
|
);
|
|
|
|
camera.position.set(0, 5, 10);
|
|
camera.lookAt(0, 0, 0);
|
|
|
|
// Update after changing parameters
|
|
camera.fov = 60;
|
|
camera.updateProjectionMatrix();
|
|
```
|
|
|
|
## Orthographic Camera
|
|
|
|
No perspective distortion (parallel projection):
|
|
|
|
```javascript
|
|
const frustumSize = 10;
|
|
const aspect = window.innerWidth / window.innerHeight;
|
|
const camera = new THREE.OrthographicCamera(
|
|
frustumSize * aspect / -2, // left
|
|
frustumSize * aspect / 2, // right
|
|
frustumSize / 2, // top
|
|
frustumSize / -2, // bottom
|
|
0.1, // near
|
|
1000 // far
|
|
);
|
|
|
|
// Useful for: 2D games, CAD, isometric views
|
|
```
|
|
|
|
## Camera Controls (Addons)
|
|
|
|
### OrbitControls (Most Common)
|
|
```javascript
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
|
const controls = new OrbitControls(camera, renderer.domElement);
|
|
controls.target.set(0, 0, 0);
|
|
controls.enableDamping = true; // smooth motion
|
|
controls.dampingFactor = 0.05;
|
|
controls.minDistance = 5;
|
|
controls.maxDistance = 50;
|
|
controls.maxPolarAngle = Math.PI / 2; // prevent going below ground
|
|
|
|
// In animation loop
|
|
function animate() {
|
|
controls.update(); // required if enableDamping = true
|
|
renderer.render(scene, camera);
|
|
}
|
|
```
|
|
|
|
### FirstPersonControls
|
|
```javascript
|
|
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js';
|
|
|
|
const controls = new FirstPersonControls(camera, renderer.domElement);
|
|
controls.movementSpeed = 10;
|
|
controls.lookSpeed = 0.1;
|
|
|
|
const clock = new THREE.Clock();
|
|
function animate() {
|
|
const delta = clock.getDelta();
|
|
controls.update(delta);
|
|
renderer.render(scene, camera);
|
|
}
|
|
```
|
|
|
|
### FlyControls
|
|
```javascript
|
|
import { FlyControls } from 'three/addons/controls/FlyControls.js';
|
|
|
|
const controls = new FlyControls(camera, renderer.domElement);
|
|
controls.movementSpeed = 10;
|
|
controls.rollSpeed = Math.PI / 24;
|
|
controls.dragToLook = true;
|
|
```
|
|
|
|
### TransformControls
|
|
```javascript
|
|
import { TransformControls } from 'three/addons/controls/TransformControls.js';
|
|
|
|
const controls = new TransformControls(camera, renderer.domElement);
|
|
controls.attach(mesh);
|
|
scene.add(controls);
|
|
|
|
// Switch modes
|
|
controls.setMode('translate'); // or 'rotate', 'scale'
|
|
|
|
// Events
|
|
controls.addEventListener('change', () => renderer.render(scene, camera));
|
|
controls.addEventListener('dragging-changed', (event) => {
|
|
orbitControls.enabled = !event.value;
|
|
});
|
|
```
|
|
|
|
## Camera Methods
|
|
|
|
```javascript
|
|
// Position and orientation
|
|
camera.position.set(x, y, z);
|
|
camera.lookAt(x, y, z); // or lookAt(vector3) or lookAt(object.position)
|
|
camera.up.set(0, 1, 0); // define "up" direction
|
|
|
|
// Get world direction
|
|
const direction = new THREE.Vector3();
|
|
camera.getWorldDirection(direction);
|
|
|
|
// Screen to world conversion
|
|
const mouse = new THREE.Vector2(x, y); // normalized device coords (-1 to 1)
|
|
const raycaster = new THREE.Raycaster();
|
|
raycaster.setFromCamera(mouse, camera);
|
|
|
|
// World to screen
|
|
const vector = new THREE.Vector3(x, y, z);
|
|
vector.project(camera); // now in normalized device coords
|
|
```
|
|
|
|
## Layers
|
|
|
|
Selective rendering with layers:
|
|
|
|
```javascript
|
|
// Set object layers
|
|
mesh.layers.set(1);
|
|
|
|
// Set camera layers
|
|
camera.layers.enable(0); // render layer 0
|
|
camera.layers.enable(1); // render layer 1
|
|
camera.layers.disable(2); // don't render layer 2
|
|
|
|
// Objects on disabled layers won't be rendered
|
|
```
|
|
|
|
## Frustum Culling
|
|
|
|
Automatic optimization (objects outside view are not rendered):
|
|
|
|
```javascript
|
|
// Manually check if object is in view
|
|
const frustum = new THREE.Frustum();
|
|
const matrix = new THREE.Matrix4().multiplyMatrices(
|
|
camera.projectionMatrix,
|
|
camera.matrixWorldInverse
|
|
);
|
|
frustum.setFromProjectionMatrix(matrix);
|
|
|
|
if (frustum.containsPoint(object.position)) {
|
|
// Object is visible
|
|
}
|
|
```
|
|
|
|
## Multiple Cameras
|
|
|
|
```javascript
|
|
const mainCamera = new THREE.PerspectiveCamera(...);
|
|
const minimapCamera = new THREE.OrthographicCamera(...);
|
|
|
|
// Render with different viewports
|
|
renderer.setViewport(0, 0, width, height);
|
|
renderer.render(scene, mainCamera);
|
|
|
|
renderer.setViewport(width - 200, height - 200, 200, 200);
|
|
renderer.render(scene, minimapCamera);
|
|
```
|
|
|
|
## Resize Handling
|
|
|
|
```javascript
|
|
window.addEventListener('resize', () => {
|
|
// Perspective camera
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
|
|
// Orthographic camera
|
|
const aspect = window.innerWidth / window.innerHeight;
|
|
camera.left = -frustumSize * aspect / 2;
|
|
camera.right = frustumSize * aspect / 2;
|
|
camera.updateProjectionMatrix();
|
|
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
```
|