Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions docs/user-manual/graphics/cameras/camera-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: Camera Controls
description: Add orbit, fly, and pan navigation to any camera with the engine's ready-made camera-controls script, with mouse, touch, and gamepad support.
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

Most applications need some way for the user to move the camera. Rather than writing this from scratch, you can use the `CameraControls` script that ships with the engine at [`scripts/esm/camera-controls.mjs`](https://github.com/playcanvas/engine/blob/main/scripts/esm/camera-controls.mjs). It provides production-quality navigation with a single script:

* **Orbit** — left mouse drag rotates around a focus point, right mouse drag pans, and the mouse wheel zooms.
* **Fly** — WASD keys move the camera freely while the mouse looks around.
* **Touch and gamepad** — multi-touch gestures and gamepad input are supported out of the box.

Attach the script to a camera entity:

<Tabs groupId="workflow" defaultValue="engine">
<TabItem value="engine" label="Engine">

```javascript
// Load the script (here from the CDN; you can also bundle it with your app)
const asset = new pc.Asset('camera-controls', 'script', {
url: 'https://cdn.jsdelivr.net/npm/playcanvas/scripts/esm/camera-controls.mjs'
});
app.assets.add(asset);
app.assets.load(asset);

asset.ready(() => {
// Attach it to the camera entity
camera.addComponent('script');
camera.script.create('cameraControls', {
properties: {
focusPoint: new pc.Vec3(0, 1, 0)
}
});
});
```

If you build your app with a bundler, you can import the script class directly instead:

```javascript
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';

camera.addComponent('script');
camera.script.create(CameraControls);
```

</TabItem>
<TabItem value="editor" label="Editor">

Add `camera-controls.mjs` to your project as a script asset (copy it from the [engine repository](https://github.com/playcanvas/engine/blob/main/scripts/esm/camera-controls.mjs)). Then select your camera entity, add a [Script Component](/user-manual/editor/scenes/components/script) and add the **cameraControls** script to it. The script's attributes can then be tuned in the Inspector.

</TabItem>
<TabItem value="react" label="React">

```jsx
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
import { Script } from '@playcanvas/react/components';

<Entity name="camera" position={[0, 1, 4]}>
<Camera />
<Script script={CameraControls} />
</Entity>
```

</TabItem>
<TabItem value="web-components" label="Web Components">

```html
<pc-app>
<pc-asset src="https://cdn.jsdelivr.net/npm/playcanvas/scripts/esm/camera-controls.mjs"></pc-asset>
<pc-scene>
<pc-entity name="camera" position="0 1 4">
<pc-camera></pc-camera>
<pc-scripts>
<pc-script name="cameraControls"></pc-script>
</pc-scripts>
</pc-entity>
</pc-scene>
</pc-app>
```

</TabItem>
</Tabs>

## Configuration {#configuration}

The script exposes a rich set of attributes. The most commonly used are:

| Attribute | Default | Description |
| --- | --- | --- |
| `enableOrbit` | `true` | Enable orbit controls |
| `enableFly` | `true` | Enable fly controls |
| `enablePan` | `true` | Enable panning |
| `focusPoint` | `[0, 0, 0]` | The point the camera orbits around |
| `rotateSpeed` | `0.2` | Rotation sensitivity |
| `moveSpeed` | `10` | Fly movement speed (with `moveFastSpeed` and `moveSlowSpeed` variants) |
| `zoomSpeed` | `0.001` | Zoom sensitivity |
| `pitchRange` | `[-360, 360]` | Limits for the camera's pitch angle in degrees |
| `zoomRange` | `[0.01, 0]` | Min/max zoom distance (a max of 0 means unlimited) |

Damping attributes (`rotateDamping`, `moveDamping`, `zoomDamping`, `focusDamping`, all defaulting to `0.98`) control how smoothly motion eases out — 0 stops instantly. To create a pure orbit camera, disable fly; for a pure fly camera, disable orbit.

## Examples {#examples}

See the script in action in the engine examples:

<EngineExample id="camera/orbit" title="Orbit Camera" />

<EngineExample id="camera/fly" title="Fly Camera" />

<EngineExample id="camera/multi" title="Multi Camera" />

For building your own custom camera logic instead, the [Orbit Camera tutorial](/tutorials/orbit-camera) walks through a complete implementation.
29 changes: 29 additions & 0 deletions docs/user-manual/graphics/cameras/clearing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: Clearing
description: Control how a camera clears its render target — set the background color, make the canvas transparent, or disable clearing entirely.
---

Before a camera renders the scene, it clears its render target — the screen, unless a [render target](multiple-cameras.md#render-targets) is assigned. You can control what gets cleared and to what color:

```javascript
camera.camera.clearColor = new pc.Color(0, 0, 0);

camera.camera.clearColorBuffer = true; // clear the color buffer (default: true)
camera.camera.clearDepthBuffer = true; // clear the depth buffer (default: true)
camera.camera.clearStencilBuffer = true; // clear the stencil buffer (default: true)
```

The clear color is effectively the background color of your scene — though if your scene has a skybox, it covers the clear color entirely.

## Transparent Backgrounds {#transparent-backgrounds}

The clear color has an alpha channel, and the application's canvas supports transparency by default. This means a single camera can render the scene over the surrounding web page — useful for 3D product views, headers, and other embedded content:

```javascript
// The web page shows through wherever nothing is drawn
camera.camera.clearColor = new pc.Color(0, 0, 0, 0);
```

## Disabling Clearing {#disabling-clearing}

Disabling the clear flags keeps the existing contents of the render target, which is the building block for compositing several cameras into one image — see [Camera Stacking](multiple-cameras.md#camera-stacking).
78 changes: 61 additions & 17 deletions docs/user-manual/graphics/cameras/index.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,82 @@
---
title: Cameras
description: Add cameras, choose orthographic or perspective projection, and configure viewports for split-screen and picture-in-picture.
description: Create cameras and explore projection, tone mapping, multi-camera rendering, camera controls, and coordinate conversion.
---

Cameras are responsible for rendering a scene to the screen. You need at least one camera in your scene to see anything. When you create a new scene in PlayCanvas, it is automatically populated with a single camera (along with a directional light).
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

## Creating a Camera
Cameras render your scene to the screen. A camera is simply an entity with a [CameraComponent](https://api.playcanvas.com/engine/classes/CameraComponent.html) attached — the scene is drawn from the entity's position and orientation, so you aim a camera by moving and rotating its entity just like any other. Cameras look down their local negative Z axis.

In the Editor's 3D View, an unselected camera is represented with the following icon:
You need at least one enabled camera in your scene to see anything. Beyond that single camera, there is a lot you can control: the [projection](projection.md) that maps the 3D scene to a 2D image, the [tone mapping](tone-mapping.md) that shapes the final colors, and how [multiple cameras](multiple-cameras.md) compose views for split-screen, overlays, and render-to-texture.

![Camera icon](/img/user-manual/graphics/cameras/camera-icon.png)
## Creating a Camera {#creating-a-camera}

To create a new camera, simply create a new entity and add a camera component to it. For convenience, the Editor menu has an item that does this in a single step:
<Tabs groupId="workflow" defaultValue="engine">
<TabItem value="engine" label="Engine">

```javascript
// Create an entity with a camera component
const camera = new pc.Entity('Camera');
camera.addComponent('camera', {
clearColor: new pc.Color(0.3, 0.3, 0.7)
});
app.root.addChild(camera);

// Aim the camera by transforming its entity
camera.setPosition(0, 5, 10);
camera.lookAt(0, 0, 0);
```

</TabItem>
<TabItem value="editor" label="Editor">

New scenes are automatically populated with a camera entity. To create another, use the Entity menu, which creates an entity with a [Camera Component](/user-manual/editor/scenes/components/camera) in a single step:

![Camera creation](/img/user-manual/graphics/cameras/camera-create.png)

## Orthographic vs Perspective Projection
All camera properties can then be edited in the Inspector.

</TabItem>
<TabItem value="react" label="React">

Cameras can have one of two types of projection: orthographic or perspective. Orthographic cameras define a parallel projection and are often used for 2D or isometric games.
```jsx
<Entity name="camera" position={[0, 5, 10]}>
<Camera clearColor="#4d4db3" />
</Entity>
```

![Orthographic camera](/img/user-manual/graphics/cameras/camera-orthographic.png)
See the [`<Camera/>` component reference](/user-manual/react/api/camera) for all available props.

More commonly used is the perspective projection. It more closely mimics how our eyes or cameras work.
</TabItem>
<TabItem value="web-components" label="Web Components">

![Perspective camera](/img/user-manual/graphics/cameras/camera-perspective.png)
```html
<pc-entity name="camera" position="0 5 10">
<pc-camera clear-color="0.3 0.3 0.7 1"></pc-camera>
</pc-entity>
```

## Controlling the Viewport
See the [`<pc-camera>` tag reference](/user-manual/web-components/tags/pc-camera) for all available attributes.

By default, a camera will render to the full width and height of its render target. However, there are circumstances where you might want to change this behavior. For example, perhaps you are writing a game that has a local multiplayer mode that requires split-screen rendering to show each player's viewpoint.
</TabItem>
</Tabs>

For 2-player horizontal split screen, you would create two cameras and configure their viewports as follows:
## In This Section

![Horizontal splitscreen](/img/user-manual/graphics/cameras/camera-horizontal-splitscreen.png)
* [Projection](projection.md) — perspective vs orthographic projection, field of view, clip planes and frustum culling.
* [Clearing](clearing.md) — set the background color, make the canvas transparent, or disable clearing.
* [Tone Mapping & Exposure](tone-mapping.md) — map HDR scene lighting to your display, with optional physical exposure controls.
* [Multiple Cameras](multiple-cameras.md) — compose views with priorities, viewports, layers and render targets.
* [Camera Controls](camera-controls.md) — add orbit, fly and first-person navigation with the engine's ready-made script.
* [Screen and World Coordinates](screen-and-world.md) — convert between 2D screen positions and 3D world positions.
* [Scene Picker](scene-picker.md) — accurately select the objects under a screen coordinate.
* [Depth Layer](depth-layer.md) — give shaders access to the scene's color and depth buffers.

And for vertical split screen, you would configure the viewports as follows:
## Going Further

![Vertical splitscreen](/img/user-manual/graphics/cameras/camera-vertical-splitscreen.png)
* **Post-processing** — bloom, depth of field, SSAO, TAA, vignette and more are applied per camera. See [Post Effects](/user-manual/graphics/posteffects/).
* **AR and VR** — a camera can drive an immersive WebXR session via [`startXr()`](https://api.playcanvas.com/engine/classes/CameraComponent.html#startxr). See the [XR section](/user-manual/xr/).
* **Per-camera fog** — override the scene's fog settings on an individual camera with [`fog`](https://api.playcanvas.com/engine/classes/CameraComponent.html#fog).
* **Custom projections** — supply [`calculateProjection`](https://api.playcanvas.com/engine/classes/CameraComponent.html#calculateprojection) and [`calculateTransform`](https://api.playcanvas.com/engine/classes/CameraComponent.html#calculatetransform) callbacks for advanced effects such as oblique projections and planar reflections.
* **Tutorials** — try [Camera Following a Path](/tutorials/camera-following-a-path) and [Orbit Camera](/tutorials/orbit-camera).
103 changes: 103 additions & 0 deletions docs/user-manual/graphics/cameras/multiple-cameras.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: Multiple Cameras
description: Compose views from several cameras using priorities, viewports, layers, and render targets for split-screen, overlays, and more.
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

A scene can contain any number of cameras, and they compose into the final image according to a simple model: every enabled camera renders its [layers](#layers) into its [viewport](#viewports) of its render target (the screen, unless a [render target](#render-targets) is set), in order of `priority` — lower values render first.

This model supports a wide range of setups: split-screen multiplayer, picture-in-picture overlays such as minimaps and rear-view mirrors, UI rendered over the 3D scene, and live render-to-texture surfaces like security monitors and portals.

<EngineExample id="graphics/multi-view" title="Multi View" />
Comment thread
willeastcott marked this conversation as resolved.

## Viewports {#viewports}

By default, a camera renders to the full width and height of its render target. The `rect` property restricts rendering to a rectangle, specified as `[x, y, width, height]` in normalized 0–1 coordinates (the origin is the bottom-left corner).

For 2-player horizontal split-screen, two cameras each take half the screen:

![Horizontal splitscreen](/img/user-manual/graphics/cameras/camera-horizontal-splitscreen.png)

<Tabs groupId="workflow" defaultValue="engine">
<TabItem value="engine" label="Engine">

```javascript
// Player 1: left half of the screen
camera1.camera.rect = new pc.Vec4(0, 0, 0.5, 1);

// Player 2: right half of the screen, rendered after camera1
camera2.camera.rect = new pc.Vec4(0.5, 0, 0.5, 1);
camera2.camera.priority = 1;
```

</TabItem>
<TabItem value="editor" label="Editor">

Select each camera in the Hierarchy and set its **Viewport** (X, Y, Width, Height) and **Priority** in the [Camera Component](/user-manual/editor/scenes/components/camera).

</TabItem>
<TabItem value="react" label="React">

```jsx
<Entity name="player1Camera">
<Camera rect={[0, 0, 0.5, 1]} />
</Entity>
<Entity name="player2Camera">
<Camera rect={[0.5, 0, 0.5, 1]} priority={1} />
</Entity>
```

</TabItem>
<TabItem value="web-components" label="Web Components">

```html
<pc-entity name="player1-camera">
<pc-camera rect="0 0 0.5 1"></pc-camera>
</pc-entity>
<pc-entity name="player2-camera">
<pc-camera rect="0.5 0 0.5 1" priority="1"></pc-camera>
</pc-entity>
```

</TabItem>
</Tabs>

For vertical split-screen, stack the viewports instead — `[0, 0.5, 1, 0.5]` on top and `[0, 0, 1, 0.5]` below:

![Vertical splitscreen](/img/user-manual/graphics/cameras/camera-vertical-splitscreen.png)

A related property, `scissorRect`, clips rendering to a rectangle in the same normalized format without changing how the image is projected into the viewport.

## Layers {#layers}

Each camera renders only the [layers](/user-manual/graphics/layers) listed in its `layers` property, so different cameras can see entirely different subsets of the scene. Typical uses include a UI camera that renders only a UI layer over the game, a minimap camera that skips effects layers, and first-person weapon rendering. See the [Camera Model Masking tutorial](/tutorials/camera-model-masking) for a worked example.

## Camera Stacking {#camera-stacking}

When one camera renders on top of another — a picture-in-picture overlay, or a full-screen camera drawing a different set of layers — the later camera (higher `priority`) must not wipe out the earlier camera's image. Disable its [clear flags](clearing.md) as appropriate:

```javascript
// Render an overlay camera on top of the main view, in the bottom-right corner
overlay.camera.priority = 1;
overlay.camera.rect = new pc.Vec4(0.7, 0, 0.3, 0.3);
overlay.camera.clearColorBuffer = true; // overlay has its own background
overlay.camera.clearDepthBuffer = true; // don't depth-test against the main view

// For a full-screen overlay that composites over the main view instead:
// overlay.camera.clearColorBuffer = false;
```

## Render Targets {#render-targets}

Instead of the screen, a camera can render into an offscreen texture by assigning a [RenderTarget](https://api.playcanvas.com/engine/classes/RenderTarget.html) to its `renderTarget` property. The resulting texture can then be applied to a material — for in-world screens, mirrors and portals — or processed further. See the engine's render-to-texture example:

<EngineExample id="graphics/render-to-texture" title="Render to Texture" />

## Performance Considerations {#performance-considerations}

* Every enabled camera renders its layers again — draw calls scale with the number of cameras. Restrict each camera's `layers` to the minimum it actually needs.
* Reducing a camera's `rect` reduces the pixels it fills, but not the per-draw-call CPU cost of the objects it renders.
* Per-camera [post-processing](/user-manual/graphics/posteffects/) runs once per camera, so effects on split-screen views multiply their GPU cost.
* Render targets that are only needed occasionally (e.g. a static mirror) don't have to be updated every frame — disable the camera and enable it on demand.
Loading