Formation OpenGL |
|
Nous décrivons comment avec une fonction polynomiale de degré
3, on peut construire simplement une courbe qui interpole deux points et leurs
tangentes.
Dans le cadre de la modélisation 3D, nous allons définir
des courbes dans l'espace de dimension 3 : IR^3.
Mais c'est un cas particulier, la dimension de l'espace d'arrivée n'est
pas nécessairement 3, elle peut être 2 si la courbe est définie
sur un plan ou pour des coordonnées de texture sur une surface, 4 pour
des couleurs en mode RGBA, et même 1 pour des niveaux de gris.
L’ingénieur français Pierre Bézier qui travaillait chez Renault a eu en 1960 l’idée géniale de définir
simplement une courbe polynomiale à l’aide de Points de contrôle,
et de fonctions de mélange.
Ses courbes se retrouvent maintenant dans tous les logiciels de dessin, et sont à la base de la définition des polices TrueType.
Bézier est décédé en 1999 à 89 ans. Vous pouvez en savoir plus sur son histoire,
et vous pouvez aussi voir une de ses oeuvres numériques.
Notez que le paramètre u prend des valeurs réelles, tandis que les coefficients ai sont des points de l’espace IR^3.
Si le degré n est grand, le temps de calcul de C(u) sera important,
c'est pourquoi on s'intéresse au cas particulier des courbes polynomiales
de degré 3, les cubiques. L'expression est alors :
C(u) = a0 +a1.u +a2.u^2 + a3.u^3
Voici en rouge et vert la représentation d’une courbe polynomiale
cubique étant donnés 4 points ai= (xi, yi, zi). La portion de
courbe en vert correspond à l'intervalle [0,1].
On remarque
que pour u=0, C=a0, mais dehors de cette propriété, les points
ai n’ont pas de signification géométrique particulière...
C'est pourtant un cas particulier des courbes polygonales cubiques qui permet
de réaliser l'interpolation de 4 points appelés Points de
contrôle:
Considérons l’exemple suivant, dans lequel on a quatre points
Pi.
Les segments
de droite en noir qui relient les Pi s'appellent le Polygone de contrôle.
Intéressons-nous plus particulièrement à la portion représentée en vert, celle qui correspond à l'intervalle [0,1]. Cette portion de courbe a des propriétés intéressantes :
De plus, les courbes de Bézier sont invariantes par translation, rotation, et changement d’échelle, c’est à dire qu’on applique une de ces transformations à la courbe en l’appliquant à ses points de contrôle.
Cette expression fait apparaître les points de contrôle Pi. On pourrait bien entendu développer l'expression pour retrouver les coefficients ai qui sont, notons-le, différents des points Pi. Mais cette forme est plus intéressante, et elle a même une expression générique :
C(u) = B0,3(u).P0 + B1,3(u).P1+ B2,3(u).P2+ B3,3(u).P3, que l’on peut
écrire pour une courbe de Bézier de degré n :
C(u) = Somme_[i=0...n] (Bi,n(u).Pi)
On peut aussi les écrire
Bi,n(u) = Ci, n . u^i . (1 – u)^(n-i)
Rappelez-vous, Ci, n = n! / (i! . (n – i )!), est le nombre
de combinaisons à i éléments parmi n, il s’agit
du nombre d’ensembles à i éléments que l’on
peut faire avec un n éléments.
Avec n=3 éléments {a, b, c}, par exemple, on peut faire 3 ensembles
à i=2 éléments distincts : {a, b}, {a, c} et {b, c},
on note C2,3 = 3.
Autres rappels : à propos des factorielles, on a 0! = 1, et à
propos des puissances, 0^0 = 1 et 0^i = 0 pour i>0.
Les fonctions de Bernstein ont des propriétés intéressantes :
• l'activer, | ex. glEnable(GL_MAP1_VERTEX_3); active l'évaluateur 1D des sommets 3D |
• définir la courbe C(u) par ses points de contrôle | GLfloat ctrlpoints[4][3]
= |
• et ensuite utiliser la fonction glEvalCoord1*() à la place de glVertex*() pour dessiner la courbe. | //Code (*) //n correspond à la discrétisation de la courbe glBegin(GL_LINE_STRIP); for (i = 0; i <= n; i++) glEvalCoord1f((GLfloat) i/((float)n); glEnd(); |
La fonction glMap1*(GLenum target,
GLdouble u1, GLdouble u2, GLint stride, GLint ordre,
const GLdouble *points) définit un évaluateur 1D.
target peut prendre les valeurs suivantes : GL_MAP1_VERTEX_3,
GL_MAP1_VERTEX_4, GL_MAP1_INDEX, GL_MAP1_COLOR_4, GL_MAP1_NORMAL, GL_MAP1_TEXTURE_COORD_1,
GL_MAP1_TEXTURE_COORD_2, GL_MAP1_TEXTURE_COORD_3, and GL_MAP1_TEXTURE_COORD_4
Il est possible d'évaluer plusieurs évaluateurs à la fois, pour générer par exemple une position et une couleur, ou bien une position et une coordonnée de texture.
N.B.1 C'est donc OpenGL qui se charge du calcul des polynomes de Bernstein et de leur mélange !.
N.B.2 On ne peut pas récupérer les coordonnées des points générés par l'évaluateur. Ils sont directement restitués pour dessiner la courbe.
N.B.3 Le code (*) utilisé pour dessiner la courbe peut-être
remplacé par deux appels :
glMapGrid1f(n,
0.0, 1.0); //Pour définir une grille régulière de 0.0
à 1.0 en n étapes
glEvalMesh(GL_LINE, 0, n);
//Pour appliquer cette grille aux évaluateurs activés.
Modifiez le programme précédent pour générer
une animation en déplaçant un des points de contrôle
sur un cercle de rayon unitaire.
Un carreau de surface de Bézier bicubique est défini par
un ensemble de 4x4 points de contrôle Pi,j. Son équation est
:
S(u,v) = Somme_[i=0...3] (Somme_[j=0...3] (Bi,3(u).Bj,3(v).Pi,j))
dans laquelle on retrouve les polynomes de Bernstein de degré 3.
N.B. L'équation est un produit tensoriel: pour u (resp.v) fixé,
on retrouve l'expression d'une courbe de Bézier de degré 3
en v (resp. u).
Bicubique signifie cubique en u et en v.
![]() |
Sur ce schéma, on a
|
Pour calculer et afficher une surface de Bézier, on utilise un évaluateur : il faut
• l'activer, | ex. glEnable(GL_MAP2_VERTEX_3); active l'évaluateur 2D des sommets 3D |
• définir la surface S(u,v) par ses points de contrôle | GLfloat ctrlpoints[4][4][3]
= { |
• et ensuite utiliser la fonction glEvalCoord2*() pour dessiner la surface. | //Code (**) //n et m correspondent à la discrétisation de la surface for (j = 0; j <= n; j++) { glBegin(GL_LINE_STRIP); for (i = 0; i <= m; i++) glEvalCoord2f(i/((GLfloat)m), j/((GLfloat)n)); glEnd(); glBegin(GL_LINE_STRIP); for (i = 0; i <= m; i++) glEvalCoord2f(j/((GLfloat)n), i/((GLfloat)m)); glEnd(); } |
N.B. Le code (**) utilisé pour dessiner la surface peut-être
remplacé par deux appels :
glMapGrid2f(n,
0.0, 1.0, m, 0.0, 1.0); //Pour définir une grille régulière
de 0.0 à 1.0 en n étapes en u et m étapes en v
glEvalMesh2(GL_FILL, 0,
n, 0, m); //Pour appliquer cette grille aux évaluateurs activés.
On peut également générer des coordonnées de texture, comme dans le programme texturesurf.c. Modifiez-le pour afficher snoopy sur la surface.