\( \newcommand{\vecIII}[3]{\left[\begin{array}{c} #1\\#2\\#3 \end{array}\right]} \newcommand{\vecIV}[4]{\left[\begin{array}{c} #1\\#2\\#3\\#4 \end{array}\right]} \newcommand{\Choose}[2]{ { { #1 }\choose{ #2 } } } \newcommand{\vecII}[2]{\left[\begin{array}{c} #1\\#2 \end{array}\right]} \newcommand{\vecIII}[3]{\left[\begin{array}{c} #1\\#2\\#3 \end{array}\right]} \newcommand{\vecIV}[4]{\left[\begin{array}{c} #1\\#2\\#3\\#4 \end{array}\right]} \newcommand{\matIIxII}[4]{\left[ \begin{array}{cc} #1 & #2 \\ #3 & #4 \end{array}\right]} \newcommand{\matIIIxIII}[9]{\left[ \begin{array}{ccc} #1 & #2 & #3 \\ #4 & #5 & #6 \\ #7 & #8 & #9 \end{array}\right]} \)

CS307: Introduction to Three.js Programming

Plan

  • (Very high-level) summary of reading
  • I'll answer your quiz questions
  • A simple scene with a box
    • Use the orbiting camera interface
    • Keyboard commands
    • Add a sphere to the scene
    • Use the JavaScript console for debugging
    • (breakout exercise) Enhance the box scene
  • Building a geometry from scratch
  • Save and load a copy of the Barn demo
  • Exercise: Local library files
  • (breakout exercises) Changing width, two barns

Summary

We will use three APIs:

  • WebGL, a subset of OpenGL (we won't actually see much of it)
  • Three.js, a JavaScript library built on top of WebGL
  • TW, a home-grown set of conveniences and shortcuts created by Scott Anderson

Graphics are drawn on a canvas on the webpage

As shown in the skeleton code below for the scene with a box, you need to create:

  • a scene object
  • stuff to look at, added to the scene object
  • a renderer object
  • a camera object
var scene = new THREE.Scene();                // scene object
var boxGeom = new THREE.BoxGeometry(4,6,8);   // stuff to look at
var boxMesh = TW.createMesh(boxGeom);
scene.add(boxMesh);
var renderer = new THREE.WebGLRenderer();     // renderer object
TW.mainInit(renderer,scene);
var state = TW.cameraSetup(renderer,          // camera object
                           scene,
                           {minx: -2.5, maxx: 2.5,
                            miny: -3.5, maxy: 3.5,
                            minz: -4.5, maxz: 4.5});

 

The "stuff to look at" are meshes that consist of geometry and material

  • Geometry is comprised of vertices and faces (triangles in Three.js)
  • Vertices of triangular faces are specified in counterclockwise order, as viewed from the front

Quiz Questions

I'll answer your quiz questions

So Let's Get Started...

  • Visit this simple scene with a box and save the page to your Desktop as box.html. You can do this by selecting the File>Save Page As... menu item and specifying the file name as box.html and Format as "Webpage, HTML Only".
  • In a new tab, visit the local file from your browser:
    • File>Open File...
    • Navigate to Desktop
    • Choose box.html

The URL will be something like file:///Users/youraccount/Desktop/box.html

Alternatively, you can start the local web server. This is not necessary at this point in the course, but it can't hurt. It will be necessary later when we want to dynamically load images for texture-mapping, so I decided to teach it right away. But you can do either one.

Using the Orbiting Camera Interface

The TW package creates a Three.js Orbiting Camera for the scene, which makes it easy to view your model.

  • Clicking and dragging with the mouse left/right moves the camera around the model (orbits it), keeping a constant distance from the center. You can also drag up/down but that stops at the poles.
  • The arrow keys can be used to shift the camera to the left, right, up, or down — this also changes the center of the scene, so that dragging with the mouse then rotates the model around a different point. (Note: in the current version of the OrbitControls code, the up/down arrow keys do not work properly.)
  • Scrolling your mouse lets you dolly in/out.

Keyboard Commands

TW also defines some keyboard commands:

  • A "?" prints a list of single-letter commands to the terminal window
  • The letter "a" toggles an axis helper that shows you the orientation of the model (where the X (red), Y (green) and Z (blue) axes are). The axes emanate from the origin of the coordinate system.
  • The letter "b" toggles a visualization of the scene bounding box. (This is the bounding box used to set up the camera, not the true bounding box of the scene.)
  • The lowercase axis letters view the model from that direction: "x" "y" "z"
  • The letter "o" (for "oblique") views the model from an oblique direction.

Code for the Simple Box

Open the box.html code file in a text editor on your local machine and view the code. These slides capture the essential parts of the code, which we'll review in class.

Now let's add another object using the built-in geometry, THREE.SphereGeometry, with an input that specifies the radius of the sphere:

var sphereGeom = new THREE.SphereGeometry(3);
var sphereMesh = TW.createMesh(sphereGeom);
scene.add(sphereMesh);

Save your modified code file and reload the webpage in your browser (you may want to hold down the shift key while reloading the page). hmmmm.... where is our sphere? Hint: Use your mouse to move the camera inside the box!

Let's move the sphere outside the box so we can see it better:

sphereMesh.position.set(5,0,0);  // x,y,z coordinates of the center

Use the JavaScript Console for Debugging

Our main debugging tool for JavaScript will be the JavaScript Console. To open the JS console in your browser (the process may be different on a PC):

  • Chrome: Enter command-option-j or select View>Developer>JavaScript Console (enter control-shift-j on a PC)
  • Safari: If you do not see the Develop menu along the top of the browser window, first go to Safari>Preferences. Select the Advanced tab, and then check the Show Develop menu in menu bar item at the bottom. In the Develop menu, you can then select Show JavaScript Console (shortcut: command-option-c) (this may be control-shift-c on a PC)
  • Firefox: Enter command-option-k or select Tools>Web Developer>Web Console (control-shift-k on a PC)

Try introducing an error in your code file, reload your webpage, and view the error message(s) in the JS console.

Add a print statement to your code with console.log() — also handy for debugging!

var name = 'Scott';
var age = 29;
console.log("Hi " + name + ", you're really only " + age + "??");

Breakout Exercise: Be Creative!

For this exercise, I'll divide you into breakout groups. Using the box and sphere geometries, and the ability to position your meshes anywhere in the scene, work together to build on the box.html code file to create a new scene of your own design. Be careful about the size of your objects and coordinates — you might need to change the coordinates specified in the bounding box in order to see everything (e.g. minx, maxx, miny, maxy, minz, maxz). Imagine things like a lollipop, barbell, stack of blocks, toy, snowman, robot, ...

Pairs:

  • 2 Annabel+Faith
  • 4 Aizah+Ingrid
  • 6 Lan+Martha
  • 8 Ari+Hae Rin
  • 10 Gabby+Genevieve
  • 12 Anastacia+Elisha
  • 14 Amy+UGyeong
  • 16 Ailie+Emily
  • 18 Joyce+Kate
  • 20 Ariana+Lindsay
  • 22 Claire+Rik
  • 24 Heidi+Marilyn
  • 26 Alo+Hana
  • 28 Claire (ILL)

Building a Geometry From Scratch

Step 1: Create a new THREE.Geometry object and define its vertices:

      

var barnGeometry = new THREE.Geometry();
// add vertices to the barnGeometry.vertices array -
// indices of this array correspond to the above labels 
barnGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
barnGeometry.vertices.push(new THREE.Vector3(30, 0, 0));
barnGeometry.vertices.push(new THREE.Vector3(30, 40, 0));
...

 

Step 2: Divide each object surface into triangular faces, and define each face using three vertex labels (indices of the barnGeometry.vertices array).

Keep in mind:

  • each triangular face has a front and back side
  • by default, Three.js only renders the front side (the back side is invisible)

So how do you define which side is the front?

  • imagine you're looking at the side you want to define as the front side...
  • ... and list the three vertices in a counterclockwise order, as viewed from this side
  • Three.js will interpret this side as the front side

      

// front side of each of these faces is outside the barn
barnGeometry.faces.push(new THREE.Face3(0, 1, 2));
barnGeometry.faces.push(new THREE.Face3(0, 2, 3));
barnGeometry.faces.push(new THREE.Face3(3, 2, 4));
...

 

How would you specify the face outlined in red, if you want the outside of the barn to be visible?

Let's look at the code to create the barn

In case you're wondering how faces look when the vertices are specified in the "wrong" order:

   

Here is the code that created this unfortunate barn, which also provides a sneak preview on how to create a mesh with desired colors, and how to control which side is rendered.

Set up the Barn Code

  • Visit the barn and save the page to your Desktop as barn.html. You can do this by selecting the File>Save Page As... menu item and specifying the file name as barn.html and Format as "Webpage, HTML Only".
  • In a new tab, visit the local file from your browser:
    • File>Open File...
    • Navigate to Desktop
    • Choose barn.html

The URL will be something like file:///Users/youraccount/Desktop/barn.html

Working Without a Network

View the source of the file you downloaded to your Desktop. Note that it starts by loading three JavaScript files from the web, all from /~cs307/threejs/libs.

  • three-r95.all.js, version 95 of the Three.js code
  • OrbitControls-r95.js, which is the extra code for the orbiting camera
  • tw-sp21.js, which is the Spring 2021 version of our TW code. This file contains the TW.createBarn() function.

What if you have a laptop, hiking in the White Mountains on a glorious fall day, far from any internet connection, and you have a sudden inspiration to do some graphics coding? What do you do then?

First, try sitting down to view the beauty of nature, wait for the feeling to pass, and remind yourself that there's more to life than work...

If that doesn't work, you can hope that you had the foresight to have a a local copy of those files. If they're on your laptop, you can make the appropriate changes to your program file, and load the local copies instead.

Exercise: Using Local Library Files

(Do this exercise individually, on your own computer.)

You can "manually" download the necessary library files to your local machine and modify the source code to use the local files, but here we'll use a shortcut. Again save the code for the barn using the File>Save Page As... menu item, but this time save it as "Webpage, Complete" and use a different name, like barnComplete. You'll see both an HTML file and a folder (barnComplete_files) on your Desktop. Load the new HTML file into your browser and view the source. What changed in the code statements that load the library files?

Breakout Exercises: Modify the Barn

Exercise: Changing Width

This is a relatively quick exercise, to get you warmed up.

  1. Copy the original barn.html to another file, say wide-barn.html.
  2. Edit the new file to change the width of the barn (barnWidth), say to 50.
  3. View the changed wide-barn.html file in your browser. Is this what you expected?
  4. The variable name barnWidth is supplied in the call to the TW.createBarn() function, and is also used to specify maxx for the bounding box in the later call to TW.cameraSetup(). In both places, change the variable name (barnWidth) to numeric constants. Use a larger value in the call to TW.createBarn() relative to the value chosen for maxx.

    What do you notice about the wide barn in this case?

    • You might have something like this wide-barn.html. In this sample solution, the barn is wide (50) but maxx for the camera is only 20, so the camera setup is off. The original code allowed us to change the width of the barn in just one place and the camera setup changed automatically. This is a good reason to use variables instead of numeric constants!
  5. Change the numeric constants back to the variable barnWidth. The bounding box for the original barn truncates some of the roof of the barn from view. Modify the value for maxy in the bounding box, using an expression with variable names, so that the entire roof is visible. (Hint: In the createBarn() function, how is the y coordinate of the roof of the barn defined?)

Earlier, you adjusted the position of meshes using position.set():

sphereMesh.position.set(5,0,0);  // x,y,z coordinates of the center

In Three.js, an instance of Mesh (actually, an instance of Object3D, which is the parent class of Mesh) has a property called position that is a Vector3, which has a method set(x,y,z) that can be used to set the components of the vector. This will come in handy for the next exercise.

Exercise: Two Barns

Modify the code from the previous exercise to add a second barn that is

  1. half the size of the first barn
  2. shifted to the left of the first barn, leaving a gap between the two barns

Adjust the bounding box so that you can see the two barns in their entirety. Your result might look something like this:

Optional: Add more barns to your scene, with different sizes.

A possible solution: two-barns.html

General Coding Tips

  • Build and test your code incrementally. Save often!
  • Save versions by saving the file to a different filename (ex1.html, ex2.html, ...) It will be easier (emotionally) to experiment with things if you know you can go back to an earlier version.
  • Be willing to create a simple test program to see how something works without all the complexity of your larger program.
  • Be modular, and document as you go. It'll be easier to understand and debug your own code.

To Do for the Next Class