Créer de la 3D dans le navigateur de zéro : scène, modèles STL/GLTF, animation au scroll, shaders, post-processing et portfolio 3D animé complet.
Les primitives Three.js (cube, sphère, cylindre) couvrent les cas simples. Pour des objets réels — une pièce mécanique, un produit, un personnage — on charge des modèles 3D depuis des fichiers. Deux formats dominent : STL (simple, géométrie pure) et GLTF (complet, standard web 3D moderne).
STL est le format de l'impression 3D. Il encode une géométrie comme un ensemble de triangles. Pas de matériaux, pas de textures, pas d'animations — juste la forme.
Pourquoi l'utiliser : fichiers compacts, export universel depuis tous les logiciels de CAO (Fusion 360, SolidWorks, FreeCAD, Blender). Parfait pour afficher des pièces techniques, des sculptures, des objets imprimés en 3D.
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'
const loader = new STLLoader()
loader.load(
'/models/piece.stl',
(geometry) => {
// STL retourne directement une BufferGeometry
const material = new THREE.MeshStandardMaterial({
color: '#cdff4f',
roughness: 0.3,
metalness: 0.7,
})
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
},
(progress) => {
const percent = (progress.loaded / progress.total * 100).toFixed(0)
console.log(`Chargement : ${percent}%`)
},
(error) => {
console.error('Erreur STL :', error)
}
)Les fichiers STL ne sont pas toujours centrés à l'origine. Un modèle exporté depuis Fusion 360 peut avoir ses coordonnées n'importe où.
loader.load('/models/piece.stl', (geometry) => {
// Calculer le bounding box
geometry.computeBoundingBox()
const box = geometry.boundingBox
// Centrer à l'origine
const center = new THREE.Vector3()
box.getCenter(center)
geometry.translate(-center.x, -center.y, -center.z)
// Normaliser la taille
const size = new THREE.Vector3()
box.getSize(size)
const maxDim = Math.max(size.x, size.y, size.z)
const scale = 2 / maxDim // ramener dans une boîte de 2 unités
const mesh = new THREE.Mesh(geometry, material)
mesh.scale.setScalar(scale)
scene.add(mesh)
})Certains STL binaires n'ont pas de normales correctes — les surfaces apparaissent plates.
loader.load('/models/piece.stl', (geometry) => {
geometry.computeVertexNormals() // recalculer les normales
// ...
})GLTF (GL Transmission Format) est le format recommandé pour le web. Il encode géométrie, matériaux PBR, textures, animations, squelettes et métadonnées dans un seul fichier (.glb = binaire, .gltf = JSON + fichiers séparés).
Pourquoi l'utiliser : matériaux préservés, animations incluses, optimisé pour la transmission web, supporté par tous les éditeurs 3D modernes.
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
const gltfLoader = new GLTFLoader()
gltfLoader.load('/models/laptop.glb', (gltf) => {
const model = gltf.scene // Group contenant tous les meshes
model.position.set(0, -1, 0)
model.scale.setScalar(0.01) // les exports Blender sont souvent en centimètres
scene.add(model)
// Accéder aux animations
if (gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(model)
const action = mixer.clipAction(gltf.animations[0])
action.play()
// Dans la render loop
const clock = new THREE.Clock()
function animate() {
requestAnimationFrame(animate)
mixer.update(clock.getDelta())
renderer.render(scene, camera)
}
animate()
}
})Un GLTF peut contenir des dizaines de meshes imbriqués. Pour accéder à chacun ou modifier leurs matériaux :
gltfLoader.load('/models/scene.glb', (gltf) => {
gltf.scene.traverse((child) => {
if (child.isMesh) {
// Activer les ombres sur chaque mesh
child.castShadow = true
child.receiveShadow = true
// Modifier le matériau
child.material.envMapIntensity = 1.5
child.material.needsUpdate = true
}
})
scene.add(gltf.scene)
})Dans Blender, nommer vos objets permet de les cibler en code.
gltfLoader.load('/models/product.glb', (gltf) => {
scene.add(gltf.scene)
// Trouver par nom (nom donné dans Blender)
const screenMesh = gltf.scene.getObjectByName('Screen')
if (screenMesh) {
screenMesh.material = new THREE.MeshStandardMaterial({
color: '#000000',
roughness: 0,
metalness: 0.5,
})
}
})Draco est un algorithme de compression de géométrie 3D par Google. Un fichier GLTF peut être compressé avec Draco et diviser sa taille par 5 à 10.
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
const dracoLoader = new DRACOLoader()
// Le décodeur Draco est un WebAssembly — pointer vers les fichiers decoder
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')
// Ou en local (copier depuis node_modules/three/examples/jsm/libs/draco/)
dracoLoader.setDecoderPath('/draco/')
const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)
gltfLoader.load('/models/compressed.glb', (gltf) => {
scene.add(gltf.scene)
})Pour les fichiers volumineux (> 1 Mo), Draco est indispensable pour les performances web.
Pour coordonner le chargement de plusieurs fichiers et afficher une barre de progression globale :
const loadingManager = new THREE.LoadingManager(
// onLoad — tout est chargé
() => {
console.log('Tous les assets sont prêts')
document.querySelector('.loader').style.display = 'none'
},
// onProgress
(url, loaded, total) => {
const progress = (loaded / total * 100).toFixed(0)
document.querySelector('.loader-bar').style.width = `${progress}%`
},
// onError
(url) => {
console.error('Erreur lors du chargement :', url)
}
)
const textureLoader = new THREE.TextureLoader(loadingManager)
const gltfLoader = new GLTFLoader(loadingManager)
const stlLoader = new STLLoader(loadingManager)Blender est le logiciel open-source de référence pour préparer des modèles pour le web.
Paramètres d'export GLTF recommandés :
Format : GLB (binaire, un seul fichier)
✓ Include : Selected Objects ou Visible Objects
✓ Geometry : Apply Modifiers
✓ Materials : Export
✓ Compression : Draco (si disponible)
Transform : +Y Up (convention Three.js)Optimisations avant export :
1. Appliquer les transforms (Ctrl+A → All Transforms)
2. Nettoyer les vertices doubles (Edit Mode → Merge by Distance)
3. Réduire les polygones (Decimate Modifier si modèle trop dense)
4. Bake les textures haute résolution si le modèle vient de sculpture
5. Nommer les objets clairement (ils seront accessibles par getObjectByName)En Next.js, placer les modèles dans /public/models/. Ils sont servis à /models/.
public/
models/
piece.stl
laptop.glb
textures/
wood.jpg
hdri/
studio.hdr// Dans le code
loader.load('/models/piece.stl', ...)| Critère | STL | GLTF/GLB |
|---|---|---|
| Matériaux | Non | Oui (PBR complet) |
| Textures | Non | Oui |
| Animations | Non | Oui |
| Taille typique | Compact | Variable, compressible Draco |
| Source | CAO, impression 3D | Blender, Maya, Unity |
| Quand l'utiliser | Pièces techniques, formes simples | Produits, scènes, personnages |
Les modèles se chargent dans la scène. Le chapitre suivant est le cœur du cours : animer ces modèles au scroll avec GSAP ScrollTrigger — position, rotation, opacité synchronisées avec la position de défilement de l'utilisateur.