Three.js university hello world

Chapitre 2 – Un Hello World avec Three.js

Dans ce nouveau chapitre, nous allons mettre en application les concepts théoriques de l’article précédent.

Je vous conseille vivement de commencer par lire le premier chapitre si ce n’est pas déjà fait, car nous allons aborder de manière pratique les concepts qui y sont détaillés :

Si vous êtes prêts, nous pouvons commencer !

Ainsi, notre objectif du jour consiste à créer un environnement de base Three.js composé d’un simple cube en 3D. Ce dernier pourra être utilisé comme point de départ de n’importe quel tutoriel.

Le code final est à votre disposition à la fin du chapitre.

Architecture de base du projet

Commençons ! Dans votre éditeur de code préféré, créons trois fichiers :

  • index.html
  • script.js
  • style.css

Importer la librairie dans le projet

Puis, l’étape suivante consiste à créer un script module ES6 dans notre code et d’y charger la bibliothèque Three.js.

L’instruction import est utilisée pour ajouter une bibliothèque type module ES6 au projet :

<script type="module">
    import * as THREE from  'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.js';
</script>

Notons qu’il est toujours possible d’importer la librairie à la manière d’un script JavaScript classique. Cependant, la méthode module est recommandée depuis la version r106 de Three.js.

La fonction d’initialisation de notre application

Puis, nous créons une fonction d’initialisation :init , cette dernière sera appelée à la fin du chargement de notre module JavaScript. Ce sera le point d’entrée de notre code.

import * as THREE from  'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.js';

function init()
{

}
init();

L’élément Scene de Three.JS

C’est l’heure d’attaquer le concret ! Dans la fonction init, initialisons une variable globale de type Scene Three.js :

import * as THREE from  'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.js';

var scene;

function init()
{
    scene = new THREE.Scene();
}
init();

Le constructeur de cette classe ne requiert aucun paramètre obligatoire ou facultatif.

Le Renderer WebGL de Three.js – La classe WebGLRenderer

Puis, il est nécessaire d’instancier un moteur de rendu dans notre code. La classe WebGLRenderer permet d’afficher une scène Three.js dans une page WEB grâce à la technologie WebGL.

Pour cela, nous initialisons une variable globale dans la fonction init comme précédemment :

import * as THREE from  'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.js';

var scene;
var renderer;

function init()
{
    scene = new THREE.Scene();

    // ---------------- RENDERER ----------------
		
    renderer = new THREE.WebGLRenderer( { antialias : true } );
    renderer.setPixelRatio( window.devicePixelRatio  );
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement ); // we add the HTML element to the HTML page
}
init();

WebGLRenderer – Constructeur

Le constructeur de cette classe accepte un objet JavaScript en paramètre facultatif afin de
pouvoir configurer notre instance de WebGLRenderer.

Certaines options peuvent ainsi être activées, dans notre cas, c’est l’antialiasing, une fonctionnalité permettant de lisser les contours des objets 3D.

WebGLRenderer – setPixelRatio

Cette méthode est utilisée pour définir un ratio entre la taille de l’écran et le nombre de pixels.
Habituellement et comme dans notre exemple, la valeur définie en paramètres est égale au
ratio natif de la page actuelle de notre navigateur web.

WebGLRenderer – setSize

Cette méthode redimensionne l’élément HTML dans lequel est affiché le rendu 3D en fonction du ratio pixel et de la taille hauteur/largeur donnée en paramètres.

WebGLRenderer – domElement

Cette propriété représente l’entité HTML à ajouter a notre page WEB. Le rendu 3D sera affiché dans cet élément HTML.
Dans notre exemple, nous utilisons une fonction JavaScript pour ajouter cet élément dans notre page.

La Camera de notre projet – PerspectiveCamera

Nous choisissons d’utiliser une caméra de type PerspectiveCamera. Cette dernière est conçue pour recréer le mode le projection basé sur la perspective de la vision humaine.

Toujours dans la fonction init :

[...]

var camera;

function init()
{
    [...]

    // ---------------- CAMERA ----------------
			
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
    camera.position.set( -500, 400, -500 );
    camera.lookAt(new THREE.Vector3(0,0,0));
    scene.add( camera );
}
init();
Three.JS PerpectiveCamera
Three.js PerpectiveCamera

PerspectiveCamera – Constructeur

