Construisons un moteur graphique 3D espaces et culling

Bienvenue! Ceci est la troisième partie de notre série sur les moteurs graphiques 3D. Si vous arrivez jusque-là dans la série, vous serez heureux de savoir que cette pièce sera beaucoup plus claire sur l'aspect mathématique des moteurs 3D et se concentrera plutôt sur des choses plus pratiques, notamment l'ajout d'un un système de rendu de base.

Pointe: Si vous n'avez pas encore lu les deux premières parties, je vous suggère fortement de le faire avant de continuer..

Vous pouvez également obtenir une aide supplémentaire sur Envato Studio, où vous pouvez choisir parmi une large gamme de services de conception et de modélisation 3D de haute qualité auprès de fournisseurs expérimentés.. 

Services de conception et de modélisation 3D sur Envato Studio

résumer

Tout d’abord, examinons les classes que nous avons créées jusqu’à présent:

Classe de points Variables: num tuple [3]; // (x, y, z) Opérateurs: Point AddVectorToPoint (Vector); Point SubtractVectorFromPoint (Vector); Vecteur SubtractPointFromPoint (Point); NULL SetPointToPoint (Point); // déplace le point vers le point spécifié Fonctions: drawPoint; // dessine un point sur son n-ucle de position Classe Vector Variables: num tuple [3]; // (x, y, z) Opérateurs: vecteur AddVectorToVector (vecteur); Vecteur SubtractVectorFromVector (vecteur); Vecteur RotateXY (degrés); Vecteur RotateYZ (degrés); Vecteur RotateXZ (degrés); Échelle vectorielle (s0, s1, s2); // params: mise à l'échelle le long de chaque axe

L'utilisation de ces deux classes à elles seules s'est révélée un peu compliquée jusqu'à présent, et dessiner chaque point possible peut épuiser assez rapidement la mémoire de votre système. Pour résoudre ces problèmes, nous allons introduire une nouvelle classe dans notre moteur de jeu: le caméra.

Notre caméra va être où tout notre rendu se produit, exclusivement; sa va à cueillir tous nos objets à l'écran, et il va également gérer une liste de tous nos points.

Mais avant que nous puissions arriver à tout cela, nous devons d'abord parler un peu de la réforme.


Londres Culling

Culling, par définition, est la sélection d'objets à partir d'un groupe d'objets plus important. Pour notre moteur de jeu, la petite sélection que nous prenons correspondra aux points que nous voulons dessiner à l'écran. Le plus grand groupe d'objets sera chaque point qui existe.

Cela réduira considérablement la consommation de votre moteur pour la mémoire du système, en ne dessinant que ce qu'un joueur est réellement capable de voir, plutôt que la valeur totale des points du monde. Dans notre moteur, nous allons le faire en définissant des paramètres pour un voir l'espace.

Notre espace de vision sera défini sur les trois axes traditionnels: x, y et z. Sa définition x consistera de tout ce qui se situe entre les limites gauche et droite de la fenêtre, sa définition y consistera de tout ce qui se situe entre les limites supérieure et inférieure de la fenêtre et sa définition z sera comprise entre 0 (où la caméra est réglée) et la distance de vue de notre joueur (pour notre démonstration, nous utiliserons une valeur arbitraire de 100).

Avant de dessiner un point, notre classe de caméra va vérifier si ce point se situe dans notre espace de vision. Si c'est le cas, alors le point sera tracé; sinon, ce ne sera pas.


Pouvons-nous avoir quelques caméras ici??

Avec cette compréhension de base de la réforme, nous pouvons en déduire que notre classe ressemblera à ceci:

Classe de caméra Vars: int minX, maxX; // bornes minimales et maximales de X int minY, maxY; // bornes minimales et maximales de Y int minZ, maxZ; // bornes minimales et maximales de Z

Nous allons aussi laisser notre caméra gérer tout le rendu de notre moteur. En fonction du moteur, vous constaterez que les moteurs de rendu sont souvent séparés des systèmes de caméra. Cela est généralement fait pour garder les systèmes bien encapsulés, car - en fonction de la portée de votre moteur - les deux peuvent devenir très compliqués s'ils sont conservés ensemble. Pour nos besoins, cependant, il sera plus simple de les traiter comme un seul.

Premièrement, nous allons vouloir une fonction pouvant être appelée de manière externe à partir de la classe et qui dessinera la scène. Cette fonction permet de parcourir chacun des points existants, de les comparer aux paramètres de sélection de la caméra et de les dessiner, le cas échéant..


Source: http://en.wikipedia.org/wiki/File:ViewFrustum.svg

