Get started with Three.js

In this fifth tu­to­rial, you’ll learn how to load com­plex 3D mod­els in WEBGL us­ing Three.js

Web Designer - - Contents -

In the fi­nal part of the se­ries, learn how to load com­plex 3D mod­els in WEBGL.

Webgl en­ables de­vel­op­ers to cre­ate rich, con­sole-qual­ity ex­pe­ri­ences, that ren­der in real time on mo­bile de­vices and desk­top browsers. Nearly univer­sal browser and de­vice sup­port makes it a per­fect ap­proach for web de­vel­op­ers want­ing to cre­ate in­cred­i­ble ex­pe­ri­ences. No plug­ins are re­quired and you can start learn­ing these tech­nolo­gies right away.

One of the ques­tions that comes up the most of­ten about 3D de­vel­op­ment is about im­port­ing mod­els. It’s of­ten con­fus­ing try­ing to choose the right for­mat and load­ing it cor­rectly. You can ac­com­plish a lot with prim­i­tives, but very of­ten you want to in­te­grate mod­els cre­ated in 3D mod­el­ing soft­ware.

You’ll be us­ing the popular 3D li­brary Three.js in this se­ries. It’s free, open source and light­weight, and count­less award-win­ning web­sites have used it.

Build­ing on pre­vi­ous tu­to­ri­als, you’ll move onto learn­ing about load­ing com­plex mod­els in your 3D scenes: What for­mats are sup­ported, which are the best to use, and why. Plus, where to get mod­els and how to get them into your scenes to start us­ing them in 3D. Other than hav­ing a Javascript back­ground, you can dive into this tu­to­rial with no prior knowl­edge and get some great re­sults. The goal is to get you started in 3D web pro­gram­ming and to get you in­spired.

1. Choose your mod­els

To fol­low along, you’ll need an OBJ for­mat model and it’s cor­re­spond­ing tex­ture(s). You’ll also need a gltf model as well. Here are two great sources for down­load­ing mod­els:

• tur­

• sketch­­els?fea­tures=down­load­able&so rt_by=-like­count&type=mod­els

We’ve used the Pony Car­toon, by Slava Z, which you can buy or try out here: sketch­­els/885d9 f60b3a9429b­b4077c­fac5653cf9

2. Cre­ate a ba­sic HTML file

Next, you need to set up a ba­sic HTML file. You can setup ex­ter­nal CSS and Javascript files or in­clude inline for sim­plic­ity. Three.js’s ‘ren­derer’ class will cre­ate a <can­vas> el­e­ment for you. Add the fol­low­ing code to your ‘in­dex.html’ file.

3. In­clude Three.js classes

In­clude a link to the Three.js li­brary in the <head> of your file, ei­ther hosted ex­ter­nally or down­load it from the Three.js repos­i­tory. You will also need the ‘Or­bit­con­trols’, ‘Ob­jloader’ and ‘Glt­floader’ classes for this tu­to­rial. You can find both the li­brary and the sup­port­ing classes at­doob/three.js/. Note: The code in this tu­to­rial has been tested on the lat­est re­lease of Three.js v95.

<script src=”libs/three.min.js”></script> <script src=”libs/or­bit­con­trols.js”></script> <script src=”libs/ob­jloader.js”></script> <script src=”libs/glt­floader.js”></script>

4. Add global vari­ables

Be­tween your <script> tags for your code, add the fol­low­ing global vari­ables to glob­ally ac­cess the cam­era, scene, ren­ders, loaded ob­ject and the Or­bit con­trols. In­stead of writ­ing your own in­ter­ac­tiv­ity, you’ll use the built in Or­bit con­trols in this tu­to­rial:

// global vars var cam­era, scene, ren­derer, ob­ject, con­trols;

5. Cre­ate a 3D scene

You’re go­ing to add a ba­sic 3D scene, which will be the con­tainer for your ob­jects. The scene is the stage that will ren­der with the cam­era. All 3D pre­sen­ta­tions will have a scene or stage of some form. What is in that stage and in view of the cam­era is what the user will see. Add the fol­low­ing code to add a scene:

// cre­ate a scene ob­ject var scene = new THREE.SCENE();

6. Add a per­spec­tive cam­era

Next, you need to add a cam­era. You’ll use the per­spec­tive cam­era, meant for 3D scenes. The first at­tribute is the field of view of the cam­era. The sec­ond is the as­pect ra­tio (width:height). Then you in­di­cate the near clip­ping plane and far clip­ping plane dis­tances, which de­fine what is to be vis­i­ble to the cam­era. You’ll also push the cam­era back in Z space a lit­tle to make things things eas­ier to see.

// cre­ate cam­era cam­era = new Three.per­spec­tive­cam­era( 75, win­­ner­width / win­­ner­height, 1,

2000 ); cam­era.po­si­tion.z = 15; scene.add( cam­era );

7. Add a ren­derer and can­vas

