Construisons un moteur graphique 3D Couleurs

Bienvenue! C’est la sixième partie de notre série Construisons un moteur graphique 3D couvrant les bases des systèmes graphiques 3D. Cette fois, nous allons parler de la couleur et de la manière de l’ajouter à nos classes existantes. Nous allons également créer quelques fonctions utiles pour faciliter la gestion de l’éclairage. C’est le sens de notre prochaine et dernière partie..


résumer

Ajouter de la couleur à nos objets ne sera pas une entreprise trop lourde, aussi les deux seules classes sur lesquelles nous allons nous concentrer sont la classe de points et la classe de caméra. En guise de rappel, voici à quoi ils ressemblent:

 Classe de points Variables: num tuple [3]; // (x, y, z) Opérateurs: Point AddVectorToPoint (Vector); Point SubtractVectorFromPoint (Vector); Vecteur SubtractPointFromPoint (Point); NULL SetPointToPoint (Point); Fonctions: drawPoint; // dessine un point sur sa position Camera Class Vars: int minX, maxX; int minY, maxY; int minZ, maxZ; tableau objetsInWorld; // un tableau de tous les objets existants Fonctions: null drawScene (); // dessine tous les objets nécessaires à l'écran

Jusqu'à présent, notre moteur théorique dispose de presque toutes les bases, y compris:

  • Point et Vecteur classes (les blocs de construction de notre moteur).
  • Fonctions de transformation pour nos points.
  • UNE Caméra class (définit notre fenêtre d'affichage et élimine des points en dehors de l'écran).
  • Trois classes pour la pixellisation (segments de ligne, cercles et polygones).

Ajoutons maintenant de la couleur!


Couleur pour tous!

Notre moteur va gérer les couleurs en stockant leurs valeurs dans son Point classe. Cela permet à chaque point d’avoir sa propre couleur, ce qui simplifie considérablement les calculs d’ombrage et d’ombrage (du moins pour les utilisateurs - il est parfois moins efficace de coder un moteur de cette manière). Lorsque vous déterminez l'éclairage ou l'ombrage d'une scène, nous pouvons facilement fournir à la fonction une liste de points, puis passer à travers chacun d'eux, en utilisant leur distance de la lumière pour modifier leur couleur en conséquence..

L'un des moyens les plus courants de stocker des couleurs dans la programmation consiste à utiliser les valeurs rouge, verte et bleue pour créer la couleur souhaitée (cela s'appelle généralement le mélange de couleurs additif). En stockant une valeur de 0 à 255 dans chacun de ces segments de couleur, vous pouvez facilement créer une grande variété de couleurs. (C’est ainsi que la plupart des API déterminent la couleur. Il est donc logique d’utiliser cette méthode pour des raisons de compatibilité.). 

