Vectors (direction)
Un vecteur est un objet généralisant plusieurs notions :- La Géométrie
- →
Translations→ etc. - L'Algèbre
- → "solution" d'un
système d'équationsà plusieurs inconnues. - La Physique
- →
Forces,Vitesses,Accélérations→ etc.
La notion de vecteur est le fondement de l'algèbre linéaire, le rendu 3D est de l'algèbre linéaire appliqué.
Un vecteur est un élément d'un espace vectoriel : on peut l'additionner avec un autre vecteur ou le multiplier par un scalaire (un nombre seul), composante par composante.
Par exemple un orderer pair (couple), un tuple (triplet) de nombres réels, peuvent être vu comme un vecteur.
UN vecteur = UNE direction & UNE magnitude (également connue sous le nom de norme, force ou longueur).
Vecteur + Vecteur
\( \vec{D} \) \( = \begin{pmatrix} 4 \\ 0 \\ 8 \end{pmatrix}, \vec{u} = \begin{pmatrix} 1 \\ 1 \\ 23 \end{pmatrix} → \) \( \vec{D} \) + \( \vec{u} = \begin{pmatrix} 4 + 1 \\ 0 + 1 \\ 8 + 23 \end{pmatrix} = \begin{pmatrix} 5 \\ 1 \\ 31 \end{pmatrix}\)
Le vecteur b peut avoir la même origine que le vecteur a. Le résultat sera le même.
Puisque les vecteurs représentent des directions, l'origine du vecteur ne change pas sa valeur. Ci-dessus nous pouvons voir que les vecteurs A et D sont égaux même si leur origine est différente :
En notation mathématique, on affichage les vecteurs dans les formules avec une fléche au dessus du nom du vecteur.
\( \vec{A} \) = \( \begin{pmatrix} 4 \\ 0 \end{pmatrix} \qquad \) \( \vec{D} \) = \( \begin{pmatrix} x \\ y \\ z \end{pmatrix} \)
Vecteurs en C++
/// C++ — Exemple — update:18/05/26
simd::float2 uv = { 0.5f, 0.5f };
simd::float3 normal = { 0.0f, 1.0f, 0.0f };
simd::float4 clipPos = { x, y, z, 1.0f };
// Swizzling — accès aux composantes dans n'importe quel ordre
simd::float3 rgb = color.xyz;
simd::float2 yx = uv.yx;
Opérations courantes
Scalaire + Vecteur
\[ s = 1 \qquad s + \vec{v} = \begin{pmatrix} s + v_x \\ s + v_y \\ s + v_z \end{pmatrix} = \begin{pmatrix} 1 + v_x \\ 1 + v_y \\ 1 + v_z \end{pmatrix} = 1 + \begin{pmatrix} v_x \\ v_y \\ v_z \end{pmatrix} = \begin{pmatrix} 1 \\ 1 \\ 1 \end{pmatrix} + \begin{pmatrix} v_x \\ v_y \\ v_z \end{pmatrix} \]
Scalaire * Vecteur
\[ s \cdot \vec{v} = \begin{pmatrix} s \cdot v_x \\ s \cdot v_y \\ s \cdot v_z \end{pmatrix} = s \cdot \begin{pmatrix} v_x \\ v_y \\ v_z \end{pmatrix}\]
Il en va de même pour la soustraction et la division.
Un scalaire est un chiffre unique. Lors de l'ajout/soustraction/multiplication ou division d'un vecteur avec un scalaire, nous ajoutons/soustrayons/multiplions simplement ou divisons chaque élément du vecteur par le scalaire. Pour ajouter, cela ressemblerait à ceci :
Vecteur négatif
La négation d'un vecteur donne un vecteur dans la direction inversée. Pour annuler un vecteur, nous ajoutons un signe moins à chaque composant :
\( -\vec{v} = -\begin{pmatrix} v_x \\ v_y \\ v_z \end{pmatrix} = \begin{pmatrix} -v_x \\ -v_y \\ -v_z \end{pmatrix} \)
La multiplication par -1 revient à trouver son vecteur opposé.
\( -1 \cdot \vec{v}=-\vec{v} \)
Si le vecteur \( \vec{v} \) est défini par ses composantes dans le plan \( \vec{v} = (x, y) \) :
\( -1 \cdot \vec{v}=-1 \cdot (x,y) = (-x,-y) \)
Vecteurs en MSL
/// MSL — Exemple — update:18/05/26
float2 uv = float2(0.5, 0.5);
float3 normal = float3(0.0, 1.0, 0.0);
float4 clipPos = float4( x, y, z, 1.0);
// Le swizzle fonctionne aussi côté GPU avec simd
float3 rgb = color.rgb; // float3(color.red, color.green, color.blue)
float2 yx = uv.yx; // inverse x et y
float4 aaaa = color.aaaa; // répéter le canal alpha
// Swizzle en écriture
float4 c;
c.rgb = float3(1.0, 0.5, 0.0);
c.a = 1.0;
float3 v = float3(1.0, 2.0, 3.0);
float3 scaled = v * 2.0; // (2, 4, 6) — magnitude ×2 sans changer la direction
float3 neg = v * -1.0; // (-1, -2, -3) — direction inversée
float3 shifted = v + 0.5; // (1.5, 2.5, 3.5)
// direction lumière → fragment
float3 toLight = normalize(lightPos - fragPos);
Opérations vectorielles fondamentales
Produit scalaire (dot product)
Le produit scalaire de deux vecteurs est égal au produit scalaire de leurs longueurs multiplié par le cosinus de l'angle entre eux :\( \vec{v} \cdot \vec{k} = |\vec{v}| \cdot |\vec{k}| \cdot \cos\theta \)
Si \( \vec{V} \) et \( \vec{K} \) sont des vecteurs unité, leur longueur valent 1, ce qui réduit la formule à :\( \hat{v} \cdot \hat{k} = 1 \cdot 1 \cdot \cos\theta = \cos\theta \)
Le produit scalaire ne définit alors plus que l'angle entre les deux vecteurs. Le cosinus vaut 0 à 90° et 1 à 0° — ce qui permet de tester facilement si deux vecteurs sont orthogonaux ou parallèles. On peut aussi calculer l'angle entre deux vecteurs non-unitaires, mais il faut alors diviser par leurs longueurs pour isoler le cosinus de l'angle thêta.Le produit scalaire est une multiplication composante par composante dont on somme les résultats :
\( \begin{pmatrix} 0.6 \\ -0.8 \\ 0 \end{pmatrix} \cdot \begin{pmatrix} 0 \\ 1 \\ 0 \end{pmatrix} = (0.6 \times 0) + (-0.8 \times 1) + (0 \times 0) = -0.8) \)
Pour obtenir l'angle, on applique
\( \cos^{-1} : \arccos(-0.8) \approx 143.1°. \)Produit scalaire — quantité de lumière reçue
Le calcul le plus classique en shading est le produit scalaire entre :
- la normale \( \vec{n} \)
- la direction de la lumière \( \vec{l} \)
\[ \text{light} = \max(0, \vec{n} \cdot \vec{l}) \]
Le produit scalaire vaut :
- \(1\) → la surface regarde directement la lumière
- \(0\) → la lumière est parallèle à la surface
- \(<0\) → la lumière est derrière la surface
Exemple :
\[ \vec{n} = \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix} \qquad \vec{l} = \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix} \]
\[ \vec{n} \cdot \vec{l} = 0 \cdot 0 + 0 \cdot 0 + 1 \cdot 1 = 1 \]
La face reçoit alors la lumière maximale.
Produit vectoriel (cross product)
Défini uniquement en 3D. Prend deux vecteurs non-parallèles et produit un troisième vecteur orthogonal aux deux. Si les deux entrées sont elles-mêmes orthogonales, on obtient 3 vecteurs mutuellement orthogonaux.
\( \begin{pmatrix} A_x \\ A_y \\ A_z \end{pmatrix} \times \begin{pmatrix} B_x \\ B_y \\ B_z \end{pmatrix} = \begin{pmatrix} A_y \cdot B_z - A_z \cdot B_y \\ A_z \cdot B_x - A_x \cdot B_z \\ A_x \cdot B_y - A_y \cdot B_x \end{pmatrix} \)
Le résultat sera toujours un vecteur orthogonal aux deux entrées.float3 a = float3(1.0, 0.0, 0.0);
float3 b = float3(0.0, 1.0, 0.0);
float3 sum = a + b; // (1, 1, 0)
float3 diff = b - a; // (-1, 1, 0) — direction de a vers b
// Utilisation classique — éclairage diffus de Lambert :
float lambert = saturate(dot(N, L));
// Produit scalaire (dot product)
// dot(a, b) = |a| × |b| × cos(θ)
// Si les deux vecteurs sont normalisés → cos(θ) directement.
// dot = 1 : même direction | dot = 0 : perpendiculaires
// dot = -1 : directions opposées
float d = dot(a, b); // 0.0 (perpendiculaires)
// Produit vectoriel (cross product)
// Retourne un vecteur perpendiculaire aux deux vecteurs d'entrée.
// La longueur = |a| × |b| × sin(θ).
// L'ordre compte : cross(a, b) = -cross(b, a)
float3 n = cross(a, b); // float3(0, 0, 1)
// Calcul de la normale d'un triangle :
float3 faceNormal = normalize(cross(v1 - v0, v2 - v0));
// Norme (longueur)
// length(v) = sqrt(vx² + vy² + vz²)
float len = length(a); // 1.0
// Normalisation
// Ramène le vecteur à longueur 1 — indispensable pour les normales
// et les directions. normalize(v) = v / length(v)
float3 dir = normalize(a);
// Distance
float dist = distance(float3(0.0), float3(1.0));
// Réflexion
// reflect(I, N) = I - 2 × dot(N, I) × N
// I = vecteur incident, N = normale de surface (normalisée)
float3 r = reflect(incident, normal);
// Réfraction
// refract(I, N, eta) — eta = n1/n2 (rapport des indices)
float3 t = refract(incident, normal, 1.0 / 1.5);
// ── Interpolation linéaire ────────────────────────────────────
float3 c = mix(a, b, 0.5); // mi-chemin entre a et b
\[ \|\vec{E}\| = \sqrt{v_x^2 + v_y^2 + v_z^2} \]
Où \( \|\vec{v}\| \) désigne la longueur (length) du vecteur. On étend naturellement à la 3D en ajoutant \( v_z^2 \) à 0. La longueur du vecteur (4, 3) vaut :
\[ \|\vec{E}\| = \sqrt{4^2 + 3^2} = \sqrt{16 + 9} = \sqrt{25} = 5 \]
Vecteur unité & Normalisation
Un vecteur unité est un vecteur dont la longueur est exactement 1. On le calcule en divisant chaque composante par la longueur du vecteur :
\[ \hat{v} = \frac{\vec{v}}{\|\vec{v}\|} \]
On appelle ça normaliser un vecteur. Les vecteurs unité s'affichent avec un chapeau \(\hat{v}\) et sont généralement plus pratiques à manipuler — notamment quand seule la direction compte, pas la magnitude (la direction ne change pas quand on modifie la longueur d'un vecteur).
float3 v = float3(4.0, 2.0, 0.0);
float3 nrm = normalize(v); // (0.894, 0.447, 0)
float3 direction = normalize({1, 0, 1}) → {0.707f, 0.f, 0.707f}
float3 target = normalize({1, -1, 2}) → {0.408f, -0.408f, 0.816f}
// Les composantes sont des flottants quelconques dans [-1.0, 1.0].
// La seule contrainte c'est : x² + y² + z² = 1.0f
// Dans la documentation : simd_normalize
// renvoie un vecteur pointant dans la même direction que le vecteur fourni, avec une longueur de 1
// Attention : normalize(float3(0)) → NaN — toujours vérifier en amont
float3 safe = length(v) > 0.0001 ? normalize(v) : float3(0, 1, 0);
\[ \frac{0}{0} \qquad \infty - \infty \qquad \sqrt{-1} \]
Défini par la norme IEEE 754, il indique une opération invalide plutôt qu'un numéro fonctionnel.Vecteurs vs Matrices — comparaison rapide
Un vecteur est une liste de n valeurs. Une matrice est une grille de m × n valeurs. En fait, un vecteur colonne est juste un cas particulier de matrice (n × 1). La différence fondamentale est leur rôle :
- Vecteur
- → Représente un point ou une direction dans l'espace.
- Matrice
- → Représente une transformation : translation, rotation, scale, projection…
- → Composée de 4 vecteurs → x, y, z & translation/position
En rendu 3D, les matrices s'appliquent aux vecteurs via la multiplication matricielle. Multiplier une matrice 4×4 par un vecteur float4 produit un nouveau vecteur transformé :
\[ M \cdot \vec{v} = \vec{v'} \qquad \begin{pmatrix} m_{00} & m_{01} & m_{02} & m_{03} \\ m_{10} & m_{11} & m_{12} & m_{13} \\ m_{20} & m_{21} & m_{22} & m_{23} \\ m_{30} & m_{31} & m_{32} & m_{33} \end{pmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ w \end{pmatrix} = \begin{pmatrix} x' \\ y' \\ z' \\ w' \end{pmatrix} \]
La 4ème composante w
Le vecteur float4 utilisé avec une matrice 4×4 suit la convention des coordonnées homogènes :
w = 1- → Le vecteur est un point. Les translations de la matrice s'appliquent.
w = 0- → Le vecteur est une direction. Les translations sont ignorées (normales, vents, …).
// Point dans l'espace (translation active)
float4 point = float4(pos, 1.0);
// Direction pure (translation sans effet)
float4 direction = float4(dir, 0.0);
Récapitulatif
| Concept | Type MSL | Rôle |
|---|---|---|
| Vecteur 2D | float2 |
UV, position 2D, taille |
| Vecteur 3D | float3 |
Position, normale, couleur RGB |
| Vecteur 4D | float4 |
Couleur RGBA, coords homogènes |
| Matrice 4×4 | float4x4 |
MVP, Model, View, Projection |
| Matrice 3×3 | float3x3 |
Normal matrix (sans translation) |
En pratique, pour du code partagé (_shared), on utilisera des simd::float4 afin d'assurer un alignement mémoire de 16 bytes des deux côtés, comme avec OpenGL.