Image Image Image Image Image
Scroll to Top

To Top

3D

01

Jun
2016

11 Comments

In 3D
JavaScript
Learning
WebGL

By synergyseeker

Learning WebGL 3D: Starting Textures and Lights

On 01, Jun 2016 | 11 Comments | In 3D, JavaScript, Learning, WebGL | By synergyseeker

We’ve already covered the basics of setting up a scene, a camera, the render loop ( animation loop ) and some other basics about geometry and transformations in previous tutorials. In this lesson, let’s dig into applying textures and materials to “skin” our objects and adding lights, so we can see them.

So we’ll start with a basic scene from previous lessons and add in an object to focus on. I’m making a 16:9 ratio plane so I can add a little video sized texture to it after. I added a camera.lookAt function to ensure the camera is looking at the center of the object instead of straight ahead so the object is centered in the camera’s view. Again, in this example we are using ThreeJS, but the techniques are similar in various 3D libraries.

var scene = new THREE.Scene();
var fieldOfView= 45;
var aspect = window.innerWidth / window.innerHeight;
var nearClippingPlane = .1;
var farClippingPlane = 1000;

var camera = new THREE.PerspectiveCamera( fieldOfView, aspect, nearClippingPlane, farClippingPlane );
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var geometry = new THREE.PlaneGeometry( 16,9 );
var material = new THREE.MeshNormalMaterial();
var obj = new THREE.Mesh( geometry, material );
scene.add( obj );
camera.position.set(0,-2,15);
camera.lookAt(obj.position);

var render = function () {
requestAnimationFrame( render );
renderer.render( scene, camera );
};

render();

Here is what is should look like:

Screen Shot 2016-05-31 at 4.45.43 PM

 

Let’s get it moving around by modifying our code a bit.


var scene = new THREE.Scene();
var fieldOfView= 45;
var aspect = window.innerWidth / window.innerHeight;
var nearClippingPlane = .1;
var farClippingPlane = 1000;

var camera = new THREE.PerspectiveCamera( fieldOfView, aspect, nearClippingPlane, farClippingPlane );
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var obj,rot=0;

// load a texture
createObjects();

function createObjects(){
var material = new THREE.MeshNormalMaterial();
var geometry = new THREE.PlaneGeometry( 16,9,20,20 );
obj = new THREE.Mesh( geometry, material );
scene.add( obj );
camera.position.set(0,-2,15);
obj.rotation.x=Math.PI/180 * 0;
render();
}
function render() {
rot+=.01;
obj.rotation.x = Math.cos(rot)*-.25;
obj.rotation.z = Math.cos(rot)*-.25;
obj.rotation.y = Math.cos(rot)*1;
camera.lookAt(obj.position);
renderer.render( scene, camera );
requestAnimationFrame( render );
};

It should look like this now:

Nice. So we have a basic object and a basic Material on it. That material is a basic one called a NormalMaterial which in this case is cool, because it colors the faces of the object using the normals defined but,does not require and lights to do so. It’s a simple Material choice to quickly see the object we have and get a sense of it in 3D.

There are a number of attributes we can define when creating a Material, things such as color, blending, opacity. Check out the details here to learn more.

A useful attribute of materials is wireframe, so we can see the “structure” of the geometry without a image or other texture over it. You can set “wireframe:true” for most materials to see what I mean. Here is what our scene looks like using wireframe. This is what it looks like on Normal material. Just add {“wireframe”:true} in your material options, like this: var material = new THREE.MeshNormalMaterial({“wireframe”:true});

Screen Shot 2016-06-01 at 10.16.11 AM

Here it is in action on a phong ,material with a texture map applied ( we’re getting to it! )

material = new THREE.MeshPhongMaterial({map: mat, side:THREE.DoubleSide, wireframe:true });

And here is an example applying to a sphere, with a texture iamge / map as well.

Screen Shot 2016-06-01 at 10.14.47 AMOk, so let’s replace that Normal Material with an image or Texture file. Materials can come in a lot of varieties, but there are 2 main ones we’ll focus on for applying image or video based textures. Lambert and Phong. Lambert is a flatter one and great if the texture you have is flat or has reflectiveness baked in. Phong is reflective and really shows of the light. You can set how light reflects as well, in terms of color, to give a more metallic shine or more matte / look, somewhere in between also makes it look like plastic. So playing with the attributes can get some really cool looks.

