Dispersion par Prisme
Ouvrir l'expérience du prisme. Faites pivoter le prisme triangulaire équilatéral à la souris ou avec les touches fléchées. Le tiroir de paramètres permet d'ajuster l'angle de rotation et la dispersion de Cauchy, ou de rétablir les réglages de référence en un clic.
Modèle d'Optique Géométrique
La simulation suit, dans un plan, les rayons d’un faisceau blanc traversant un prisme équilatéral. Le calcul applique les lois de Snell-Descartes sous forme vectorielle pour chaque rayon incident, détectant la réfraction et la réflexion totale interne (TIR).
Lois vectorielles de réfraction et de réflexion
Soient la direction unitaire du rayon incident, la normale extérieure à l’interface et le rapport des indices des deux milieux.
-
Orienter la normale dans le sens opposé au rayon incident, de sorte que .
-
Calculer le discriminant de transmission :
-
Réflexion totale interne (TIR) : si , aucune transmission n’est possible. La direction réfléchie est :
-
Transmission réfractée : si , la direction transmise est :
export function refract(direction, outwardNormal, fromIndex, toIndex) {
const normal = dot(direction, outwardNormal) > 0 ? outwardNormal.map(x => -x) : outwardNormal;
const cosine = -dot(direction, normal);
const ratio = fromIndex / toIndex;
const discriminant = 1 - ratio * ratio * Math.max(0, 1 - cosine * cosine);
if (discriminant < 0) return null; // Réflexion totale interne
return unit(direction.map((x, i) => ratio * x + (ratio * cosine - Math.sqrt(discriminant)) * normal[i]));
}
Modèle de dispersion de Cauchy et échantillonnage des longueurs d’onde
Pour décomposer la lumière blanche en un spectre continu, l’indice de réfraction varie avec la longueur d’onde selon la formule empirique de Cauchy :
règle l’intensité de la dispersion ; quelle que soit sa valeur, l’indice de réfraction reste fixé à . Les 101 longueurs d’onde sont uniformément réparties entre 400 nm (violet) et 700 nm (rouge). Chacune comporte 21 rayons parallèles, soit rayons au total, avec au plus 12 interactions par rayon.
export function traceRay(origin, direction, polygon, index, width, height) {
const points = [origin];
let p = origin, v = direction;
for (let bounce = 0; bounce < 12; bounce++) {
const hit = intersectRay(p, v, polygon);
if (!hit) {
// Le rayon sort du prisme : prolonger jusqu’au bord de la vue
points.push(extendToBoundary(p, v, width, height));
break;
}
points.push(hit.point);
const exiting = dot(v, hit.normal) > 0;
const transmitted = refract(v, hit.normal, exiting ? index : 1, exiting ? 1 : index);
v = transmitted ?? v.map((x, i) => x - 2 * dot(v, hit.normal) * hit.normal[i]);
p = hit.point.map((x, i) => x + v[i] * 1e-4);
}
return points;
}
Dans Bending Light de PhET, ouvrez l’écran Prisms, placez un prisme triangulaire dans le faisceau, choisissez la lumière blanche et allumez la source. Changez le milieu ou l’angle du prisme pour relier la réfraction à chaque interface à la séparation des couleurs observée ici.
Optimisation GPU avec p5.js WebGL
Avec un contexte Canvas 2D, tracer chaque rayon séparément demanderait 2 121 appels de tracé par redessin, ainsi que la composition des couleurs. La rastérisation de Canvas 2D peut utiliser le CPU ou le GPU, selon le navigateur et la configuration du contexte. Ici, la géométrie des faisceaux est soumise par lots à WebGL pour réduire les appels de dessin. Le moteur actuel convertit toutes les coordonnées de faisceaux dans un tampon Float32Array unique (BeamMesh) :
// Chaque segment devient un rectangle : 2 triangles et 6 sommets
// Les sommets portent les coordonnées, la couleur prémultipliée et la distance normale
mesh.segment(p1, p2, [r, g, b, alpha], lineWidth);
// Un seul bufferSubData transfère tous les faisceaux, suivis d’un seul appel de dessin
gl.bufferSubData(gl.ARRAY_BUFFER, 0, mesh.data.subarray(0, mesh.count * VERTEX_SIZE));
gl.drawArrays(gl.TRIANGLES, 0, beamVertexCount);
L'ensemble des faisceaux est tracé en un seul appel de rendu GPU avec mélange additif et anticrénelage dans le fragment shader. Le fragment shader calcule l’adoucissement gaussien à partir de la distance normale signée transmise par les sommets, sans échantillonner de texture.
Découplage des interactions et restauration du contexte
- Interactions et état React : le glissement modifie directement l’angle dans une référence interne et redessine à la demande dans
requestAnimationFrame, évitant de relancer le rendu React à 60 FPS. Les contrôles de paramètres ne sont synchronisés qu’à la fin du geste. - Perte du contexte WebGL : les événements
webglcontextlostetwebglcontextrestoredpermettent de libérer puis de réinitialiser les shaders lors d’un changement d’écran sur mobile ou d’une réaffectation des ressources GPU, sans fuite de mémoire.