iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
JavaScript · Intermédiaire

Three.js

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.

Three.jsJavaScriptGSAPGLSLWebGL
01Introduction à Three.js02Géométries, matériaux et lumières03Charger des modèles 3D — STL et GLTF04Animer au scroll — Three.js + GSAP05Post-processing et effets visuels06Shaders GLSL et systèmes de particules07Portfolio 3D animé — projet complet
Chapitre 5·20 min

Post-processing et effets visuels

Le post-processing applique des effets sur l'image rendue avant de l'afficher — comme des filtres photo sur le rendu 3D. Bloom qui fait luire les zones lumineuses, film grain qui donne un effet pellicule, vignette qui assombrit les bords. Ces effets transforment un rendu correct en rendu mémorable.

EffectComposer

L'EffectComposer remplace l'appel direct à renderer.render(). Il enchaîne des "passes" : RenderPass (le rendu de la scène) + les effets dans l'ordre.

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'
 
const composer = new EffectComposer(renderer)
 
// Passe 1 : rendu de la scène
composer.addPass(new RenderPass(scene, camera))
 
// Passe 2 : bloom
const bloom = new UnrealBloomPass(
  new THREE.Vector2(innerWidth, innerHeight),
  1.2,   // strength — intensité du glow
  0.5,   // radius — rayon de diffusion
  0.7    // threshold — luminosité minimale pour le bloom (0 = tout, 1 = rien)
)
composer.addPass(bloom)
 
// Passe finale : conversion couleur pour l'affichage
composer.addPass(new OutputPass())
 
// Dans la render loop : remplacer renderer.render() par composer.render()
function animate() {
  requestAnimationFrame(animate)
  composer.render()  // ← plus renderer.render()
}

OutputPass convertit le rendu HDR interne en sRGB pour l'écran. Sans elle, les couleurs sont trop sombres ou trop claires.

UnrealBloomPass — bloom sélectif

Le problème du bloom global : il s'applique à tout, y compris au texte HTML superposé si on n'y fait pas attention. Pour un bloom uniquement sur certains objets, on utilise deux layers.

// Technique : double render pass
// Layer 0 = normal, Layer 1 = bloom
 
const BLOOM_LAYER = 1
const bloomLayer = new THREE.Layers()
bloomLayer.set(BLOOM_LAYER)
 
// Assigner le layer bloom aux objets qui doivent luire
model.layers.enable(BLOOM_LAYER)
 
// Passe bloom — uniquement les objets du layer 1
const bloomComposer = new EffectComposer(renderer)
bloomComposer.renderToScreen = false
bloomComposer.addPass(new RenderPass(scene, camera))
bloomComposer.addPass(new UnrealBloomPass(...))
 
// Passe finale — combiner bloom + scène normale
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
 
const finalPass = new ShaderPass(
  new THREE.ShaderMaterial({
    uniforms: {
      baseTexture: { value: null },
      bloomTexture: { value: bloomComposer.renderTarget2.texture },
    },
    vertexShader: /* glsl */`
      varying vec2 vUv;
      void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `,
    fragmentShader: /* glsl */`
      uniform sampler2D baseTexture;
      uniform sampler2D bloomTexture;
      varying vec2 vUv;
      void main() {
        gl_FragColor = texture2D(baseTexture, vUv) + vec4(texture2D(bloomTexture, vUv).rgb, 0.0);
      }
    `,
    defines: {},
  }),
  'baseTexture'
)
finalPass.needsSwap = true
 
const finalComposer = new EffectComposer(renderer)
finalComposer.addPass(new RenderPass(scene, camera))
finalComposer.addPass(finalPass)
finalComposer.addPass(new OutputPass())
 
// Render loop
function animate() {
  requestAnimationFrame(animate)
 
  // 1. Rendre uniquement les objets bloom
  camera.layers.set(BLOOM_LAYER)
  bloomComposer.render()
 
  // 2. Rendre la scène complète avec le bloom fusionné
  camera.layers.set(0)
  finalComposer.render()
}

Cette technique permet d'avoir un bloom précis : seule la pièce métallique luit, pas le fond ni les textes.

SMAAPass — anti-aliasing post-processing

L'antialiasing du renderer (antialias: true) ne fonctionne pas avec le post-processing. Il faut l'ajouter comme passe.

