Your First ThreeJs Scene: Hello, Cube!

YOUR FIRST three.js SCENE: HELLO, CUBE!

In this chapter, we’ll create a bare-bones three.js app. We’ll introduce quite a bit of theory along the way, but the actual code is short - just 18 lines without the comments.
// Get a reference to the container element that will hold our scene
const container = document.querySelector( '#scene-container' );

// create a Scene
const scene = new THREE.Scene();

// Set the background color
scene.background = new THREE.Color( 'skyblue' );

// Create a Camera
const fov = 35; // AKA Field of View
const aspect = container.clientWidth / container.clientHeight;
const near = 0.1; // the near clipping plane
const far = 100; // the far clipping plane

const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );

// every object is initially created at ( 0, 0, 0 )
// we'll move the camera back a bit so that we can view the scene
camera.position.set( 0, 0, 10 );

// create a geometry
const geometry = new THREE.BoxBufferGeometry( 2, 2, 2 );

// create a default (white) Basic material
const material = new THREE.MeshBasicMaterial();

// create a Mesh containing the geometry and material
const mesh = new THREE.Mesh( geometry, material );

// add the mesh to the scene
scene.add( mesh );

// create the renderer
const renderer = new THREE.WebGLRenderer();

renderer.setSize( container.clientWidth, container.clientHeight );
renderer.setPixelRatio( window.devicePixelRatio );

// add the automatically created <canvas> element to the page
container.appendChild( renderer.domElement );

// render, or 'create a still image', of the scene
renderer.render( scene, camera );
Open up the empty template on Codesandbox.io (or your local equivalent) now, and we’ll get started.

The Components of a Real-Time 3D App


















A basic scene

Every three.js app (in fact, nearly every real-time 3D app) will have the following basic components:
  • Scene - this is a holder for everything else. You can think of it as a “tiny universe” in which all your 3D objects live.
  • Camera - this is directly equivalent to the concept of a camera in the real world, and you’ll use this to view your scene.
  • <canvas> - this is an HTML canvas element, and just like a canvas in the real world, it starts out as a blank rectangle, just waiting to hold your beautiful creations!
  • Renderer - this is a machine that takes a Camera and a Scene as input and outputs beautiful drawings (or renderings) onto your <canvas>.
Together, the SceneCamera<canvas>, and Renderer give us the scaffolding of an app, however, none of them can actually be seen. We’ll also need to add some kind of visible object, and in this chapter, we’ll add a simple box-shaped Mesh, which has three components.

















Mesh Details




























  • Mesh - this is just a holder for a Geometryand a Material and defines the position in 3D space. We can add this to our Scene.
  • Geometry - this defines the shape of our Mesh.
  • Material - and this defines the way that the surface of the Meshlooks.


















Our First three.js App

Now we are finally ready to write some code! We’ll divide our app up into 6 steps:
  1. Initial Setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render The Scene
Every app that you write will always contain at least these basic steps, so you should take some time to make sure that you fully understand them.

1. Initial Setup

  1. Initial Setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render The Scene

Inside index.html we’ve created a container to hold our scene
<div id="scene-container">
  <!-- This div will hold our scene-->
</div>
… and we’ll use querySelector to get a reference to that container in JavaScript
// Get a reference to the container element that will hold our scene
const container = document.querySelector( '#scene-container' );
Even the most basic of apps requires some setup. We already covered the index.html and main.css files in the previous chapter, so we just need to add the JavaScript here. Turn your attention to app.js, which should currently be empty.
In this simple app, our JavaScript for the setup step is a single line. We’ve created an HTML container element to hold our scene and we’ll need to know its width and height when we’re setting up the Camera and Renderer below, so we’ll store a reference to it in the variable container at the top of the file. Once we’ve done so we’ll be able to use it further down in our code.

2. The Scene

  1. Initial setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render The Scene

















The scene


2.1 Create the Scene


Create a scene
const container = document.querySelector( '#scene-container' );

// create a Scene
const scene = new THREE.Scene();

// Set the background color
scene.background = new THREE.Color( 'skyblue' );
First, we’ll create the scene. We’re calling the Scene constructor to create a scene instance, and storing that instance in a variable also called scene.

2.2 Set the Background Color