The ‘ren­derer’ han­dles the draw­ing of the ob­jects in your scene that are vis­i­ble to the cam­era. Set the ‘an­tialias’ prop­erty to ‘true’ to get smooth edges on our ob­ject. The ren­derer cre­ates a ‘domele­ment’, which is ac­tu­ally an HTML <can­vas> el­e­ment, you can then ap­pend to the body. Try set­ting your ren­derer’s ‘gam­maout­put’ to ‘true’ for op­ti­mal sup­port of gltf mod­els.

// cre­ate ren­derer ren­derer = new Three.we­bglren­derer({ an­tialias:true}); ren­derer.set­pix­el­ra­tio( win­dow. de­vi­cepix­el­ra­tio ); ren­derer.set­size( win­­ner­width, win­dow. in­ner­height ); ren­derer.gam­maout­put = true; doc­u­ment.body.ap­pend­child( ren­derer.domele­ment );

8. Add or­bit con­trols

To keep things sim­ple, you can use Three.js’s built-in ‘or­bit con­trols’ class. This en­ables you to drag the cam­era around in an ‘or­bit’ around the tar­get. You at­tach the con­trols to the cam­era and then set a tar­get for it to or­bit.

// add or­bit con­trols con­trols = new Three.or­bit­con­trols( cam­era ); con­trols.tar­get.set( 0, 0, 0 ); con­trols.up­date();

9. Add lights to the scene

To light your loaded mod­els, a bal­ance of am­bi­ent and point or di­rec­tional lights can work well. OBJ mod­els do not have lights, but gltf scenes can in­clude them. If your cho­sen model in­cludes lights then you can ei­ther omit them later on, or use them as ad­di­tional light­ing. // add am­bi­ent light var am­bi­ent­light = new­bi­ent­light(

0xffffff, .2 ); scene.add( am­bi­ent­light );

// add point light var point­light = new Three.point­light(

0xf­fcc66, 0.6 ); cam­era.add( point­light );

10. Load en­vi­ron­ment map

Mod­els of­ten ben­e­fit from en­vi­ron­ment maps. These maps are ‘sky­boxes’ that sur­round the ob­ject so it can af­fect it from all di­rec­tions ac­cu­rately, im­pact­ing the colour and in­ten­sity of the colour on the sur­face tex­ture. A great Cube Maps re­source can be found on the Hu­mus site (hu­­dex.php?page=tex­tures).

Add the fol­low­ing code to load your map:

// cre­ate en­vi­ron­ment map var en­vmap = new­be­tex­tureloader() .set­path( ‘as­sets/’)

.load( [ ‘posx.jpg’, ‘negx.jpg’, ‘posy.jpg’, ‘negy.jpg’, ‘posz.jpg’, ‘negz.jpg’ ] );

// set as skybox scene.back­ground = en­vmap;

Note: the or­der of the cube map im­ages is im­por­tant, so be sure to fol­low the above pat­tern when set­ting yours.

11. Cre­ate a load­ing man­ager and han­dlers

You will be load­ing the OBJ for­mat model first. It is a much sim­pler model, but needs more steps than a gltf model. Start by adding a ‘Load­ing­man­ager’ class and han­dlers for progress and com­ple­tion. Use these to time the next step and when to show your model. For now we’re go­ing to keep things sim­ple.

// load­ing man­ager var man­ager = new Three.load­ing­man­ager(); man­ager.on­progress = func­tion ( item, loaded, to­tal ) {

con­sole.log( item, loaded, to­tal ); };

// load com­plete man­ager.on­load = func­tion ( ) {

con­sole.log( “fin­ished load­ing mod­els & tex­tures” ); };

12. Set up tex­tures ar­ray

For some mod­els, such as OBJ, you may want to man­u­ally as­sign the tex­tures to the loaded model. Set up an ar­ray of tex­tures for as many as you have for your model. Not all will have these, but you’ll want at least one pri­mary tex­ture to use as the de­fault ‘map’.

// tex­tures tex­tures = []; tex­ture­as­sets = [

{“file”:”as­sets/sword/ger­man_bas­tard_ Sword_d­if­fuse.jpg”,”name”:”dif­fuse”},

{“file”:”as­sets/sword/ger­man_bas­tard_ Sword_spec­u­lar.jpg”,”name”:”spec­u­lar”},

{“file”:”as­sets/sword/ger­man_bas­tard_ Sword_nor­malmap.jpg”,”name”:”nor­mal”}


13. Load tex­ture files

You can use the ‘Imageloader’ class to load in the im­age files and then as­sign them to your tex­tures ar­ray. This gives you an easy way to ac­cess all your loaded im­ages, so you can as­sign them to your loaded OBJ model. It also con­nects to the load­ing man­ager you set up. Add the fol­low­ing code next:

// as­sign each loaded tex­ture to a tex­ture ob­ject tex­ture­as­sets.fore­ach(func­tion(t){

tex­tures[] = new THREE. Tex­ture(); var loader = new Three.imageloader(

man­ager ); loader.load( t.file, func­tion ( im­age ) { tex­tures[].im­age = im­age; tex­tures[]. need­sup­date = true;

}); });

14. Load OBJ Model

Next, load in the OBJ file you want to use. You can use the ‘Ob­jloader’ class to do this and con­nect it to your load­ing man­ager, like you did for the ‘Imageloader’. Once it’s loaded you will be able to work with the re­sul­tant 3D ob­ject that is passed to the func­tion.