import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js'
 
// Ajouter après le bloom, avant OutputPass
composer.addPass(new SMAAPass(innerWidth, innerHeight))

SMAA (Subpixel Morphological Antialiasing) est le meilleur rapport qualité/performance disponible en post-processing.

FilmPass — grain et scanlines

import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js'
 
const film = new FilmPass(
  0.35,   // noiseIntensity — intensité du grain
  false   // grayscale
)
composer.addPass(film)

Un léger grain (0.2-0.4) donne un rendu plus organique, moins "virtuel". C'est subtil mais ça change le ressenti.

ShaderPass — effets custom

Pour des effets non disponibles en passes intégrées, ShaderPass permet d'écrire du GLSL appliqué sur le rendu final.

Vignette

const vignetteShader = {
  uniforms: {
    tDiffuse: { value: null },
    offset: { value: 0.95 },
    darkness: { value: 1.6 },
  },
  vertexShader: /* glsl */`
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: /* glsl */`
    uniform sampler2D tDiffuse;
    uniform float offset;
    uniform float darkness;
    varying vec2 vUv;
 
    void main() {
      vec4 color = texture2D(tDiffuse, vUv);
      vec2 uv = (vUv - vec2(0.5)) * vec2(offset);
      float vignette = 1.0 - dot(uv, uv) * darkness;
      gl_FragColor = vec4(color.rgb * vignette, color.a);
    }
  `,
}
 
composer.addPass(new ShaderPass(vignetteShader))

Aberration chromatique

Décale les canaux rouge, vert et bleu — effet "lentille bon marché" très populaire dans les portfolios.

const chromaticAberrationShader = {
  uniforms: {
    tDiffuse: { value: null },
    amount: { value: 0.003 },
  },
  vertexShader: /* glsl */`
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: /* glsl */`
    uniform sampler2D tDiffuse;
    uniform float amount;
    varying vec2 vUv;
 
    void main() {
      vec2 dir = vUv - vec2(0.5);
      float dist = length(dir) * amount;
      vec2 offset = normalize(dir) * dist;
 
      float r = texture2D(tDiffuse, vUv - offset).r;
      float g = texture2D(tDiffuse, vUv).g;
      float b = texture2D(tDiffuse, vUv + offset).b;
 
      gl_FragColor = vec4(r, g, b, 1.0);
    }
  `,
}
 
const caPass = new ShaderPass(chromaticAberrationShader)
composer.addPass(caPass)
 
// Animer l'intensité au scroll
gsap.to(caPass.uniforms.amount, {
  value: 0.01,
  scrollTrigger: {
    trigger: '#section-transition',
    start: 'top center',
    end: 'bottom center',
    scrub: true,
  },
})

L'aberration chromatique animée pendant les transitions de section donne un effet d'"entrée dans un autre espace" très efficace.

Resize avec le composer

window.addEventListener('resize', () => {
  camera.aspect = innerWidth / innerHeight
  camera.updateProjectionMatrix()
 
  renderer.setSize(innerWidth, innerHeight)
  composer.setSize(innerWidth, innerHeight)  // ← ne pas oublier le composer
})

Ordre des passes recommandé

RenderPass        ← toujours en premier
UnrealBloomPass   ← avant SMAA (bloom sur les jagged edges sinon)
SMAAPass          ← antialiasing
FilmPass          ← grain sur l'image déjà clean
ShaderPass (CA)   ← aberration chromatique
ShaderPass (vig)  ← vignette en dernier
OutputPass        ← toujours en dernier

Impact performance

Chaque passe lit et écrit un texture fullscreen. Sur mobile ou GPU intégré, trois passes peuvent diviser le framerate par deux.

Stratégie recommandée :

const isMobile = /Mobi|Android/i.test(navigator.userAgent)
 
if (!isMobile) {
  composer.addPass(bloom)
  composer.addPass(film)
}
// Toujours ajouter SMAA et OutputPass
composer.addPass(smaa)
composer.addPass(new OutputPass())

Le rendu est maintenant cinématographique. Le chapitre suivant plonge dans les shaders GLSL et les systèmes de particules — pour créer des effets entièrement custom qu'aucun matériau Three.js ne peut reproduire.

Précédent
Animer au scroll — Three.js + GSAP
Suivant
Shaders GLSL et systèmes de particules

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main