Set the scene’s background color
const container = document.querySelector( '#scene-container' );

// create a Scene
const scene = new THREE.Scene();

// Set the background color
scene.background = new THREE.Color( 'skyblue' );
We’ve also set the background color to a light blue color. As with the scene, this color was also created using a constructor, in this case, the Colorconstructor, and we’ve passed in as a parameter the word ‘skyblue’, which can be any of the CSS color names.
Aside from writing the name of the color, there are quite a few other ways of defining colors, as we’ll see soon.


















The scene instance that we’ve just created is used to make a data structure called a scene graph. We’ll go over the technical details in of this in Chapter 2.5, but for now, just think of the scene object you have created as a holder for all the visible objects you want to display.
Once we have created an object, if we want to see it we’ll need to add it to the scene using scene.add( object ) and if we later want to remove it from the scene, we can just do scene.remove( object ). Simple!
We’ll need a camera to actually view the scene though. Let’s make one now.

3. The Camera

  1. Initial Setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render The Scene

















The camera

We’ll make the camera in two steps. First, we’ll call the constructor to create a camerainstance, just as we did with the scene in step 2. The only difference is that the camera’s constructor does take several parameters, as we’ll see in just a moment. Next, we’ll need to move the camera back a bit to view the scene, and we’ll take the opportunity to briefly introduce positioning objects in 3D space and the three.js coordinate system.
There are a couple of different cameras available, but we’ll generally stick with the PerspectiveCamera which uses perspective projection to set up a view of the scene.
Without going into any great detail here, perspective projection renders the scene in the way you expect a 3D scene to look - in other words, it mimics the way the human eye sees the world, with objects getting smaller the further away they are from the camera. Nearly all 3D games and special effects in films use a perspective camera.
The other important type of camera is the OrthographicCamera. This usesorthographic projection, which means that objects don’t get smaller as they get further away. This is often used for 2D games, or for user interfaces drawn on top of a 3D game (or 3D website). We’ll investigate both of these camera types in detail in Section 3: Cameras.

















Orthographic and perspective projection


3.1 Create the Camera


Add the following code to create a camera
scene.background = new THREE.Color( 'skyblue' );

// Create a Camera
const fov = 35; // AKA Field of View
const aspect = container.clientWidth / container.clientHeight;

const near = 0.1; // the near clipping plane
const far = 100; // the far clipping plane

const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );

// every object is initially created at ( 0, 0, 0 )
// we'll move the camera back a bit so that we can view the scene
camera.position.set( 0, 0, 10 );
The PerspectiveCamera’s constructor takes four parameters, which together define its viewing frustum.

The Camera’s Viewing Frustum


















A frustum shape
















frustum is a mathematical term meaning a four-sided rectangular pyramid with the top cut off. When we view the scene with our camera, everything inside the frustum is visible, everything outside it is not. In the following diagram, the area in between the Near Clipping Plane and the Far Clipping Plane is the frustum.

















Perspective camera frustum

The four parameters that we passed into the constructor were used to create this shape. Let’s take a look at each of them now.

Field of View (FOV)


















The Field of View angle

The .fov or Field of View parameter defines the angle of the viewing frustum, that is, how much of the world can be seen through the camera. It’s specified in degrees (as we’ll see soon, this is an exception; most angles in three.js are expressed in radians). Another way to think of this is that the .fov parameter defines how much bigger the far clipping plane will be than the near clipping plane. The valid range for the FOV is from 1 to 179 degrees.


















Aspect Ratio


















Aspect ratio examples











The .aspectratio is the width divided by the height of the viewing rectangle. An aspect ratio of 1 will be a square, while window.innerWidth / window.innerHeight will be a rectangle with the same proportions as your current browser window (minus the portion at the top with the URL and browser controls). We’ve created a <container> element to hold our scene, and we’re using the width and height from that to calculate the aspect ratio.


















Clipping Planes


















Clipping Planes

The .near parameter defines the near clipping plane. Objects closer to the camera than .near will not be visible. For a PerspectiveCamera, the near plane must be greater than 0, and less than the .far plane. This defines the smaller end of the frustum pyramid shape in the diagram above.
The .far parameter defines the far clipping plane. Objects further away from the camera than this will not be visible. For a PerspectiveCamera, this can be anything bigger than the .near plane. In practice, to make your scene efficient you will want this to be as small as possible. This defines the larger “base” of the frustum.


















