Un rayon qui cherche une sphère
Le lancer de rayons calcule une image en envoyant, pour chaque pixel, un rayon de lumière dans la scène et en cherchant ce qu’il touche. La première question est toujours la même : où ce rayon rencontre-t-il une sphère ?
La réponse tient en une équation du second degré, que vous savez déjà résoudre avec un discriminant.
À la fin de cette leçon
Remarque
Je peux calculer où un rayon touche une sphère, ou dire qu’il la rate.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Rayon | une demi-droite : une origine et une direction |
| Sphère | les points à la même distance d’un centre |
| Discriminant | le nombre qui dit combien de solutions a l’équation |
| Distance t | de combien on avance le long du rayon |
| Produit scalaire | la somme des produits des coordonnées deux à deux |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un rayon est une origine et une direction
Un rayon part d’un point O, l’origine, et suit une direction D. Le point atteint après avoir avancé d’une distance t est P = O + t × D, calculé coordonnée par coordonnée.
Avec t = 0 on est à l’origine. Plus t grandit, plus on s’éloigne. Un t négatif serait derrière l’origine : on l’écarte.
Quand un point est-il sur la sphère ?
Une sphère de centre C et de rayon r est faite de tous les points P dont la distance à C vaut r. Autrement dit, la distance de P à C, élevée au carré, vaut r au carré.
Cherchons les t pour lesquels le point P = O + t × D est sur la sphère. On remplace P et on développe.
Exemple travaillé : l’équation du rayon
| Nom | Calcul | Rayon vers +z, sphère en (0, 0, 5), rayon 2 |
|---|---|---|
| OC | origine moins centre | (0, 0, −5) |
| a | D · D | 1 |
| b | 2 × (OC · D) | −10 |
| c | OC · OC − r × r | 25 − 4 = 21 |
| Discriminant | b × b − 4 × a × c | 100 − 84 = 16 |
| t le plus proche | (−b − racine du discriminant) / (2 × a) | (10 − 4) / 2 = 3 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Résoudre l’équation pour ce rayon
docker exec jupyter python3 -c "o=(0,0,0); d=(0,0,1); c=(0,0,5); r=2; oc=tuple(o[i]-c[i] for i in range(3)); a=sum(v*v for v in d); b=2*sum(oc[i]*d[i] for i in range(3)); k=sum(v*v for v in oc)-r*r; disc=b*b-4*a*k; print(a,b,k,disc,(-b-disc**0.5)/(2*a))"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Le discriminant dit si le rayon touche
Si le discriminant est négatif, l’équation n’a pas de solution : le rayon rate la sphère. S’il vaut 0, il y a une solution : le rayon frôle la sphère. S’il est positif, il y en a deux : le rayon entre et ressort.
On garde la plus petite solution positive, car c’est le premier point que le rayon rencontre.
Voir le discriminant changer avec la hauteur
# Un rayon part de (0, hauteur, 0) vers +z. Sphère : centre (0, 0, 5), rayon 2.
for hauteur in (0, 2, 3):
oc = [0, hauteur, -5]
b = 2 * (oc[2] * 1)
c = oc[0] ** 2 + oc[1] ** 2 + oc[2] ** 2 - 2 * 2
print("hauteur", hauteur, "discriminant", b * b - 4 * c)
# Pour quelle hauteur le rayon touche-t-il la sphère sans la traverser ?
Une erreur fréquente
Attention
Beaucoup pensent qu’un discriminant positif suffit pour dire que le rayon touche. Ce n’est pas juste : les deux solutions peuvent être négatives, et la sphère est alors derrière l’origine. Vérifiez que la solution gardée est positive.
Un rayon qui rate la sphère
docker exec jupyter python3 -c "o=(0,0,0); d=(0,1,0); c=(0,0,5); r=2; oc=tuple(o[i]-c[i] for i in range(3)); a=sum(v*v for v in d); b=2*sum(oc[i]*d[i] for i in range(3)); k=sum(v*v for v in oc)-r*r; print(b*b-4*a*k)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Un rayon par pixel dessine une silhouette
Dans un lancer de rayons, chaque pixel envoie son rayon, tout droit vers l’avant. Un pixel est allumé quand son rayon touche la sphère.
Pour une sphère de rayon 4 centrée sur le pixel (8, 4), un rayon touche si le pixel est à 4 au plus du centre. Le résultat est un disque, la silhouette de la sphère vue de face.
Dessiner la silhouette en texte
docker exec jupyter python3 -c "print('\n'.join(''.join('#' if (x-8)**2+(y-4)**2<=16 else '.' for x in range(17)) for y in range(9)))"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce que la commande a affiché
........#........
......#####......
.....#######.....
.....#######.....
....#########....
.....#######.....
.....#######.....
......#####......
........#........