Add the fol­low­ing code:

// load OBJ Model var loader = new THREE.OB­JLOADER( man­ager ); loader.load( ‘as­sets/sword/ger­man_bas­tard_ Sword.obj’, func­tion ( obj ) {

// han­dle loaded file here

// ad­just po­si­tion and scale });

15. Han­dle the loaded OBJ

Inside your ‘han­dler’ func­tion, you can as­sign the tex­tures to the loaded OBJ. Your loaded ob­ject may be com­prised of mul­ti­ple meshes, de­pend­ing on the model. You need to ‘tra­verse’ the ob­ject and find the mesh you wish to as­sign the tex­tures to. You can check if a child is an in­stance of a mesh. You could also check against its ‘name’ prop­erty.

// han­dle loaded file here con­sole.log(ob­ject); ob­ject=obj; ob­ject.tra­verse( func­tion ( child ) { if ( child in­stanceof THREE.MESH ) { // as­sign tex­tures here } });

16. As­sign ma­te­rial to tar­get mesh

Once you have the mesh lo­cated, you can as­sign its ‘ma­te­rial’ prop­erty as usual, us­ing the tex­tures in your tex­tures ar­ray. If your OBJ also has an as­so­ci­ated MTL file you could use the Three.js Mtl­loader and as­sign tex­tures us­ing that. It some­times needs ad­just­ments to work nicely, so we’ll use this more straight­for­ward method here. Add the fol­low­ing code next:

// as­sign tex­tures here­te­rial = new THREE. Mesh­phys­i­cal­ma­te­rial({ map:tex­tures[“dif­fuse”], spec­u­larmap: tex­tures[“spec­u­lar”], nor­malmap:t ex­tures[“nor­mal”],en­vmap: en­vmap});

17. Set ob­ject scale and po­si­tion

Once you have your OBJ loaded and the ma­te­ri­als as­signed, you are ready to add it to the 3D scene. You’ll most likely need to scale your model and po­si­tion it where you can see it. This is go­ing to vary de­pend­ing on the model you use. You’ll need to up­date ‘scale’ and ‘po­si­tion’, and ‘ro­ta­tion’. Add the fol­low­ing code af­ter your ‘tra­verse’ func­tion:

// ad­just po­si­tion and scale ob­ject.po­si­tion.set(5,0,0); ob­­ta­tion.z=math.pi/180*70; ob­ject.scale.set(.25,.25,.25); scene.add( ob­ject );

18. An­i­ma­tion ren­der loop and ob­ject ro­ta­tion

Next, you need to ren­der your scene. You learned about the ren­derer in pre­vi­ous tu­to­ri­als. It ren­ders a frame inside the an­i­ma­tion loop run­ning at a tar­get of 60 frames per sec­ond. Add the fol­low­ing code and run it to see your loaded model: var an­i­mate = func­tion () { re­ques­tani­ma­tion­frame( an­i­mate ); if(ob­ject){ ob­­ta­tion.x+=.01; } ren­­der(scene, cam­era); } an­i­mate();

19. Re­move tex­tures and tex­ture load­ing

Now that you know how to load OBJ and as­sign tex­tures, you can use these com­plex mod­els in your scenes. How­ever, there is a much bet­ter way to load mod­els when you have the for­mat avail­able – and that is to use the gltf mod­els. To do this, re­move the code (ap­prox­i­mately 25 lines) from ‘load­ing man­ager’ all the way down to the tex­ture load­ing lines, in­clud­ing the tex­ture as­sets for each sec­tion.

// load­ing man­ager

// as­sign each loaded tex­ture to a tex­ture ob­ject


20. Re­place the OBJ loader with GLTF loader

Next, re­place the OBJ loader you were us­ing with the gltf loader us­ing the fol­low­ing code. No­tice that the for­mat is the same and the steps you fol­low are sim­i­lar, but it’s just much sim­pler; the gltf loader han­dles all the tex­ture as­sign­ments for you, and much more:

// load gltf model/scene var loader = new THREE.GLT­FLOADER(); loader.load( ‘as­sets/pony/scene.gltf’, func­tion ( gltf ) {

// han­dle loaded file here

// ad­just po­si­tion and scale });

21. Han­dle gltf model loaded

Now that your gltf scene is loaded, you can use the ‘tra­verse’ func­tion you used be­fore. This time it’s used to as­sign the ‘en­vmap’ you cre­ated. You could also man­u­ally ad­just tex­tures fur­ther us­ing this method as well. Run this new code and you’ll see your loaded gltf model! This is a great first step in get­ting com­plex mod­els into your 3D scenes!

// han­dle loaded file here gltf.scene.tra­verse( func­tion ( child ) { if ( child.ismesh ) {­te­rial.en­vmap = en­vmap; } });

// ad­just po­si­tion and scale gltf.scene.scale.set(.005,.005,.005); gltf.scene.po­si­tion.y = -1; scene.add( gltf.scene );

Newspapers in English

Newspapers from UK

© PressReader. All rights reserved.