News

Create an interactive 3D interface using WebGL

WebGL is hardware accelerated, native 3D graphics for web browsers, and here we show you how to harness it’s power to create a 3D interface.

webgl8002

WebGL has been around for a little while now, and the three.js library is really coming of age with each new release. WebGL on the whole is incredibly complicated, with mounds of code needed just to get a polygon to display. The three.js library abstracts all of that and gives you access to simple JavaScript commands that allow you as a web designer or developer to create stunning 3D scenes in the browser. The library now fully supports the COLLADA model format, which is an XML-based model, so is easy for browsers to read. This format also supports animation so feel free to incorporate fully animated content from your 3D application in your browser.

In this tutorial we are going to import a model in the COLLADA format and add interactivity to models contained within the overall COLLADA scene. We will attach mouse events to these models, which will give us effectively a 3D interface for a website or application. At present WebGL is supported by all the major browsers except Internet Explorer, which has no plans to support it because WebGL is based on OpenGL (the open graphics library) and Microsoft use DirectX instead. However, there are plug-ins available that will render WebGL. Please note that some browsers can only load models if they’re accessed via a web server.

Getting started

It’s probably a good idea to start by downloading three.js from GitHub (github.com/mrdoob/three.js). Have a look through the projects to get an idea about WebGL. Now copy the start folder to the desktop and open index.html in Dreamweaver. There is some CSS already in there, so add this code to the head section.

001 <meta name="viewport"
content="width=device-width, user-scalable=no,
minimum-scale=1.0, maximum-scale=1.0">
002 <      link href='http://fonts.googleapis.com/
css?family=Merriweather:700' rel='stylesheet'
type='text/css'>

Body content

We’re going to add some text to the project, so paste the code shown below into the body section of the page. This adds a heading and a box that will only be shown when we roll over various models in the 3D WebGL scene with the cursor. Change the text here, to have some real content.

001  <div id="header">   < h1>   Discovery Centre</h1></div>
002 <      div class="info"
id="mountain">   <      h2>   Mountain Valley<      /h2>
003 <      p>   Your text here.<      /p>   <      /div>

Copy the info

Copy the entire div with the class info and paste it after itself twice. Change the id of the first copy to ‘lake’ and the second to ‘forest’. These are the two content sections which are hidden to start with, but when the user rolls over points in the scene, this content will be made visible.

001 <      div class="info" id="forest">   <      h2>   Forest
Trails<      /h2>
002 <      p>   Your text here.<      /p>   <      /div>

Link to libraries

Keeping inside the body tag add the following lines, which link to the JavaScript libraries that we use. The first is the main three.js library, while the second is a library for loading COLLADA models (an XML 3D model file). The detector code is used to see if the user has WebGL, while the remaining two are extensions to add events to 3D objects.

001 <      script src="js/Three.js">   <      /script>
002 <      script src="js/ColladaLoader.js">   <      /
script>
003 <      script src="js/Detector.js">   <      /script>
004 <      script src='js/threex.domevent.js'>   <      /
script>
005 <      script src='js/threex.domevent.object3d.
js'>   <      /script>

Detecting WebGL

Using newer technology like WebGL requires you to ensure that the user knows if there is a problem. While most web browsers support WebGL, IE has no intention of doing so. There are plug-ins available for IE though. Add this code to detect WebGL.

001 <      script>
002 if ( ! Detector.webgl ) Detector.
addGetWebGLMessage();

Adding variables

This is perhaps not one of the most exciting parts of our code, but these variables hold many important features that make our WebGL application work. Scene, camera and renderer are the basics required to get a WebGL scene. Obviously we then need content to make the scene actually work.

001 var scene, camera, renderer, model,
ground, rot;
002 var rotate=true, over=false;
003 var loader = new THREE.ColladaLoader();
004 var mnt, mbox, lk, lbox, fr, fbox;

Load the scene

The next block of code that we add loads the 3D COLLADA model into our scene. The scene is stored in our model variable that we’ll use later on. The scene is scaled down, and then two functions are called. First we call the init function, to initialise our scene, and then after that we call the animate function – this will make it move for us.

001  loader.load( 'scene.dae', function (
collada ) {
002 	model = collada.scene;
003 	model.scale.x = model.scale.y =
model.scale.z = 0.125;
004 	init();
005 	animate();
006 } );

Initializing the scene

To start our project we will create the init function. The first line creates a new div, which is then placed into the body. This will later be used to render our scene. The next line sets up a new scene that will hold everything we need to add, such as lights, models and a camera.