Le constructeur de cette classe accepte quatre paramètres :

  1. FOV – Field of View (Angle d’ouverture de l’objectif)
  2. Aspect – Le ratio de projection (généralement Largeur / Hauteur)
  3. Distance minimale – Distance de vue minimale de la caméra
  4. Distance maximale – Distance de vue maximale de la caméra
Three.JS PerpectiveCamera Constructor
Three.js PerpectiveCamera – Constructor

PerspectiveCamera – position.set

Chaque objet de l’univers 3D Three.js possède une position sur trois axes, XYZ. Ces valeurs permettent de positionner un élément dans la scène.
Grace à la méthode set, il est possible de définir la position de notre caméra dans la scène avec trois arguments – les valeurs X, Y et Z.

PerspectiveCamera – lookAt

Tout comme les valeurs de position sur trois axes, chaque objet de l’univers 3D Three.js possède une valeur de rotation sur les axes X, Y et Z.

Il est possible d’orienter notre camera en modifiant ces valeurs, mais il est beaucoup plus simple d’utiliser la méthode lookAt.
Cette dernière ne nécessite qu’un seul argument, les coordonnées XYZ vers lesquelles la caméra doit s’orienter.

Ajouter la caméra à notre scène

Dans Three.js, chaque objet de l’univers propose une méthode add .

Cette méthode permet d’établir une relation parent/enfant entre deux éléments. Ainsi, pour ajouter la caméra à la liste des enfants de notre scène, nous utiliserons cette fonctionnalité.

L’éclairage de notre scène

Un univers Three.js est composé d’éléments qui nécessitent le plus souvent d’être éclairés. Il existe plusieurs éclairages disponibles dans la bibliothèque, nous allons utiliser deux d’entre eux.

Éclairage global – AmbientLight

Cet éclairage illumine de manière égale l’intégralité des objets de la scène. Toujours dans la fonction init, ajoutons une instance de AmbientLight dans la scène :

// ---------------- LIGHTS ----------------
			
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.2 );
scene.add( ambientLight );

Le constructeur de cette classe prend, dans notre projet, deux paramètres facultatifs :

  1. Couleur de l’éclairage – Facultatif – 0xFFFFFF par défaut
  2. Intensité de l’éclairage – Facultatif – 1 par défaut

Éclairage directionnel : DirectionalLight

La plupart du temps, un simple éclairage global AmbientLight n’est pas suffisant car il ne permet pas de distinguer correctement les polygones de nos objets 3D.
En effet ! Avec AmbientLight, chaque face des objets 3D est éclairée de manière égale, il est donc impossible de simuler correctement une lumière naturelle.

L’éclairage DirectionalLight simule une source lumineuse émise dans une direction, et est ainsi capable de créer des effets de lumière.

var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
scene.add( directionalLight );

Le constructeur de DirectionalLight fonctionne exactement comme celui de AmbientLight.

Un Cube en 3D

Dans ce chapitre, notre objectif est de créer une vue 3D d’un cube en rotation. Ainsi, en plus des éléments déjà ajoutés, nous devons instancier une variable de type Mesh pour créer notre cube.

Comme vous le savez déjà, un Mesh est composé du combo d’une Geometry et d’un Material.

Nous créons une variable globale pour notre cube, et nous l’initialisons dans la fonction init :

// ---------------- 3D CUBE ----------------
			
const geometry = new THREE.BoxGeometry( 150, 150, 150 );
const material = new THREE.MeshPhongMaterial( {color: 0x00ffff} );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
Mesh Geometry Material
Composition de notre Mesh

Notre Mesh est composé d’une BoxGeometry et d’un MeshPhongMaterial.

Une Geometry cubique avec BoxGeometry

La première chose à faire est de créer une Geometry , la forme de notre cube.
Pour cela, nous utilisons la classe BoxGeometry. Cette dernière permet d’instancier une Geometry rectangulaire selon trois paramètres :

  1. Largeur – Taille de la structure sur l’axe X
  2. Hauteur – Taille de la structure sur l’axe Y
  3. Profondeur – Taille de la structure sur l’axe Z

Ainsi, en définissant trois valeurs égales, il est possible de créer une Geometry cubique.

L’aspect visuel de notre Mesh avec MeshPhongMaterial

La prochaine étape consiste a créer le Material de notre cube. Il existe plusieurs types de Material disponibles dans Three.js, nous choisissons l’un des plus utilisés, MeshPhongMaterial.