3.2 Position the Camera


We need to move the camera back a bit so that we can see the scene
// Create a Camera
const fov = 35; // AKA Field of View
const aspect = container.clientWidth / container.clientHeight;

const near = 0.1; // the near clipping plane
const far = 100; // the far clipping plane

const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );

// every object is initially created at ( 0, 0, 0 )
// we'll move the camera back a bit so that we can view the scene
camera.position.set( 0, 0, 10 );
By default everything that we create is put at the point (0,0,0). This is called the origin and it’s the point where:
\begin{aligned} x=0 & \cr y=0 & \cr z=0 & \end{aligned}
The camera that we created just now is positioned at (0,0,0), and any objects that we add to the scene will also be positioned at (0,0,0), by default.
This means that we need to move the camera back a bit to let us see the scene. You can also think of the point that the camera is placed at to be the dividing line between your screen, and the 3D scene that you are creating.


















Since this is the first time that we’ve had to take the position of an object into account, let’s take a few moments to understand the three.js coordinate system.

The three.js Coordinate System


















The three.js coordinate system

The above diagram shows the three.js coordinate system, along with the camera we just created, and your screen. The point where the x-axisy-axis, and z-axis meet is (0, 0, 0). As we just mentioned, this is called the origin, and we’ve moved our camera to the point (0, 0, 10) - ten units towards us.
Take note of the direction of the axes relative to the camera and your screen.

















The three.js coordinate system, simple

  • +X points to the right of the screen
  • -X points to the left of the screen
  • +Y points to the top of the screen
  • -Y points to the bottom of the screen
  • +Z points out of the screen (towards you)
  • -Z points into the screen (away from you)
With the line camera.position.set( 0, 0, 10 ), we’ve left the camera in the middle of the x-axis and y-axis, but moved it 10 units towards us along the z-axis.


















4. Visible Objects

  1. Initial Setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render The Scene

















A visible object

We’ve created a camera to see things with, and a scene to put them in. The next step is to create something to see! We’ll start by making a simple white cube.
To do this, we’ll use a kind of object called a Mesh, which consists of three components - a Geometry (or rather a BufferGeometry, but more on that below), a Material, and the Mesh itself, which is a holder for the geometry and material and is used to set the position of the object inside the scene. We’ll make the geometry and material first, and then create the mesh to hold them.

















4.1 Create a Geometry


Add the following line after the camera to create a box-shaped buffer geometry:
camera.position.set( 0, 0, 10 );

// create a geometry
const geometry = new THREE.BoxBufferGeometry( 2, 2, 2 );

// create a default (white) Basic material
const material = new THREE.MeshBasicMaterial();

// create a Mesh containing the geometry and material
const mesh = new THREE.Mesh( geometry, material );

// add the mesh to the scene
scene.add( mesh );
The geometry of an object defines its shape. If we create a box-shaped geometry (like we are doing here), our mesh will be shaped like a box. If we create a sphere shaped geometry, our mesh will be shaped like a sphere. If we create a cat-shaped geometry, our mesh will be shaped like a cat, and so on.
We’re creating our cube using a kind of geometry called a BoxBufferGeometry. The three numbers that we have passed in as parameters define the width, height, and depth of the box, respectively.
Most objects in three.js have built-in defaults, so even though the docs say that BoxBufferGeometry should take 6 parameters, we can get away with ignoring most or all of them and three.js will fill in the blanks with default values.
In this case, if we were to not pass in any parameters: const geometry = new THREE.BoxBufferGeometry() - we would get default box that is 1 x 1 x 1 - one unit high, one unit wide, and one unit deep. That’s a bit small for us now though, so we’re passing in bigger size parameters to create a 2 x 2 x 2 box.


















Now that we’ve created a geometry… ahem, buffer geometry, that is… we’ve given our object a shape.
The next thing we need to do is describe how it looks, and for that, we need a material.

4.2 Create a Material


Add the following line to create the material
// create a geometry
const geometry = new THREE.BoxBufferGeometry( 2, 2, 2 );

