Les courbes: de la 2D à la 3D

Comment faire qu'une courbe en 2D se transforme en un plan 3D

Stack technique

openGL + SDL en C++ sous Visual Studio

Etape 1 : Qu’est ce qu’une courbe ?

Damien Leroux Courbes3D 8

C’est un ensemble de points ! En openGL, plus on a de points, plus la courbe a une haute définition mais plus l’application sera lente. De ce fait, en openGL, on considère pour le dessin d’une courbe un nombre prédéfini de points : 20, 30, 100 ! Chaque point est relié au suivant par une ligne (GL_LINES). Les points dessinés possèdent 3 coordonnées : x, y et z. Dans le cadre du dessin d’une courbe, on placera notre dessin sur le plan XY afin de mettre Z à 0 (ce qui accélère les calculs avec une composante en moins).

Etape 2 : Configurer la caméra

Dans le cas où on souhaite simplement dessiner des courbes : gluOrtho2D(0,width,0,heigth); est suffisante car cette fonction délimite la zone visible par la caméra. En paramétrant cette fonction avec la largeur et la hauteur réelle de la fenêtre, on est assuré que les coordonnées de la souris qui vont être récupérées correspondront aux coordonnées à l’intérieur de la fenêtre.

Dans le cas où on souhaite pouvoir non seulement dessiner des courbes mais aussi avoir un rendu 3D (révolution et extrusion par exemple) :

  • On utilisera : gluPerspective(90,(double)width/heigth,0.1,2000); à la place de gluOrtho2D(0,width,0,heigth); pour déterminer la zone visible par la caméra. Ainsi, la caméra aura un angle de vue de 90 degrés, proportionné à la forme de l’écran et affichera tous les objets de la distance 0.1 à 2000.

Pour utiliser la caméra, il faut faire appel à la fonction qui place la caméra et qui lui indique où regarder : gluLookAt.

Etape 3 : passer de la 2D à la 3D

En réalité, je ne passe pas réellement de la 3D à la 2D, je reste toujours en 3D mais pour simuler la 2D, et faciliter l’affichage de courbes, je place ma camera sur un axe et je bloque son mouvement. Ainsi la caméra se retrouve face à un seul plan, comme en 2D. Mon mode 3D consiste à débloquer le mouvement de la caméra afin de lui permettre de regarder le centre de la scène sous n’importe quel angle.

Damien Leroux Courbes3D DeplacementCamera

Expliqué comme cela, le passage de la 3D à la 2D, et vice versa semble très simple. Cependant un paramètre influence grandement l’efficacité du rendu : la distance de la caméra par rapport au centre de la scène. En effet : En 2D réel, gluOrtho2D permet de délimiter la zone visible de la caméra et donc de dessiner nos courbes en conséquence. En 2D simulé, cette zone n’est pas spécifiée, elle est automatiquement calculée par la configuration de gluPerspective() (angle de vision) et de gluLookAt() (position de la camera).  En effet, plus l’angle de vision est grand et plus la surface visible est grande. De même, plus on s’éloigne de l’endroit que l’on regarde, plus la surface visible est grande. Comment donc connaitre la zone dans laquelle on dessine ? Il faut utiliser la trigonométrie. En effet, lorsque la caméra observe une position, elle le fait avec un angle précis et à une distance précise elle aussi. On obtient donc un triangle rectangle comme dans l'image ci-contre.

Ainsi, si on veut dessiner nos courbes sur un plan qui va de 500 à -500, on règle la distance de la caméra en fonction.Si le plan sur lequel on dessine n’est pas carré mais rectangulaire, on prend la distance la plus grande entre deux cotés opposés. Le width/heigth de : gluPerspective(90,(double)width/heigth,0.1,2000) se chargera de faire le reste.

Etape 4 : vouloir mettre un fond

Il n’y a rien de plus simple que de mettre un fond en 2D lorsque l’on utilise gluOrtho(). On créé un quad de la taille de l’écran et on pose une texture dessus. La taille de ce quad est celle décrite dans gluOrtho2D(0,width,0,heigth). En 3D, il faut :

