Command Palette

Search for a command to run...

Hectal
Course outline
Topic 3.1Beginner ~19 min · step 1 of 7

Scene, Camera and Renderer

In one line

Every three.js app has three parts: a scene that holds things, a camera that decides what is seen, and a renderer that draws it. You learn the renderer's options, what render() sends to the GPU, and the console warnings you will meet.

0/7 · 0%

Think of it like this

A film set. The scene is the set with everything on it. The camera is the camera on its tripod: it decides what is in the shot. The renderer is the film lab that turns what the camera sees into a picture. You can have several cameras for one set, and a camera is useless without a set to point at.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

scene
The root of the tree of objects to draw, plus scene-wide settings such as background and fog.
renderer
The object that draws a scene, as a camera sees it, into a <canvas>. WebGLRenderer uses WebGL 2.
frustum culling
Skipping objects that are completely outside the camera's view, so the GPU never gets a draw call for them.
antialiasing
Smoothing jagged (stair-stepped) edges. antialias: true uses multisampling, which costs GPU memory and time.

Step by step

01Make the three parts

new THREE.Scene(), new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100) and new THREE.WebGLRenderer({ ... }). The camera needs a position and a direction (position.set, lookAt); at (0, 0, 0) looking down −z it would sit inside whatever you put at the origin.

02Choose the renderer's options

Options go in one object when the renderer is created: antialias, alpha, powerPreference. They become the context attributes of the canvas's WebGL connection, which are fixed for the life of the canvas. To change one you need a new canvas.

03Size it and put it on the page

renderer.setSize(width, height) sets the canvas size, and document.body.append(renderer.domElement) (or your container's append) puts it on the page. A canvas that is not on the page still draws, but nobody sees it.

04Call render()

renderer.render(scene, camera) draws one frame. Before it renderer.info.render.calls is 0 and frame is 0; after it they are 1 and 1 (the example prints both). The screen shows nothing new until you call it again.

Try it yourself

  1. 1

    Turn antialiasing off

    In the first example set antialias: false and press Run. The status bar numbers stay the same (1 draw call, 12 triangles): antialiasing costs GPU memory and time, not draw calls. Look closely at the cube's edges: with it off they can show stair steps. On a phone at pixel ratio 3 the steps are too small to notice, which is why many teams turn it off there (Topic 3.3).

  2. 2

    Count before and after

    Add a second cube to the first example before render and run it. The line after render now prints calls 2 | triangles 24: one draw call and 12 triangles per box. The line before render still prints calls 0: nothing is counted until render() runs.

Code & diagrams

Create the three parts and look at what you got New tab

The values come from this scene's own renderer. The `alpha` line shows the r186 behaviour: the context has alpha, the default clear alpha is 1.

idle

Console

Run the scene to see its console.

Expected console output

before render: calls 0 | frame 0
after render: calls 1 | triangles 12 | frame 1
context alpha: true | clear alpha: 1 | depth: true
WebGL version: 2
Three small mistakes, three console messages New tab

None of these stops the scene. The messages are copied from a real run; Topic 3.4 explains the first one.

idle

Console

Run the scene to see its console.

Expected console output

THREE.Object3D.add: object not an instance of THREE.Object3D. undefined
THREE.Color: Unknown color orang
THREE.Clock: This module has been deprecated. Please use THREE.Timer instead.
children in the scene: 1
cube colour: ffffff

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Render with the camera inside the object

In the first example change camera.position.set(2, 1.5, 3); to camera.position.set(0, 0.5, 0); and remove the lookAt line.

terminal
$ Run
── what you'll see ──
calls 1 | triangles 12 (the status bar), but the canvas shows only the dark background
No error and no warning. The draw call really happens.

Break #2

Swap the two arguments

In the second example's last lines, replace renderer.render(scene, camera) with renderer.render(camera, scene).

terminal
$ Run
── what you'll see ──
THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.
Logged as a console error; nothing is drawn, and the page keeps running.

Myth vs fact

Myth

alpha: false takes the alpha channel away from the canvas.

Fact

Not in r186. The context is always created with alpha (the example prints context alpha: true with alpha: false). The option only sets the default clear alpha, so the canvas looks opaque because each frame is cleared to alpha 1. With no scene.background set, renderer.setClearAlpha(0) makes it see-through again.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    A context's attributes are fixed when it is created, and the browser may ignore a request. In our test browser (a software GPU) a context created with powerPreference: "high-performance" reads back as low-power: always read back what you got.

  • ▸

    render() is not free even for an empty scene: it updates matrices, sorts and sets GL state. Topic 3.2 skips it when nothing changed.

Remember this

  1. 1

    The problem this solves: in Phase 0 you copied three lines without choosing anything. A real project must choose: smooth edges or not, a see-through canvas or not, which GPU, when to update. Knowing what each choice costs keeps the showroom cool.

  2. 2

    The scene (new THREE.Scene()) is the root of a tree of objects. It draws nothing by itself. It also holds scene-wide settings: background (a colour or a texture), fog and environment. Everything you want drawn must be inside it, directly or through a parent (Topic 3.5).

  3. 3

    The camera is an object with a position and a turn. PerspectiveCamera(fov, aspect, near, far) takes the vertical field of view in degrees, the width ÷ height of the picture, and the nearest and farthest distances it sees (together they form the frustum, the pyramid-shaped box of space the camera sees; Phase 7 draws it). It need not be in the scene for render() to use it.

  4. 4

    The renderer owns a <canvas> (renderer.domElement) and a WebGL 2 connection to the GPU. Its options are requests, not orders. antialias: true asks for smoothed edges (multisampling: more memory and GPU time). In r186 the context is always created with an alpha channel, and alpha only sets the default clear alpha: 1 for false, 0 for true. So alpha: true still gives a see-through canvas, and alpha: false gives an opaque one because every frame starts cleared to alpha 1 (the first example prints both numbers). powerPreference hints which GPU a two-GPU laptop should use.

  5. 5

    **render(scene, camera) does one frame: it updates every object's world matrix (Topic 2.5), skips objects outside the camera's view (frustum culling), sorts what is left (opaque near to far, transparent far to near) and sends one draw call** per mesh to the GPU. Nothing is drawn until you call it. renderer.info.render.calls counts the last render's draw calls.

  6. 6

    Console messages are part of the API. three.js reports survivable mistakes with console.warn or console.error and goes on: adding undefined (a model that failed to load) logs an error and adds nothing; an unknown colour name warns and gives white; new THREE.Clock() warns because THREE.Timer replaced it (Topic 3.2). Read the console first.

Explain it without notes

01

What do the scene, the camera and the renderer each do? What happens if one is missing?

02

Why can renderer.info.render.calls be 1 while the canvas shows nothing?

Practice

01

Add a THREE.FogExp2 to the first example's scene and a second, farther cube. What do the status bar numbers do?

02

Write the one line that tells you the WebGL version your renderer really got.

Done when you can

  • Name what the scene, camera and renderer each do

  • Say what antialias, alpha and powerPreference really request in r186

  • Read renderer.info.render before and after render()

  • Recognise the three common console messages and fix their causes