Three.js est une bibliothèque WebGL. Elle permet de simplifier l’écriture du code WebGL.
La première chose à faire est de télécharger la bibliothèque et l’inclure dans votre page.
<script src="three.min.js"></script>
Pour créer une scène vide, on doit créer plusieurs éléments, qui nous permettrons par la suite d’afficher des objets à l’écran:
Une scène représente l’ensemble du monde 3D que nous essayons d’afficher.
var scene = new THREE.Scene();
Une caméra représente la position du spectateur dans le monde.
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 50;
PerspectiveCamera()
prend quatre arguments:
Un moteur de rendu est l’objet qui restitue une scène donnée, vu à travers une caméra donnée.
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xDDDDDD, 1);
document.body.appendChild(renderer.domElement);
L’étape finale est d’afficher la scène à travers la caméra (pour l’instant, la scène est vide):
renderer.render(scene, camera);
Three.js fournit des méthodes pour créer des formes en une seule ligne de code. Il y a des cubes, sphères, cylindres, etc. Quelque soit la forme choisie, le processus reste le même:
Créer la géométrie de l’objet (sa forme). Par exemple, pour créer un cube de 10x10x10 unités:
var boxGeometry = new THREE.BoxGeometry(10, 10, 10);
Spécifier le matériau de l’objet - la couleur ou la texture qui recouvre sa surface.
var basicMaterial = new THREE.MeshBasicMaterial({color: 0x0095DD});
Appliquer le matériau sur la géométrique avec un mesh.
var cube = new THREE.Mesh(boxGeometry, basicMaterial);
Par défaut l’objet est affiché en face (un carré), pour le faire pivoter on peut utiliser la propriété rotation
.
cube.rotation.set(0.4, 0.2, 0);
Note: pour que les bords de l’objet ne soient pas aliasés, vous pouvez passer l’option antialias
au moteur de rendu.
var renderer = new THREE.WebGLRenderer({antialias: true});
Finalement, pour ajouter l’objet à la scène (et donc l’afficher), on utilise la méthode add()
de la scène créée précédemment.
scene.add(cube);
Pour utiliser des lumières, choisissez un matériau autre que MeshBasicMaterial
pour vos objets - ce dernier ne prend pas les lumières en considération. MeshStandardMaterial
par exemple:
var basicMaterial = new THREE.MeshStandardMaterial({color: 0x0095DD});
Il existe plusieurs types de lumières.
JSFiddle lumières (utiliser la liste déroulante pour voir les différences)
AmbientLight
est une lumière douce qui illumine toute la scène
var light = new THREE.AmbientLight('rgb(255, 255, 255)');
scene.add(light);
HemisphereLight
est une lumière douce qui illumine la scène du haut.
var hemisphereLight = new THREE.HemisphereLight('rgb(255, 255, 255)', 0x080820);
On définit la couleur du ciel et la couleur du sol en paramètre.
SpotLight
est un faisceau de lumière émis d’un point lumineux, un projecteur
var spotLight = new THREE.SpotLight('rgb(255, 255, 255)');
spotLight.position.set(100, 1000, 1000);
spotLight.castShadow = true;
scene.add(spotLight);
PointLight
est une lumière diffuse émise d’un point lumineux, une ampoule.
La différence avec SpotLight n’est visible que quand le point lumineux est proche d’un objet.
var pointLight = new THREE.PointLight('rgb(255, 255, 255)');
pointLight.position.set(100, 1000, 1000);
pointLight.castShadow = true;
scene.add(pointLight);
DirectionalLight
est une lumière émise dans une seule direction.
var directionalLight = new THREE.DirectionalLight('rgb(255, 255, 255)');
directionalLight.position.set( 100, 1000, 1000 );
directionalLight.castShadow = true;
scene.add(directionalLight);
RectAreaLight
est une lumière diffuse en forme de rectangle.
Ça permet par exemple de simuler une fenêtre d’où sort de la lumière.
var rectLight = new THREE.RectAreaLight('rgb(255, 255, 255)');
rectLight.position.set(0, 5, 5);
rectLight.lookAt(0, 0, 0);
Ne marche que sur les matériaux MeshStandardMaterial
et MeshPhysicalMaterial
.
On peut spécifier les dimensions du rectangle en paramètre.
Three.js fournit des méthodes pour créer des formes 3D basiques et des formes 2D (des plans sans profondeur).
JSFiddle formes basiques
box = new THREE.BoxGeometry(10, 10, 10)
cone = new THREE.ConeGeometry(10, 10, 32)
sphere = new THREE.SphereGeometry(10, 32, 32)
cylinder = new THREE.CylinderGeometry(5, 5, 10, 32)
torus = new THREE.TorusGeometry(7, 1, 6, 12)
torusKnot = new THREE.TorusKnotGeometry(7, 1, 20, 12)
12 facettes en pentagone
dodecahedron = new THREE.DodecahedronGeometry(7)
20 facettes en triangle
icosahedron = new THREE.IcosahedronGeometry(7)
8 facettes en triangle
octahedron = new THREE.OctahedronGeometry(7)
4 facettes en triangle
tetrahedron = new THREE.TetrahedronGeometry(7)
circle = new THREE.CircleGeometry(10, 32)
plane = new THREE.PlaneGeometry(10, 10)
ring = new THREE.RingGeometry(5,10,32)
On peut également créer des formes plus complexes, qui demandent plus de travail:
Tube
permet de créer un tube 3D qui suit une courbe définie au préalable
var path = new THREE.SplineCurve3([
new THREE.Vector3(0, 10, -20),
new THREE.Vector3(10, -10, -1),
new THREE.Vector3(2, -10, 15)
]);
var tube = new THREE.TubeGeometry(path, 20, 2, 8, false);
shape
permet de créer une forme en 2D à la manière de l’API Canvas 2D
var path = new THREE.Shape();
path.quadraticCurveTo(10, -16, 18, -2);
path.quadraticCurveTo(20, -2, 23, -8);
path.quadraticCurveTo(23, 0, 23, 8);
path.quadraticCurveTo(20, 2, 18, 2);
path.quadraticCurveTo(10, 16, 0, 0);
var fish = new THREE.ShapeGeometry(path);
extrude
permet de créer une forme en 3D à partir d’une forme 2D. Il suffit de préciser la profondeur de l’objet.
var path = new THREE.Shape();
path.quadraticCurveTo(10, -16, 18, -2);
path.quadraticCurveTo(20, -2, 23, -8);
path.quadraticCurveTo(23, 0, 23, 8);
path.quadraticCurveTo(20, 2, 18, 2);
path.quadraticCurveTo(10, 16, 0, 0);
var fish3D = new THREE.ExtrudeGeometry(path,{
bevelEnabled: false,
amount: 5
});
polyhedron
permet de créer des polyhedrons personnalisés (icosahedron, octahedron, tetrahedron ou autres) en spécifiant les sommets (vertices en anglais) et indices de la forme
var vertices = [
1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1
];
var indices = [
2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
];
polyhedron = new THREE.PolyhedronGeometry(vertices, indices, controls.radius, controls.detail);
Exemple polyhedron
Polyhedrons: Tetra, Octa, Icosa
Autres Polyhedrons
BabylonJS Playground
lathe
permet de répéter une forme autour d’un cercle.
var data = [
[3, 0],
[3, 0.3],
[2.9, 0.3],
[0.3, 1],
[0.3, 6],
[2, 6],
[3, 8],
[3, 9],
[2.3, 14]
];
var points = data.map((d, i) => new THREE.Vector2(data[i][0], data[i][1]));
shapes.lathe = new THREE.LatheGeometry(points, 24);
parametric
prend en paramètre une fonction qui mappe les valeurs u et v (entre 0 et 1) en un vecteur 3D pour chaque point. Cela permet de déformer une surface plane selon une fonction graphique.
var radialWave = function (u, v, optionalTarget) {
var result = optionalTarget || new THREE.Vector3();
var r = 50;
var x = Math.sin(u) * r;
var z = Math.sin(v / 2) * 2 * r;
var y = (Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI)) * 2.8;
return result.set(x/2-10, y/2, z/2);
}
var shape = new THREE.ParametricGeometry(shapes.radialWave, 25, 25);
geometry
permet de créer un objet 3D de toutes pieces, en indiquant les sommets et les faces de l’objet à afficher.
// Un plan en forme de triangle
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(-10, 10, 0),
new THREE.Vector3(-10, -10, 0),
new THREE.Vector3(10, -10, 0)
);
geometry.faces.push( new THREE.Face3(0, 1, 2));
Il est possible de modifier l’emplacement des sommets pour tous les objets Geometry
, PlaneGeometry
, PolyhedronGeometry
, etc, à travers le tableau vertices
. Ensuite, mettre verticesNeedUpdate
à vrai pour que le moteur de rendu prenne en considération les modifications.
var delta = 0;
function render() {
delta += 0.1;
geometry.vertices[0].x = -25 + Math.sin(delta) * 50;
geometry.verticesNeedUpdate = true;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
bufferGeometry
est optimisé pour plus de performances par rapport à geometry
mais ne permet de modifier les sommets dynamiquement contrairement à geometry
.
// Un plan en forme de triangle
var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array((
new THREE.Vector3(-10, 10, 0),
new THREE.Vector3(-10, -10, 0),
new THREE.Vector3(10, -10, 0)
]);
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
Pour pouvoir dessiner du texte 3D, il est nécessaire d’inclure une police d’écriture JSON.
Three.js fournit quelques polices d’écritures JSON: helvetiker, optimer, gentilis, droid sans, droid serif.
Vous pouvez également convertir vos propres polices en JSON.
new THREE.FontLoader().load(url, function(font) {
// Créer du texte
var geometry = new THREE.TextGeometry("Hello World", {
font: font,
size: 10,
height: 5,
material: 0,
bevelThickness: 1,
extrudeMaterial: 1
}),
item = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: 0xffffff }));
// Ajouter au canvas
scene.add(item);
renderer.render(scene, camera);
});
JSFiddle TextGeometry
Exemple typing text
Cette méthode n’est pas appropriée pour écrire du texte 2D. Pour écrire du texte 2D, la manière la plus performante reste d’utiliser du HTML positionné en absolu par dessus le canvas. JSFiddle Texte 2D
Il y a différents types de matériaux dans Three.js qui ont tous des propriétés différentes, comme répondre aux lumières, mapper des textures, ajuster l’opacité…
MeshBasicMaterial
est le matériel le plus basique, il permet de mettre une couleur à un objet. Il ne prend pas en considération la lumière/les ombres. Il peut être semi-transparent en définissant les paramètres transparent
et opacity
var material = new THREE.MeshBasicMaterial({
color: 0xff0000,
transparent: true,
opacity: 0.5
});
MeshNormalMaterial
colore les faces de l’objet différemment selon leur direction.
var material = new THREE.MeshNormalMaterial();
Affiche les objets en niveau de gris. Les objets les plus proches sont plus sombres et les objets les plus éloignés sont plus clairs.
var material = new THREE.MeshDepthMaterial();
MeshLambertMaterial
est un matériel qui permet de colorer un objet, même principe que MeshBasicMaterial
à la différence qu’il prend en considération les lumières pour afficher certaines zones plus sombres ou plus claires.
var material = new THREE.MeshLambertMaterial({
color: 0xff0000,
transparent: true,
opacity: 0.5
});
En définissant le paramètre emissive
, l’objet irradie une couleur.
L’intensité peut être contrôlée avec le paramètre emissiveIntensity
.
var material = new THREE.MeshLambertMaterial({
color: 0xf3ffe2,
emissive: 0xff0000,
emissiveIntensity: 0.1
});
Par défaut, les 3 faces du devant d’un cube sont affichées (et les faces de derrière sont dissimulées).
On peut masquer les faces du devant et afficher celle du derrière à la place:
side: THREE.BackSide // par défaut: THREE.FrontSide
Avec double
, les faces de devant sont visibles lorsque l’objet est de face et celles de derrière lorsqu’il est vu de derrière (si on le fait tourner sur lui-même à 180°).
side: THREE.DoubleSide
Plutôt qu’une couleur, on peut ajouter une texture.
color: 0xf3ffe2,
map: new THREE.TextureLoader().load('wool.jpg')
MeshPhongMaterial
répond également à la lumière mais est utilisée pour les surfaces qui brillent (tandis que MeshLambertMaterial
est pour les surfaces mates) - le contraste entre les zones sombres et claires est plus élevé.
var material = new THREE.MeshPhongMaterial({
color: 0xf3ff02
})
On peut contrôler la quantité de lumière que la surface reflète avec specular
et shininess
specular: 0xff0000,
shininess: 30
MeshStandardMaterial
est un nouveau matériel qui permet de rendre à la fois des textures mates et des textures brillantes.
Il accepte pour ce faire des paramètres:
roughness
, rend la surface plus terne ou plus intense (couleur plus unie ou plus contrastée)metalness
, augmente ou diminue le taux de rebondissement de la lumière sur la surface (donne un aspect plus métal ou plus plastique)var material = new THREE.MeshStandardMaterial({
color: 0xf3ffe2,
roughness: 0.5,
metalness: 0.5
})
Même principe que MeshBasicMaterial
mais s’applique aux lignes (côtés) et non aux faces.
var geometry = new THREE.BoxGeometry(100,100,100),
material = new THREE.LineBasicMaterial(),
item = new THREE.Line(geometry, material);
Affiche des lignes avec des pointillés.
On peut spécifier la taille des tirets et des espaces.
dashSize: 2,
gapSize: 2
Pour que ça fonctionne, il est nécessaire d’appeler computeLineDistances
sur l’objet pour que le moteur de rendu calcule les distances.
var geometry = new THREE.BoxGeometry(100,100,100),
material = new THREE.LineDashedMaterial({ dashSize:2, gapSize:2 }),
item = new THREE.Line(geometry, material);
item.computeLineDistances();
S’applique aux points (sommets).
var geometry = new THREE.BoxGeometry(100,100,100),
material = new THREE.PointsMaterial(),
item = new THREE.Points(geometry, material);
Il est possible de contrôler la taille des points avec le paramètre size
size: 10
Permet d’ajouter un sprite à l’écran (un plan).
Un sprite est toujours orienté en face vers la caméra, on s’en sert pour afficher un fond.
var material = new THREE.SpriteMaterial({
map: new THREE.TextureLoader().load('wool.jpg')
}),
mesh = new THREE.Sprite(material);
mesh.scale.set(100,100,100);
mesh.position.z = -1000;
Comme pour un canvas, on utilise window.requestAnimationFrame()
pour créer une animation de 60 images par secondes (idéalement).
On peut par exemple pour faire tourner un objet sur lui-même:
function render() {
cube.rotation.y += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
On peut mettre à jour les propriétés x
, y
et z
individuellement ou toutes en même temps avec set(x, y, z)
:
cube.rotation.set(0.1,0.1,0.1);
Notez que les transformations peuvent être appliqués sur les objets ou sur la scène directement.
Exemple faire tourner/zoomer/déplacer
Exemple faire tourner
cube.rotation.y += 0.01;
cube.scale.x = Math.abs(Math.sin(0.5));
dodecahedron.position.y = -7 * Math.sin(t*2);
Il est possible de charger des objets (forme + matériel) à partir de fichiers JSON.
Cela vous permet de créer vos modèles à partir de logiciels de création 3D, comme Blender.
var url = "model.json";
var loader = new THREE.JSONLoader(), mesh;
loader.crossOrigin = "";
loader.load(url, function draw_model(geometry, materials){
var mesh = new THREE.Mesh(geometry, materials);
scene.add(mesh);
});
Il est également de jouer l’animation du modèle, s’il en a:
var mixer;
loader.load(url, function draw_model(geometry, materials){
// Display monkey
var mesh = new THREE.Mesh(geometry, materials);
scene.add(mesh);
// Create animation mixer
var clip = new THREE.AnimationClip.CreateFromMorphTargetSequence("mon_anim", geometry.morphTargets, 30);
mixer = new THREE.AnimationMixer(mesh);
mixer.clipAction(clip).setDuration(1).play();
}
// Animation loop
var prevTime = Date.now();
function render() {
requestAnimationFrame(render);
if(!mixer) {
return;
}
// Update animation
var time = Date.now();
mixer.update((time - prevTime) * 0.0003);
prevTime = time;
// Render
renderer.render(scene, camera);
}
render();
JSFiddle Modèle animé
Cours: Three.js Loading Models
Pour pouvoir afficher des ombres, la première chose à faire est de définir quelques propriétés sur le moteur de rendu:
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
Définir une source de lumière:
var light = new THREE.SpotLight(0xffffff, 4, 3000);
light.position.y = 100;
light.target = mesh;
Définir l’ombre:
light.castShadow = true;
light.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(100, 1, 1, 1000));
light.shadow.bias = 0.0001;
light.shadow.mapSize.width = 2048 * 2;
light.shadow.mapSize.height = 2048 * 2;
Ajouter la lumière à la scène
scene.add(light);
Activer le fait que nos objets peuvent projeter et recevoir des ombres:
cube.castShadow = true;
floor.receiveShadow = true;
Il y a plusieurs types de caméras
PerspectiveCamera
un type de caméra qui affiche les objets tels qu’on les voit dans le même réel: les objets éloignés sont plus petits que les objets proches.
camera = new THREE.PerspectiveCamera(
35,
window.innerWidth / window.innerHeight,
1, 1000
);
On peut bouger la caméra pour faire tourner la scène. Par exemple pour faire bouger la caméra autour du point lumineux:
var delta = 0;
function render() {
delta += 0.01;
camera.lookAt(light.position);
camera.position.x = Math.sin(delta) * 200;
camera.position.z = Math.cos(delta) * 200;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
JSFiddle Faire bouger la caméra
Une OrthographicCamera
n’a pas de perspective. Tous les objets sont affichés au même niveau.
camera = new THREE.OrthographicCamera(
-300, 300,
400, -400,
0.1, 1000
)
Three.js fournit des helpers pour afficher la position de la caméra et des lumières. C’est très utile pour debugger.
CameraHelper
permet de visualiser où se situe la caméra.
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
var newCamera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000),
cameraHelper = new THREE.CameraHelper(newCamera);
scene.add(cameraHelper);
PointLightHelper
permet de visualiser où se situe un PointLight.
var pointLightHelper = new THREE.PointLightHelper(pointLight);
scene.add(pointLightHelper);
Il existe également des SpotLightHelper, DirectionalLightHelper, etc.
Il est possible d’ajouter des filtres à la scène en utilisant un Composer
. Pour ce faire, on donne une liste de “passes” à effectuer au composer sur le moteur de rendu puis on affiche le résultat avec composer.render
au lieu de renderer.render
.
Il faut, au préalable, inclure les fichiers EffectComposer.js
, ShaderPass.js
et RenderPass.js
disponibles sur le repo Github.
Ajoutez ensuite les filtres (shaders) que vous voulez utiliser, voir les exemples disponibles sur le repo Github. Il vous faudra au minimum CopyShader.js
.
Définissez renderToScreen
sur votre dernier filtre.
var composer = new THREE.EffectComposer(renderer);
// Liste de passes
var renderPass = new THREE.RenderPass(scene, camera);
composer.addPass(renderPass);
renderPass.renderToScreen = true;
// renderer.render(scene, camera);
composer.render();
JSFiddle EffectComposer
Cours: Three.js Post Processing