Pointe: Si vous souhaitez séparer votre système de caméra de votre moteur de rendu, vous pouvez simplement créer un Renderer classe, demandez au système de caméra de sélectionner les points, stockez ceux qui doivent être dessinés dans un tableau, puis envoyez ce tableau au dessiner() fonction de votre moteur de rendu.


Gestion des points

Le dernier élément de notre classe de caméras sera son système de gestion de points. Selon le langage de programmation que vous utilisez, il pourrait ne s'agir que d'un simple tableau de tous les objets pouvant être dessinés (nous ne gérerons pas que des points dans des parties ultérieures). Sinon, vous devrez peut-être utiliser la classe parent d'objet par défaut du langage. Si vous êtes super malchanceux, vous devrez créer votre propre classe d'objet parent et faire en sorte que chaque classe pouvant être dessinée (jusqu'à présent, seuls des points) soit un enfant de cette classe..

Après avoir ajouté cela dans la classe, voici un aperçu de base de notre caméra:

Classe de caméra Vars: int minX, maxX; // bornes minimales et maximales de X int minY, maxY; // bornes minimales et maximales de Y int minZ, maxZ; // limites minimales et maximales du tableau Z objectsInWorld; // un tableau de tous les objets existants Fonctions: null drawScene (); // dessine tous les objets nécessaires à l'écran, ne renvoie rien

Avec ces ajouts, améliorons un peu le programme que nous avons créé la dernière fois..


Des choses plus grandes et meilleures

Nous allons créer un programme de dessin par points simple, avec le programme exemple que nous avons créé la dernière fois comme point de départ.

Dans cette itération du programme, nous allons ajouter l'utilisation de notre nouvelle classe d'appareils photo. Quand le la touche est enfoncée, le programme redessinera l’écran sans culling, affichant le nombre d’objets rendus dans le coin supérieur droit de l’écran. Quand le C la touche est enfoncée, le programme redessinera l’écran avec le retrait, affichant également le nombre d’objets rendus.

Jetons un coup d'oeil au code:

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 camera.objectsInWorld [100]; // crée 100 espaces-objets dans le tableau de la caméra // définit l'espace de visualisation de la caméra camera.minX = 0; camera.maxX = screenWidth; camera.minY = 0; camera.maxY = screenHeight; camera.minZ = 0; camera.maxZ = 100; pour (int x = 0; x < camera.objectsInWorld.length; x++)  //Set its location to a random point on the screen camera.objectsInWorld[x].tuple = [random(-200,1000), random(-200,1000), random(-100,200));  function redrawScreenWithoutCulling() //this function clears the screen and then draws all of the points  ClearTheScreen(); //use your Graphics API's clear screen function for(int x = 0; x < camera.objectsInWorld.length; x++)  camera.objectsInWorld[x].drawPoint(); //draw the current point to the screen   while(esc != pressed) // the main loop  if(key('d') == pressed)  redrawScreenWithoutCulling();  if(key('c') == pressed)  camera.drawScene();  if(key('a') == pressed)  Point origin = new Point(0,0,0); Vector tempVector; for(int x = 0; x < camera.objectsInWorld.length; x++)  //store the current vector address for the point, and set the point tempVector = camera.objectsInWorld[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added camera.objectsInWorld[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location camera.objectsInWorld[x].addVectorToPoint(tempVector.scale(0.5,0.5,0.5));   if(key('s') == pressed)  Point origin = new Point(0,0,0); //create the space's origin as a point Vector tempVector; for(int x = 0; x < camera.objectsInWorld.length; x++)  //store the current vector address for the point, and set the point tempVector = camera.objectsInWorld[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added camera.objectsInWorld[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location camera.objectsInWorld[x].addVectorToPoint(tempVector.scale(2.0,2.0,2.0));   if(key('r') == pressed)  Point origin = new Point(0,0,0); //create the space's origin as a point Vector tempVector; for(int x = 0; x < camera.objectsInWorld.length; x++)  //store the current vector address for the point, and set the point tempVector = camera.objectsInWorld[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added camera.objectsInWorld[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location camera.objectsInWorld[x].addVectorToPoint(tempVector.rotateXY(15));    

Vous pouvez maintenant constater le pouvoir de la réforme! Notez que si vous parcourez l'exemple de code, certaines choses sont faites un peu différemment afin de rendre les démonstrations plus conviviales pour le Web. (Vous pouvez consulter ma démo simple ici.)


Conclusion

Avec une caméra et un système de rendu à votre ceinture, vous pouvez techniquement dire que vous avez créé un moteur de jeu en 3D! Ce n'est peut-être pas trop impressionnant pour l'instant, mais c'est sur la bonne voie.

Dans notre prochain article, nous examinerons l'ajout de formes géométriques à notre moteur (notamment des segments et des cercles), ainsi que des algorithmes permettant d'ajuster leurs équations aux pixels d'un écran..