// create a default (white) Basic material
const material = new THREE.MeshBasicMaterial();

// create a Mesh containing the geometry and material
const mesh = new THREE.Mesh( geometry, material );

// add the mesh to the scene
scene.add( mesh );
Materials define the surface properties of objects - that is, which material that the object looks like it is made from. Where the geometry tells us that the mesh is a box, or a car, or a cat, the material tells us that it’s a metal box, or a stone car, or a red-painted cat.
There are quite a few materials in three.js. To start with, we’ll create a MeshBasicMaterial, which is the simplest (and fastest) material type available in three.js. It completely ignores any lights in the scene and just shades a mesh based on its color or any texture maps (which we’ll explain in Ch 1.4: A Brief Introduction to Texture Mapping). This is useful since we have not yet added any lights.


















Remember what I said about three.js assigning sensible default values if you don’t provide them yourself? Well, we haven’t passed in any parameters to the material so everything is set to default, which in this case means that the material will be white. We’ll see how to change the material’s color in the next chapter.

4.3 Create a Mesh


















A mesh consists of a geometry and a material

Create a mesh, passing in the geometry and material as parameters
// create a geometry
const geometry = new THREE.BoxBufferGeometry( 2, 2, 2 );

// create a default (white) Basic material
const material = new THREE.MeshBasicMaterial();

// create a Mesh containing the geometry and material
const mesh = new THREE.Mesh( geometry, material );

// add the mesh to the scene
scene.add( mesh );













Next, we need to create a mesh to hold our geometryand material.
The Meshobject is one of the most important and basic objects in three.js and you will be using it a lot. Thankfully, just like the Scene object, it’s very simple and its main function is to hold a geometry and a material and tell us where in the scene they are located.

4.4 Add the Mesh to the Scene


Use the scene’s add() method to attach the Mesh to the scene graph
// create a geometry
const geometry = new THREE.BoxBufferGeometry( 2, 2, 2 );

// create a default (white) Basic material
const material = new THREE.MeshBasicMaterial();

// create a Mesh containing the geometry and material
const mesh = new THREE.Mesh( geometry, material );

// add the mesh to the scene
scene.add( mesh );
We’ll add the mesh to the scene using the scene.add( mesh ) method. If we want to remove it later, we can use scene.remove( mesh ) method.
Once we’ve added the mesh to the scene, the mesh is called a child of the scene, and the scene is called the parent of the mesh.
We can access the geometry and material at any time using mesh.geometry and mesh.material.

5. The Renderer

  1. Initial Setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render The Scene

















The rendered scene outputs to a canvas element

The next step is to create the renderer, which is a machine that takes your scene and camera as inputs and outputs pretty pictures onto the <canvas>element in your HTML page.
Although a number of renderers are in available in three.js, we’ll be concentrating on the WebGLRenderer throughout this book since it’s the most full-featured and powerful.

5.1 Create the Renderer


Create a WebGLRenderer with default settings
scene.add( mesh );

// create the renderer
const renderer = new THREE.WebGLRenderer();

renderer.setSize( container.clientWidth, container.clientHeight );
renderer.setPixelRatio( window.devicePixelRatio );

// add the automatically created <canvas> element to the page
container.appendChild( renderer.domElement );
We’re creating a WebGLRenderer with default settings for now since we’re not passing in any parameters to the constructor. As usual, three.js will take care of setting up sensible defaults for any of the parameters that we do not specify.

5.2 Set the Renderer’s Width and Height


We previously created a container DIV to hold our canvas
<div id="scene-container">
  <!-- This div will hold our scene-->
</div>
..and we set the width and height of the container in CSS, so that it fills the full <body> element.
#scene-container {
  position: absolute;
  width: 100%;
  height: 100%;
}
Once we’ve done that, we can tell the renderer to set the canvas to the same size as the container
// create the renderer
const renderer = new THREE.WebGLRenderer();

renderer.setSize( container.clientWidth, container.clientHeight );
renderer.setPixelRatio( window.devicePixelRatio );

