Starting with ThreeJS

This is a first post about my endevors delving into the world of Javascript and ThreeJS. It is a ‘hello world’ to both javascript and ThreeJS. In this post I will show how to create a simple Rubik’s Cube-like object, you will also learn how to add events to interact with them. To interact with a true-to-life version takes a bit more housekeeping and is out of the scope for this exercise.

I assume that the reader has some 3D background and knows about translation and rotation and pivot points.

Result

You can download the source code here https://github.com/MatzJB/Javascript-experiments/tree/master/ThreeJSCube

Where do we start?

First we need a html file containing a description of the web page we wish to view. To run the code just open index.html in a web browser.

<!doctype html>
<html>
<head>
</head>
<body scroll="no" style="overflow: hidden">
</body>
</html>
[/HTML]

Now, this file is essentially empty, we have no content in the website, just a blank page. So, let's add some content. Start by creating another file, let's call it hello.js, place it in the same directory and put

[sourcecode language="html"]
<script src="./hello.js"></script>

inside the body tags. The javascript file should contain

console.log("yes!")

In Chrome or Firefox you can press Ctrl-Shift I to show the developer’s tools. Now click on ‘console’ inside that window and you should see “yes!” as a printout. The developer’s tools contains many useful utilities such as a profiler which lets you check memory usage and performance of your script, check it out.

Adding content

Now we are ready to add some content to the javascript file. Before we do this we need the threejs library build. The simplest way to add this is to download the build from git and simply put it in your project directory and add it in index.html just as you did with hello.js, see https://github.com/mrdoob/three.js/tree/dev/build

There are quite a lot of things you have to write in order to get something at all. The source code can be found in my github. I suggest that you read that code as the rest of this post progresses.

To create a scene we need a scene object, camera and a renderer. The renderer is later called by the ‘loop’, which is running in the background (the function is called ‘render’).

var scene = new THREE.Scene()
var camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000)
var renderer = new THREE.WebGLRenderer({ antialias: true })

renderer.setClearColor("#ffffff");
renderer.setSize(window.innerWidth, window.innerHeight)

To create the cubes I simply place them using nested for loops.

for (var i = 0; i < numi; i++) {
    for (var j = 0; j < numj; j++) {
        for (var k = 0; k < numk; k++) {
            var cube = new THREE.Mesh(geometry, createMaterial(
                new THREE.Color(i / numi, j / numj, k / numk)))
            cube.position.set(1.02 * i, 1.02 * j, 1.02 * k)
            group.add(cube)
        }
    }
}

When we rotate the group, it will rotate around the world origin, which we don’t want, so to fix this we can create a parent object which we rotate instead and offset the group.

To rotate I use

pivot.rotation.set(x,y,z)

Adding events

To make it interactive, I can add events. In order to do this I simply write the type of event and the function that should run.

document.addEventListener("mousemove", mouseMove, false)
document.addEventListener("mousedown", mouseDown, false)
document.addEventListener("mouseup", mouseUp, false)

When we interact with the cube we want it to spin and slow down. To do this I use global variables to keep track of how much I move while the mouse is being pressed. I put the slowing down code affecting the dx and dy variables inside the render loop. I also avoid calling render when there is nothing to do, so I only put renderer.render(scene, camera) inside the last if statement, when dx or dy is larger than a threshold.

var render = function() {
    requestAnimationFrame(render)
    if (!mouseisDown && (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01)) {
        dx *= 0.95
        dy *= 0.95

        floatingX += dx
        floatingY += dy
    }

    if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
        pivot.rotation.set(floatingY, floatingX, 0)
        renderer.render(scene, camera)
    }
}

That’s about it. You can read the rest of the code and experiment with it. I suggest taking away code and see if it still runs and build your own example.

Conclusions

This is only a first step into the world of 3D on the web. I will post more interactive examples and try out some more advanced scripting using glsl shaders and stuff as I learn them myself.

Suggested software

Git bash https://git-for-windows.github.io for http server.
Visual code https://code.visualstudio.com for coding.

Leave a Reply

Your email address will not be published. Required fields are marked *