001 function init() {
002	var container = document.createElement(
'div' );
003	document.body.appendChild( container );
004	scene = new THREE.Scene();

Creating the camera

The camera is a virtual way of controlling what we actually look at. It takes three arguments, the field
of view, the aspect ratio and how near and far it can see. Here we position the camera and tell it which direction to look, which as you can see is almost the centre of the scene.

001 camera = new THREE.PerspectiveCamera( 40,
window.innerWidth / window.innerHeight, 0.01,
1000 );
002 camera.position.set( -45, 12, 15 );
003 camera.lookAt( new THREE.Vector3( -1, 2,
4 ) );
004 scene.add( camera );

Adding interactivity

We add interactivity to the models by using a plug-in from www.learningthreejs.com. The first line of code enables the camera to look at which model is under the mouse. The next line gets the ground plane from the COLLADA model. We can then add mouse events for when the mouse is over or has left, leading us to turn on and off the rotate variable causing the scene to rotate or not.

001 THREE.Object3D._threexDomEvent.
camera(camera);
002 ground = model.getChildByName( 'ID276',
 true );
003 ground.on('mouseout', function(){
rotate=true;
004 });
005 ground.on('mouseover', function(){
006 	rotate=false;
007 });

Mountain point

The first line of the code grabs the mountain information point from the COLLADA model and stores it in the variable mnt. The mountain text in the div tag with the id mountain is stored in the variable mbox. When the user moves the mouse out from the model it sets the mbox to be invisible.

001 mnt = model.getChildByName( 'ID43', true );
002 mbox = document.getElementById("mountain");
003 mnt.on('mouseout', function(){
004 	mbox.style.visibility = "hidden";
005 	over=false;
006 });

Mouseover mountain

When the user rolls their mouse over the mountain information point, the relevant text becomes visible in the mountain div tag. In this way we create content that becomes visible when we rollover parts of the 3D model, and it disappears when the mouse moves out. The variable over is set to true to stop the whole model rotating.

001 mnt.on('mouseover', function(){
002 	mbox.style.visibility = "visible";
003 	over=true;
004 });

More interactivity

Similar to what we did in step 11, we store the lake information point model in the variable lk and the div tag named lake in lbox. When the cursor moves out from the lake information point we turn the visibility of the lake div tag off so it can’t be seen in the display.

001 lk = model.getChildByName( 'ID25', true );
002 lbox = document.getElementById("lake");
003 lk.on('mouseout', function(){
004 	lbox.style.visibility = "hidden";
005 	over=false;
006 });

Going over the lake

When the user moves the mouse over the lake information point, we again use this to turn on the visibility of the lake div tag so that the content connected with it becomes visible on the screen. As
you can see this is very similar to creating mouse events in the regular DOM.

001  lk.on('mouseover', function(){
002 	lbox.style.visibility = "visible";
003 	over=true;
004 });

Accessing the last model

You have probably got the hang of this by now, and so in this code we again access the model for the forest information point this time and store it in the fr variable. We store the associated div in the fbox variable. Using those variables we turn the visibility of the text off when we roll the mouse out of the model.

001 fr = model.getChildByName( 'ID34', true );
002 fbox = document.getElementById("forest");
003 fr.on('mouseout', function(){
004 	fbox.style.visibility = "hidden";
005 	over=false;
006 });

Final rollover

Finally we add the rollover for the forest information point. As done previously, we check if the mouse is over the model and turn on the visibility of the text so that the right information is displayed as we role over the model. This concludes the interactive features of the site.

001  fr.on('mouseover', function(){
002 	fbox.style.visibility = "visible";
003 	over=true;
004 });

Lighting up the scene

Our next section of code actually adds the COLLADA model into the scene so that it can be displayed. We then create an ambient light that has a dark grey colour and full luminosity, which is the 1 in the brackets. This light is also added to our scene. We do not have to position this as it just gives ambient light to the whole scene.

001 scene.add( model );
002 var ambient	= new THREE.AmbientLight(
0x333333,1 );
003 scene.add( ambient );

Add a spotlight

Now the code sets up a spotlight; this can be placed in a position and targeted to light specifically. This is given a warm, almost white colour and is set to be stronger than 1. The light is set to the same position as the camera and is pointing towards the centre of the scene. Finally this light is added to the scene.

001 var light = new THREE.SpotLight( 0xFFDDDD,
1.2 );
002 light.position = camera.position;
003 light.target.position.set( 0, 0, 0 );
004 scene.add( light );

Rendering the scene

To make the scene visible we need to have a renderer to actually draw the scene. Notice how the renderer sets the anti-aliasing of the objects to true to give softer edges to the models. The render size is set to the inside dimensions of the browser window. The renderer is added to the container we added in step 8.

001 renderer = new THREE.WebGLRenderer( {
antialias: true } );
002 renderer.setSize( window.innerWidth,
window.innerHeight );
003 container.appendChild( renderer.
domElement );
004 }

Create the animation

The animate function contains the code that moves models on the screen. The if statement checks that the user hasn’t got the mouse over the model and not over one of the information points. It then rotates the scene. Otherwise it slows the rotations down so it’s easy to hold the mouse over the information points.

001  function animate() {
002 	if(rotate==true && over==false){
003 		rot=0.005;
004 	} else if (rotate==false || over==true){
005		rot=rot*0.95;
006	}

Update the display

The final code here updates the model position with the value of the rot variable. The renderer is told to render the scene using the camera. The animate function is set to be called at 60 frames per second using the requestAnimationFrame function. The function and the code block is closed.

001 model.rotation.y+=rot;
002 renderer.render( scene, camera );
003 requestAnimationFrame( animate );
004 }
005 <      /script>

Test the project

We have now finished all the code for the project, so save the file and test it in your browser. The main scene will rotate; if you move the cursor over the scene it will stop rotating by slowing down gradually. If you move your cursor over one of the pink information points, you will get the appropriate information appear in a box.

×