Course outline
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.
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
backgroundandfog. - renderer
- The object that draws a scene, as a camera sees it, into a
<canvas>.WebGLRendereruses 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: trueuses 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
Turn antialiasing off
In the first example set
antialias: falseand 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
Count before and after
Add a second cube to the first example before
renderand run it. The line after render now printscalls 2 | triangles 24: one draw call and 12 triangles per box. The line before render still printscalls 0: nothing is counted untilrender()runs.
Code & diagrams
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.
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: 2None of these stops the scene. The messages are copied from a real run; Topic 3.4 explains the first one.
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: ffffffBreak 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.
Break #2
Swap the two arguments
In the second example's last lines, replace renderer.render(scene, camera) with renderer.render(camera, scene).
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 aslow-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
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
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),fogandenvironment. Everything you want drawn must be inside it, directly or through a parent (Topic 3.5). - 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 forrender()to use it. - 4
The renderer owns a
<canvas>(renderer.domElement) and a WebGL 2 connection to the GPU. Its options are requests, not orders.antialias: trueasks for smoothed edges (multisampling: more memory and GPU time). In r186 the context is always created with an alpha channel, andalphaonly sets the default clear alpha: 1 forfalse, 0 fortrue. Soalpha: truestill gives a see-through canvas, andalpha: falsegives an opaque one because every frame starts cleared to alpha 1 (the first example prints both numbers).powerPreferencehints which GPU a two-GPU laptop should use. - 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.callscounts the last render's draw calls. - 6
Console messages are part of the API. three.js reports survivable mistakes with
console.warnorconsole.errorand goes on: addingundefined(a model that failed to load) logs an error and adds nothing; an unknown colour name warns and gives white;new THREE.Clock()warns becauseTHREE.Timerreplaced it (Topic 3.2). Read the console first.
Explain it without notes
What do the scene, the camera and the renderer each do? What happens if one is missing?
Why can renderer.info.render.calls be 1 while the canvas shows nothing?
Practice
Add a THREE.FogExp2 to the first example's scene and a second, farther cube. What do the status bar numbers do?
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,alphaandpowerPreferencereally request in r186Read
renderer.info.renderbefore and afterrender()Recognise the three common console messages and fix their causes