Damien Leroux Courbes3D DeplacementPlan

  • Positionner un quad au centre du repère.
  • Calculer la taille du quad qui va contenir la texture de fond. Pour calculer cette taille on utilise le même procédé que celui décrit en fin d’étape 3 sauf qu’on connait la distance qui sépare la caméra de la position qu’elle observe et que l’on souhaite trouver la taille du plan. La distance à utiliser doit prendre en compte le fait que l’on va éloigner le quad du centre du repère afin qu’il ne coupe pas d’objets. Par exemple, la distance sera : distancePlan + 1000.
  • On déplace ensuite le quad en fonction de la distance choisie (par exemple 1000).
  • Orienter le quad en fonction de la position de la caméra. Dans mon système de caméra, j’utilise directement les deux angles de rotation de ma caméra sur mon quad afin qu’il se retrouve toujours face à la caméra.

En OpenGL, un procédé de redimensionnement des textures est appliqué aux textures non carrées et dont la largeur n’est pas un multiple de deux, ce qui a pour effet de rendre le texte (s’il y en a sur l’image) flou. Qui plus est, afin d’éviter les effets d’éloignements (de floutage) dus à la 3D, il ne faut pas utiliser de mipmap sur la texture.

Etape 5 : dessiner une courbe

Pour ce faire il faut cliquer sur l’écran et récupérer les coordonnées qui sont celles de votre fenêtre. Il faut donc les adapter à votre système. Pour récupérer ces coordonnées j’utilise SDL. Ainsi, lorsque event.type=SDL_MOUSEBUTTONUP et event.button.button == SDL_BUTTON_LEFT, je récupère event.motion.x pour x et event.motion.y pour y.

Si votre système de lecture des ordonnées ne se fait pas dans le même sens que le fait SDL, vous devez faire : hauteur_ecran- event.motion.y pour récupérer y. Une fois les coordonnées récupérées, il faut utiliser des formules afin d’interpoler une courbe entre chaque point. Ces interpolations peuvent être calculées de différentes façons. Dans la vidéo plus haut, j’ai recodé les interpolations selon les principes de Bézier et B-spline. Il est aussi possible de recoder les fonctions selon Hermite ou newton (de grands classiques plus simples que Bézier et B-spline).

Damien Leroux Courbes3D 3

Pour ceux qui ce demande ce que signifie C0, C1 et C2, c’est une façon de raccorder les courbes entre elles. En effet, plus il y a de points, plus le calcul de courbes de Bézier est lourd. De ce fait, il est plus simple de faire plusieurs morceaux de courbes et de rattacher ensuite ces courbes entres elles. Ainsi C0 rattache deux courbes sans interpolations. C1 rattache deux courbes en prenant en compte la forme de la courbe et C2 est comme C1 mise à part que l’on prend en compte l’accélération de la courbe.

Etape 6 : obtenir des formes

Dessiner des courbes est amusant mais ne sert pas à grand-chose. Il est donc encore plus amusant de pouvoir obtenir des formes grâce à ces courbes. J’ai personnellement voulu expérimenter l’extrusion et la révolution:

Damien Leroux Courbes3D 6

Pour l’extrusion, c’est très simple. Il faut réutiliser tous les points sauvegardés lors de l’obtention d’une courbe. Ces points sont sur un plan 3D et possèdent donc une coordonnée à 0. Il faut donc dessiner des quads entre chacun des points profondeur 0 et chacun des points profondeur 100. Dans ma vidéo, le but étant d’illustrer le principe, la profondeur d’extrusion n’est pas paramétrable mais on pourrait très bien choisir une valeur de profondeur et l’appliquer. En OpenGL, ces quads peuvent être pleins ou vides, ce qui permet de bien apprécier la géométrie de la forme obtenue.  Lorsque les quads sont pleins, il est mieux de rajouter de la lumière car sans elle, la forme parait toute plate.

Damien Leroux Courbes3D 5

Pour la révolution, il faut pour chaque point de la courbe recalculer sa position avec une rotation R par rapport à l’axe du centre. La valeur de R doit évoluer entre 0 et 360. Il est ainsi possible d’incrémenter R à chaque pas de 30° par exemple. Les calculs pour avoir les nouvelles coordonnées sont les suivants :

newX = cos(angleParcourt)*oldX;
newY = sin(angleParcourt)*oldX;
newZ=oldY;

Il faut ensuite relier chacun des points calculés avec les points précédents à l'aide de quads.

Résultat final

Damien Leroux Courbes3D 1

Damien Leroux Courbes3D 2

Damien Leroux Courbes3D 4

Damien Leroux Courbes3D 7

Damien Leroux Courbes3D 9