// add the automatically created <canvas> element to the page
container.appendChild( renderer.domElement );
The renderer has automatically created a <canvas> element to draw the scene onto. We’ll add this to our page in a moment, but first, let’s make sure that everything is set up the way that we need it.
The automatically created <canvas> has a default size, although this time it’s set by the browser rather than three.js. Currently on Chrome that default size is 150 x 300 pixels, which is rather small, so let’s tell the renderer the size that we do want.
The approach we’re taking to set up the canvas here is:
  • create an HTML <div class="container"> element to hold the <canvas>
  • set the size of the container using CSS (in this case, we’ve set it to the full window)
  • get a reference to this element in JavaScript as the variable container
  • use the size of the container to set the size of the <canvas>element
The renderer.setSize() method takes care of the final step for us. We just need to pass in the correct width and height (in pixels). container.clientWidth and container.clientHeight give us these values.


















5.3 Set The Renderer’s Pixel Ratio


We need to set the correct pixel ratio for the device our app is running on
// create the renderer
const renderer = new THREE.WebGLRenderer();

renderer.setSize( container.clientWidth, container.clientHeight );
renderer.setPixelRatio( window.devicePixelRatio );

// add the automatically created <canvas> element to the page
container.appendChild( renderer.domElement );
We mentioned back in Ch 0.10: Functions Built-In to Your Browser: the Web APIthat mobile devices draw your web page onto a virtual viewport, which may not have the same resolution as the device’s physical screen. Then, once the page has been drawn, it gets scaled down to fit onto the device’s physical screen. The pixels on this virtual viewport are referred to as CSS pixels, while the real pixels on the actual screen are referred to as physical pixels.
It’s common for the virtual viewport to be much larger than the real viewport - common ratios are 2x, 3x, 4x or even 5x! This allows for much sharper images to be rendered, which is important for viewing text on small screens.
That’s great, but when it comes to rendering WebGL, if we forget to take this into account then our scenes may look great on our development laptops but will look blurry and low-resolution on mobile devices. So, we need to tell the renderer what the pixel ratio is, which is as simple as adding the above line.

5.4 Add the Canvas Element to Our Page

We’ll append the canvas element as a child of the container
// create the renderer
const renderer = new THREE.WebGLRenderer();

renderer.setSize( container.clientWidth, container.clientHeight );
renderer.setPixelRatio( window.devicePixelRatio );

// add the automatically created <canvas> element to the page
container.appendChild( renderer.domElement );
Now we’re finally ready to add the <canvas> to our page.
As we mentioned above, when we created our WebGLRenderer, it automatically created an HTML <canvas> element for us. This element only exists in memory at first and is stored in renderer.domElement. To be able to view our scene, we’ll need to add this <canvas> to our webpage, inside the #scene-container div.
We’ll use a built-in browser method called appendChild to achieve this. Refer back to Ch 0.10: THE WEB API if this method is unfamiliar to you.


















Once we’ve appended the canvas as a child of the scene container, if you take a look at the structure of the HTML in the browser console, you should see that the renderer has indeed appended the <canvas> as a child of the container. It has also set the width and height CSS propertiesand the width and height attributes on the canvas for us.
Assuming a browser window size of 800x600, it should look like this:

<div id="scene-container">
  <!-- This div will hold our scene-->
  <canvas width="1080" height="600" style="width: 1080px; height: 600px;"></canvas>
</div>
Everything is now set up and in place. There remains just one last thing to do, and that is:

6. Render the Scene

  1. Initial Setup
  2. The Scene
  3. The Camera
  4. Visible Objects
  5. The Renderer
  6. Render the Scene



















A rendered scene


Add the following and final line to your code to render the scene
container.appendChild( renderer.domElement );

// render, or 'create a still image', of the scene
renderer.render( scene, camera );






















With this single line, we’re telling the renderer to take a still picture of the scene using the camera and output that picture into the <canvas>element.
You will now see your white cube being rendered against a blue background. It’s a bit hard to see that it’s actually a cube so far since we’re looking at it head-on. But we’ll improve that a lot in the next chapter.
Well done! You’ve completed the first chapter and taken your first giant leap in your career as a three.js developer.

.

Comments

Popular posts from this blog

How to download a file using command prompt (cmd) Windows?

The future of Artificial Intelligence: 6 ways it will impact everyday life

How to Include ThreeJs in Your Projects