Ce type de Material est surtout utilisé pour son aspect brillant, contrairement aux d’autres qui sont principalement mats.

Le constructeur de cette classe accepte un objet JavaScript en paramètre facultatif afin de pouvoir configurer notre instance de MeshPhongMaterial.
Nous utilisons cette fonctionnalité pour définir une couleur de texture en hexadécimal.

L’objet final Mesh

Notre cube est enfin créé et prêt a l’emploi dans sa variable ! Comme tout objet de la scène, il possède des valeurs de position et rotation, que nous choisissons de ne pas modifier pour le moment.
Le cube sera créé par défaut au centre le la scène (0, 0 , 0) XYZ.

Boucle principale et rendu 3D de la scène

Ouf ! Notre fonction d’initialisation est enfin terminée !

Tous les éléments et acteurs de notre univers Three.js sont désormais créés, il ne nous reste qu’une étape : la boucle principale d’animation.

Cette boucle principale nous permettra d’effectuer des actions à chaque tour de boucle (« frame »). L’une de ces actions concerne le rendu 3D, qui consiste à afficher notre scène perçue par une caméra.

Création de la boucle principale

Créons une fonction render dans notre module JavaScript, cette fonction sera utilisée comme boucle principale et sera chargée d’effectuer des actions à chaque « frame » de notre application.

function render()
{
    [...]
}

Mise à jour du rendu 3D

Pour cela, nous utilisons notre objet de type WebGLRenderer instancié dans la fonction init .
Cette instance met à disposition une méthode de classe render ( à ne pas confondre avec notre fonction render !), qui prends deux paramètres :

  1. Scene – Une scène Three.js
  2. Camera – Une caméra Three.js

Utilisons cette méthode avec nos variables :

function render()
{
    renderer.render( scene, camera ); 	// We are rendering the 3D world
}

Rotation du cube

Nous souhaitons faire tourner notre cube sur deux axes ( X et Y ).
Pour rappel, chaque objet de la scène possède une valeur de rotation pour chaque axe X, Y et Z.
Nous incrémentons à chaque tour de boucle, la valeur déjà effective sur les axes X et Y, pour faire entrer le cube en rotation

function render()
{
    // rotating the cube each render tick
    cube.rotation.x += 0.005;
    cube.rotation.y += 0.01;

    renderer.render( scene, camera ); 	// We are rendering the 3D world
}

Finalisation de la boucle

La dernière étape de ce projet consiste à finaliser le système de boucle principale.
Notre fonction render est à présent capable de faire tourner le cube et d’effectuer un rendu 3D de notre scène dans le canvas du WebGLRenderer.

Cependant, n’oublions pas que cette fonction n’est pas encore utilisée ! La fonction init est le point d’entrée de notre module JavaScript. Nous devons exécuter la fonction render à la fin de init comme ci-dessous :

[...]	

function init()
{	
        [...]	

	// ---------------- STARTING THE RENDER LOOP ----------------

	render();		
}

function render()
{
	[...]
}

Ainsi, lorsque la phase d’initialisation sera terminée, la fonction render s’exécutera. Pour qu’elle adopte un comportement de boucle nous ajoutons une dernière ligne à la fin de la fonction render :

function render()
{
    // rotating the cube each render tick
    cube.rotation.x += 0.005;
    cube.rotation.y += 0.01;

    renderer.render( scene, camera ); 	// We are rendering the 3D world
    requestAnimationFrame( render );	// we are calling render() again,  to loop
}

requestAnimationFrame est une fonction JavaScript adaptée à notre besoin, elle fera en sorte que la fonction render s’exécute en boucle.

Résultat final et code du projet

See the Pen Hello World by Thomas (@thomassifferlen) on CodePen.

N’hésitez pas à télécharger le guide complet de Three.js University pour progresser encore plus vite !

Cet article ne couvre que la surface des concepts de base de Three.js ! Dans le chapitre 2 du guide téléchargeable, vous découvrirez comment configurer en détail les différents concepts que je viens de vous présenter !

Three.js University Guide Complet – Hello World
Three.js University Guide Complet – Hello World

Vous pouvez télécharger le guide complet ici :


La prochaine étape de notre aventure en 3D concerne les classes Geometry, suivez le guide :

(5 commentaires)

Les commentaires sont fermés.