Ensuite, en fonction de l’API graphique utilisée, vous pouvez transmettre ces valeurs sous forme décimale (255,0,0) ou sous forme hexadécimale (0xFF0000 ou # FF0000). Nous allons utiliser le format décimal dans notre moteur, car il est un peu plus facile de travailler avec. De plus, si votre API graphique utilise des valeurs hexadécimales, elle a probablement une fonction de conversion de décimale en hexadécimale. Par conséquent, cela ne devrait pas poser de problème..

Pour que notre implémentation couleur commence, nous allons ajouter trois nouvelles variables à notre classe Point: rouge, bleu, et vert.  Il n’existe encore rien d’extraordinaire, mais voici ce que notre Point Le nouveau contour de la classe pourrait ressembler à ceci:

 Classe de points Variables: num tuple [3]; // (x, y, z) num rouge, vert, bleu; // peut être abrégé en r, g, b si vous le souhaitez Opérateurs: Point AddVectorToPoint (Vector); Point SubtractVectorFromPoint (Vector); Vecteur SubtractPointFromPoint (Point); NULL SetPointToPoint (Point); Fonctions: drawPoint; // dessine un point sur son tuple

C'est tout ce dont nous avons besoin pour stocker la couleur de notre point. Maintenant, nous avons juste besoin d’ajuster la fonction de tirage de notre caméra pour qu’elle utilise la couleur spécifiée..

Cela va changer radicalement en fonction de l'API graphique que vous utilisez, mais ils devraient tous avoir une fonction similaire à celle-ci:

 object.setColor (rouge, vert, bleu)

Si votre API graphique utilise des valeurs hexadécimales pour la couleur au lieu de décimales, votre fonction ressemblera à ceci:

 object.setColor (toHex (rouge, vert, bleu))

Ce dernier bit utilise un toHex () fonction (encore une fois, les noms de fonction diffèrent d’une API à l’autre) pour convertir une valeur RVB en une valeur hexadécimale de sorte que vous n’ayez pas à.  

Après avoir apporté ces modifications, vous devriez maintenant pouvoir avoir des points de couleur dans votre scène. Pour aller plus loin, nous allons ajuster chacune de nos classes de rastérisation afin que toute la forme puisse être colorée..

Pour ajouter cela à nos classes, nous devons simplement ajouter le traitement des couleurs à leurs fonctions constructeur. Cela pourrait ressembler à:

 lineSegment :: constructeur (startX, startY, endX, endY, rouge, vert, bleu) this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; this.red = rouge; this.green = green; this.blue = blue; 

Ensuite, il suffit de modifier sa fonction de points de retour pour qu’il définisse chaque point de son tableau avec la couleur spécifiée. La nouvelle fonction ressemblerait à ceci:

 function returnPointsInSegment () // crée une liste pour stocker tous les points du segment de ligne var pointArray = new Array (); // définit les variables de cette fonction en fonction des points de départ et d'arrivée de la classe var x0 = this.startX; var y0 = this.startY; var x1 = this.endX; var y1 = this.endY; // définit les différences de vecteurs et les autres variables requises pour l'algorithme de Bresenham var dx = Math.abs (x1-x0); var dy = Math.abs (y1-y0); var sx = (x0 & x1)? 1: -1; // étape x var sy = (y0 & y1)? 1: -1; // étape y var err = dx-dy; // récupère la valeur d'erreur initiale // définit le premier point du tableau pointArray.push (new Point (x0, y0, this.red, this.green, this.blue)); // Boucle de traitement principale while (! ((X0 == x1) && (y0 == y1))) var e2 = err * 2; // conserve la valeur d'erreur // utilise la valeur d'erreur pour déterminer si le point doit être arrondi à la valeur supérieure ou inférieure si (e2 => -dy) err - = dy; x0 + = sx;  if (e2 < dx)  err += dx; y0 += sy;  //add the new point to the array pointArray.push(new Point(x0, y0,this.red,this.green,this.blue));  return pointArray; 

Maintenant, chaque point du segment de ligne doit avoir la même couleur que celle qui a été transmise au segment de ligne. Vous pouvez également utiliser cette méthode pour définir les couleurs dans vos autres classes de rastérisation. Bientôt, votre scène s'animera en couleur.!  

Mettons nos nouvelles fonctionnalités en pratique en créant un programme pour les montrer.


Jouer avec 16,7 millions de couleurs

En utilisant un mélange de couleurs additif, nous pouvons facilement créer plus de 16,7 millions de couleurs différentes en utilisant simplement la méthode la plus simple (r, g, b) notation. Nous allons créer un programme qui tire parti de ce grand nombre de couleurs.

En appuyant sur les touches, nous allons permettre à l'utilisateur de contrôler individuellement les valeurs rouge, verte et bleue d'un objet, ce qui lui permet de le définir dans la couleur de son choix..

Les spécifications de notre programme sont les suivantes:

  • Dessine un objet sur l'écran.
  • Si l'utilisateur appuie sur UNE abaissez ensuite la valeur rouge de l'objet; s'ils appuient Q puis le soulève.
  • Si l'utilisateur appuie sur S abaissez ensuite la valeur verte de l'objet; s'ils appuient W puis le soulève.
  • Si l'utilisateur appuie sur abaissez ensuite la valeur bleue de l'objet; s'ils appuient E puis le soulève.
  • Redessine l'objet après que sa couleur ait été mise à jour.
  • Assurez-vous de limiter les valeurs de couleur, en évitant qu'elles ne descendent en dessous de 0 ou ne dépassent pas 255.

Gardant tout cela à l’esprit, examinons à quoi pourrait ressembler un aperçu de base de notre programme:

 main // configuration pour votre API graphique préférée ici // configuration pour la saisie au clavier (peut ne pas être nécessaire) ici var camera = new Camera (); // crée une instance de la classe camera // définit l'espace d'affichage de la caméra camera.minX = 0; camera.maxX = screenWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; // stocke nos couleurs afin qu'elles puissent être manipulées var rouge, vert, bleu; // dessine l'objet initial et le convertit en variable while (key! = esc) if (touche enfoncée = 'a') if (red> 0) red -; object.red = red; // objet redessiné if (touche enfoncée = 'q') if (rouge < 255)  red ++; object.red = red; //redraw object   if(key press = 's')  if(green > 0) vert -; object.green = green; // objet redessiné if (touche enfoncée = 'w') if (vert < 255)  green ++; object.green = green; //redraw object   if(key press = 'd')  if(blue > 0) bleu -; object.blue = blue; // objet redessiné if (touche enfoncée = 'e') if (blue < 255)  blue ++; object.blue = blue; //redraw object    

Maintenant, nous pouvons jouer avec notre objet et en faire n'importe quelle couleur que vous désirez!

Découvrez la démo ici - appuyez plusieurs fois sur la Q, W, E, UNE, S, et touches pour changer la couleur du carré.


Conclusion

Avec la couleur ajoutée à notre moteur, nous avons tout ce dont nous avons besoin pour gérer enfin un peu d’éclairage. Dans le prochain article, nous examinerons la création de sources d'éclairage et la création de fonctions permettant à ces sources d'affecter les couleurs de nos points. La profondeur que l'éclairage ajoute à un moteur est extrêmement satisfaisante, alors assurez-vous de vérifier!