Tuesday, October 8, 2013

Adding Lights with Three.js (part 3)

At this point, we have our green cube responding correctly to an off-screen light source that is moving up and down.  In this final part of "Adding Lights with Three.js", we will learn how to create a yellow sphere (representing the Sun) and place it where the light is coming from.  Then we will pull back the camera a bit so we can see the cube being lit while the yellow Sun is moving up and down, all in the same view.

First let's see how to make a yellow sphere with Three.js.  Here's how it looks:  
var sphereGeometry = new THREE.SphereGeometry(5);
var sphereMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(255,255,0)' });
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
In the first line, we name a variable 'sphereGeometry' that will hold the design or blueprint of the sphere shape.  We fill this variable using the equals sign ( = ) and a call to THREE.SphereGeometry();  This function takes a number of optional parameters inside its parentheses.  For now, we will only use the most important one, which is size.  This size parameter represents how big the sphere's radius will be.  I arbitrarily chose '5' units for the radius.  You can try other values if you like and see how it affects the scene.  
On the next line we name a variable 'sphereMaterial' which will hold the information about the sphere's material or skin.  Since the Sun itself does not have a shadow, but rather glows bright yellow on all sides, we can just use 'MeshBasicMaterial' (which has no shading) that we used a while back for the green cube.  For the rgb color parameter, we use '255,255,0' which is the rgb code for the brightest yellow. 
The final 2 lines above should look familiar.  We create a mesh variable called 'sphere' that will hold the combination of the Sun's shape (sphereGeometry) and skin (sphereMaterial).  The var 'sphere' now represents everything we need to know about the Sun object.  One last thing to do - add the Sun to our scene.  That is accomplished by typing 'scene.add(sphere);'. 

Inside the animate() loop we will set this newly created sphere to be located exactly where the light source is located.   We do this by typing:
sphere.position = light.position; 
That's pretty intuitive right? Now our Sun sphere will move exactly with the light position as it moves up and down. 

Before we try out this example in the browser,  I want to tell you about some minor changes that I made to make all of the objects fit inside the view at the same time.   First I pulled the camera back so we can see the Sun go all the way up.  So now the camera.position.z variable is set to 160 units (+Z goes behind you which is what we want).  Also, in order to see the Sun sphere rise all the way to the top, I moved the camera up as well.  Remember that the +Y axis goes up.  So now we set the camera.position.y to 40 units.
Here's how the new camera position lines look now:
camera.position.y = 40;
camera.position.z = 160;
One last minor change:  when pulling the camera back like this, I found I couldn't see the green cube very well anymore - it was too small in the distance.  So I just increased the original dimensions of the cube to 20 units on all sides (it was 1 before).   

Well, that covers all the changes.  Copy and Paste the following code and then save it as 'tempGame.js' , overwriting the old one:   

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var cubeGeometry = new THREE.CubeGeometry(20,20,20);
var cubeMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(0,255,0)' });
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
scene.add(cube);

var sphereGeometry = new THREE.SphereGeometry(5);
var sphereMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(255,255,0)' });
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);

var light = new THREE.PointLight( 'rgb(255,255,255)', 1, 0 );
scene.add(light);

var sunRiseFlag = true;
var sunHeight = 0;

camera.position.y = 40;
camera.position.z = 160;

cube.rotation.x = 0.4;
cube.rotation.y = 0.6;

animate();

function animate(){
   
   requestAnimationFrame(animate);

   if(sunRiseFlag == true){
      sunHeight = sunHeight + 1;
   }

   if(sunRiseFlag == false){
      sunHeight = sunHeight - 1;
   }

   if(sunHeight > 150){
      sunHeight = 150;
      sunRiseFlag = false;
   }

   if(sunHeight < 0){
      sunHeight = 0;
      sunRiseFlag = true;
   }

   light.position.set(50,sunHeight,50);
   sphere.position = light.position;

   renderer.render( scene, camera );

}

Once you have saved it, go back to your example01.html file and open with your Chrome browser.  If you saved everything correctly, you should see our green cube, now a little farther away, being lit up by the rising and setting Sun sphere object.  This little demo proves that the light is indeed coming from exactly where the yellow sphere is located, which is physically accurate.

Hopefully you are starting to get the hang of creating new Three.js objects, whether they be cubes, spheres, or lights.  We will return to lighting later in this blog series.  But in the next posts, we are going to add some code that will re-size and re-center our view correctly, in case the user suddenly changes their browser window dimensions.  We will also take a look at timing and the Three.js Clock object for handling faster vs. slower computers efficiently, so that everyone,  regardless of their system that they are running our future games on, will have a good experience.

Stay tuned!