So let’s add a material with a image texture, give it a Phong material type to make it kind of shiny, but keep the defaults to it’s kind of matte.
We’ll add 2 lights as well, which I’ll explain a bit after as well. We will need lights if we are using materials other than normal or similar materials that don’t require light to be seen.


var scene = new THREE.Scene();
var fieldOfView= 45;
var aspect = window.innerWidth / window.innerHeight;
var nearClippingPlane = .1;
var farClippingPlane = 1000;

var camera = new THREE.PerspectiveCamera( fieldOfView, aspect, nearClippingPlane, farClippingPlane );
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var material, obj,rot=0;

// create lights
var ambient = new THREE.AmbientLight( 0xffffff,.5);
ambient.position.set( 0, 0, 0 );
scene.add(ambient);

var directionalLight = new THREE.DirectionalLight( 0xffffff,1 );
directionalLight.position.set( 10, 10, 10 );
scene.add(directionalLight);

// load a texture
var tl = new THREE.TextureLoader();
tl.load("assets/mat.jpg",function(mat){
material = new THREE.MeshPhongMaterial({map: mat, side:THREE.DoubleSide });
createObjects();
});

function createObjects(){
var geometry = new THREE.PlaneGeometry( 16,9 );

obj = new THREE.Mesh( geometry, material );
scene.add( obj );
camera.position.set(0,-2,15);
obj.rotation.x=Math.PI/180 * 0;
render();
}
var render = function () {
rot+=.01;
obj.rotation.x = Math.cos(rot)*-.25;
obj.rotation.z = Math.cos(rot)*-.25;
obj.rotation.y = Math.cos(rot)*1;
camera.lookAt(obj.position);
renderer.render( scene, camera );
requestAnimationFrame( render );
};

You should now have something like this, using your own image as the source of the map. ( rip mine if you want to :).

To see it larger go here.
Cool. So we have a material on our object. If you used your own material, you may need to play with the size of it and the dimensions of your plane, to get it to look nice. I used a roughly 16:9 sort of size image and plane to minimize any stretching. Something important to note, is that ideally you should size your materials using the power of 2 rule. So 64, 128, 512, 1024, 2048 etc should be used as your width and height. They don’t have to be square, but should use those dimensions. If not ThreeJS will typically convert the image to that size anyway. It is much more efficient for processing when images use these dimensions. Complex objects with material maps, generally are sized like this already coming out of 3D applications.
We also used a Loader. You could roll your own in JS super quick, but ThreeJS comes with a bunch already included, so I used the TextureLoader for convince. Read more about here.

Lights

In the above example, we used an Ambient light and a Directional light. The ambient light can really be positioned anywhere and added to the scene, we can adjust the color, or set it as white if we don’t want to alter the colors of the textures. The directional light is positioned a little back between the camera and the object and a bit to the side to give it a little interest as the object rotates around.

Screen Shot 2016-06-01 at 10.24.35 AMThere are a variety of lights in 3D libraries and tools, but most are similar across the various tools. Lights shine a light in a direction or all directions, adding light to our object increasing the amount of “light” in the form of color to our objects faces. The “normals” as defined but the object indicate the angle that the light is cast pint he face. So that means we can typically indicate a level of intensity of the light source and color. Some lights change depending on how close they are to objects ( closer increases intensity ), just like real world lights. Other lights are more ambient in nature or simply work in a direction, regardless of distance.
CjJVXpDWYAAD43q

Typically it is nice to add an ambient light source if your scene is say outdoors as and you want to cast a base level light on all things. If you want to cast shadows you’ll need something with a direction like a spot light or directional light. In ThreeJS, you now separate the shadow light from other lights, so you often are making 2 lights in the same position, pointing the same direction, one to handle “light” and one to handle shadows cast both internally and externally.

 

Cju0UBPWkAEOHUc

Light seems simple but it can mean everything to how a scene looks.Light and shadows really make the 3D , the depth of materials and objects come alive. I’ll circle back more to lights in a future tutorial, but experiment with them, adding a few, moving them around, to get a feel for how they affect your scenes.

Keep asking great questions, and let me know what you’d like explore. You can follow what I’m up to on Twitter as well.  Next up we’ll look at dynamically changing textures using a canvas as a source, and how to use this to make animated textures.

FacebookTwitterGoogle+