Dans la troisième dimension!
J'ai maintenant porté NeHe OpenGL Leçon 5. Celui-ci n'est pas exactement simple - ma description de la différence sera probablement plus longue que la leçon originale 5. C'est en partie parce que des fonctionnalités qui ne sont pas disponibles sur l'iPhone, et en partie parce que l'iPhone est un appareil relativement aux ressources limitées, donc je mis en œuvre certaines optimisations qui n'étaient pas strictement nécessaires, mais que vous devez vraiment utiliser sur l'iPhone.
Voici le lien pour la leçon 5 du projet porté.
Où allons-nous même commencer cette fois-ci?
Définir et dessin de la pyramide
Tout d'abord, parlons un fait fondamental de la vie. Lorsque nous avons été la définition de nos sommets, nous avons fait ça en utilisant des nombres à virgule flottante stockés dans les variables GLfloat. Or, généralement parlant, les nombres à virgule flottante sont plus lents que les entiers. Bien sûr, avec les GPU et les FPU ce n'est pas toujours aussi simple, mais c'est généralement une bonne règle de base lors de la programmation, y compris la programmation pour l'iPhone.
Nous ne voulons pas sacrifier la précision, cependant. Nous voulons définir nos sommets avec des nombres à virgule flottante, puis reportez-vous à les utiliser entiers. Jetez un oeil à cette image spiffy j'ai dessiné (je sais, pouvez-vous croire que je ne tire pas dans la vie?):
Ce sont les sommets de la pyramide. Notez que nous avons un total de cinq sommets qui composent cette forme en trois dimensions. Cependant, nous ne pouvons pas nourrir ces cinq sommets en OpenGL et s'attendre à ce qu'il sait quoi faire avec eux. Il ne fonctionne pas de cette façon. Nous devons être un peu plus précis que cela. Nous avons pour identifier chacune des faces ou des polygones qui forment la forme.
Pour la pyramide, nous n'allons pas tirer en bas, nous allons donc vous inquiétez pas pour ça, mais en dehors du fond, une pyramide est composée de quatre formes triangulaires, et nous devons tirer toutes quatre d'entre eux. Alors que les quatre triangles avec trois sommets chacune, pour un total de douze sommets que nous avons besoin pour nourrir OpenGL pour dessiner cette pyramide. Mais, regardez à nouveau l'illustration: Nous avons seulement cinq sommets au total dans la forme. Cela signifie que nous allons nourrir les mêmes sommets dans les temps OpenGL multiples, car les sommets des polygones ou des visages part.
Nous pourrions créer un tableau avec tous les vertex douze points, et ce serait beau travail. Mais il est inefficace, parce que nous faisons OpenGL jetez autour des cinq mêmes sommets (composé de trois valeurs à virgule flottante chacun) plusieurs fois. OpenGL ne va pas regarder les valeurs et déterminer si elles sont les mêmes - il va tout simplement de les utiliser pour dessiner, et dans le processus, vous allez faire la navette autour et faire des calculs sur un terrain de plus de points flottants Les valeurs que nécessaire.
Au lieu de cela, nous allons définir un tableau de vertex, comme avant, et il va seulement avoir cinq valeurs en elle - un pour chaque point unique dans l'espace qui composent le polygone.
static const GLfloat pyramidVertices[] = {
0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f
}; Ensuite, nous allons à nourrir ce tableau de vertex à OpenGL, là encore, tout comme nous le faisions avant. Mais, nous n'allons pas utiliser glDrawArrays () pour lui dire de tirer le tableau de vertex, comme nous l'avons fait auparavant. Au lieu de cela, nous allons définir un autre tableau, mais celui-ci ne va pas pour contenir les sommets, il va contenir des valeurs entières tenant valeurs de l'indice de ce point dans le tableau de vertex qui contient les cinq sommets. Donc, pour se référer à la partie supérieure de la pyramide, au lieu de se référer à lui comme {0.0f, 1.0f, 0.0f}, nous allons voir par cet index du vertex dans le tableau de vertex.C'est un peu déroutant, cependant, parce que nous n'allons pas à s'y référer en utilisant l'index dans le tableau réel, mais plutôt que nous allons prétendre que chaque sommet dans le tableau de vertex est un objet distinct, avec son propre index valeur, comme ceci:
static const GLfloat pyramidVertices[] = {
0.0f, 1.0f, 0.0f, // These three values are one vertex with index 0
-1.0f, -1.0f, 1.0f, // These three values are one vertex with index 1
1.0f, -1.0f, 1.0f, // These three values are one vertex with index 2
1.0f, -1.0f, -1.0f, // These three values are one vertex with index 3
-1.0f, -1.0f, -1.0f // These three values are one vertex with index 4
}; Ou, pour utiliser notre illustration de fantaisie encore, c'est comment nous allons penser à ces indices:Donc, nous allons créer un autre tableau qui définit réellement chaque face de la pyramide (souvenez-vous, nous ne sommes pas le dessin du bas). Ce tableau, peut être constitué d'un des types de données entières OpenGL. J'ai choisi GLubyte parce que c'est le plus petit et j'ai moins de 256 sommets dans mon tableau, donc il fonctionnera très bien. Voici ce que ce tableau ressemble
static const GLubyte pyramidVertexFaces[] = {
0, 1, 2, // Defines Front Face
0, 3, 2, // Defines Right Face
0, 3, 4, // Defines Back Face
0, 1, 4 // Defines Left Face
}; Assez simple, non? Vous pouvez voir que nous avons encore des valeurs répétées, mais au moins ils sont gentils, les petites valeurs entières sur un octet qui peut être bousculés rapidement. Faire cela permet aussi d'OpenGL pour les calculs de cache qui correspondent à un sommet spécifiques afin qu'elles n'ont pas à faire plusieurs fois.Pour la pyramide, nous sommes encore en utilisant ce tableau de couleurs pour nous donner cette fantaisie gradation mélangés, mais nous allons l'optimiser. Les couleurs peuvent être définies en utilisant la valeur en virgule flottante compris entre 0,0 et 1,0. Ils peuvent également être défini comme un entier compris entre 0 et 255. Parce que toutes nos couleurs peuvent être définies en utilisant adéquatement un octet par canal de couleur, ce sera un tout petit peu plus vite en OpenGL, et de petits morceaux peuvent s'additionner rapidement quand vous êtes de programmation sur un appareil mobile comme l'iPhone. Voici la nouvelle version du tableau de couleur.
static const GLubyte triVertexColors[] = {
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
0, 255, 0, 255,
0, 0, 255, 255
}; Pourquoi avons-nous plus de valeurs cette fois-ci, vous pourriez vous demander? Eh bien, nous, c'est parce que nous définissons une couleur pour chaque élément dans le tableau de vertex. Lorsque nous nous référons au sommet par sa valeur d'index, OpenGL va et s'empare de la correspondance des couleurs de ce tableau, il ya un total de cinq sommets, et nous sommes attribuant une couleur à chacun. Vous voulez le même nombre de couleurs dans votre tableau de couleurs que vous avez sommets dans votre tableau de vertex.Ah, voici une autre chose - en OpenGL, vous pouvez définir des couleurs avec trois valeurs dans un tableau de couleur. Non, cependant, dans OpenGL ES. Vous devez utiliser quatre valeurs pour définir une couleur - vous devez fournir la valeur alpha, même si c'est toujours 1.0 ou 255.
Nous avons également définir une constante qui identifie le nombre de sommets, nous allons dessiner - c'est le nombre de polygones ou des visages multipliés par le nombre de sommets dans chaque polygone.
static const GLubyte triNumberOfIndices = 12Maintenant que nous avons notre tableau de vertex, à la couleur, et les visages définis, nous pouvons puiser. Nous n'allons pas utiliser glDrawArrays (), nous allons utiliser une nouvelle fonction appelée glDrawElements (). Nous allons appeler cette fonction une fois par polygone, ce qui signifie que nous ne sommes pas seulement un tableau dans l'alimentation et de laisser faire le reste OpenGL, nous allons faire une boucle à travers nos polygones et de les nourrir, un à la fois, à glDrawElements (). Ce n'est pas aussi difficile qu'il y paraît, cependant. Voici comment nous le faisons:
glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);Notez que nous avons changé notre appel à glColorPointer () pour utiliser GL_UNSIGNED_BYTE parce que nous avons changé notre gamme de couleurs à utiliser GLubyte. Puis on boucle à travers notre réseau de polygones, l'incrémentation par trois, afin que nous ne frapper chaque triangle une fois, et nous alimentons ce triangle dans l'appel glDrawElements ().
glColorPointer(4, GL_UNSIGNED_BYTE, 0, triVertexColors);
for(int i = 0; i {
glDrawElements(GL_TRIANGLE_FAN, 3, GL_UNSIGNED_BYTE, &pyramidVertexFaces[i]);
}
C'est un peu déroutant (et ce n'est pas si facile à expliquer non plus), mais il est vraiment plutôt simple une fois que vous avez enveloppé autour de votre tête l'idée de base. Le glDrawElements () Fonction sait que les valeurs que vous êtes l'alimentation sont des indices de ce point dans le tableau de vertex que vous avez fourni au plus tôt, et il trace un polygone sur la base des indices de sommets que vous le nourrissez.
Notre pyramide est composée de triangles (sauf le fond, que nous n'avons pas tirer), mais qu'en est-il le cube?
Définition et l'élaboration du Cube
Eh bien, OpenGL ES ne supporte pas GL_QUAD. Vous ne pouvez pas dessiner des carrés de OpenGL ES. Vous pouvez dessiner des points, des lignes, des boucles de ligne, des triangles, et les fans triangle, mais pas quads. Alors, comment pouvons-nous dessiner le cube, qui est composé de six quadrilatères?
En théorie, il est assez facile. Nous venons de subdiviser chaque carré en deux triangles. Tracez une ligne d'un coin à l'angle opposé du rectangle quelconque, et vous avez deux triangles. En fait, tout polygone peut être subdivisé en triangles. Ainsi, au lieu de dessiner six rectangles, nous avons juste à attirer l'douze triangles. Mais, la théorie est exactement la même que la pyramide. Nous définissons tous les sommets qui composent le cube (huit d'entre eux), puis créer un tableau de vertex d'eux. Nous définissons ensuite, en utilisant les particuliers, les douze triangles qui composent le cube. Voici ce que ces structures de données ressemblera à ceci:
static const GLfloat cubeVertices[] = {
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f,-1.0f, 1.0f,
-1.0f,-1.0f, 1.0f,
-1.0f, 1.0f,-1.0f,
1.0f, 1.0f,-1.0f,
1.0f,-1.0f,-1.0f,
-1.0f,-1.0f,-1.0f
};
static const GLubyte cubeNumberOfIndices = 36;
const GLubyte cubeVertexFaces[] = {
0, 1, 5, // Half of top face
0, 5, 4, // Other half of top face
4, 6, 5, // Half of front face
4, 6, 7, // Other half of front face
0, 1, 2, // Half of back face
0, 3, 2, // Other half of back face
1, 2, 5, // Half of right face
2, 5, 6, // Other half of right face
0, 3, 4, // Half of left face
7, 4, 3, // Other half of left face
3, 6, 2, // Half of bottom face
6, 7, 3, // Other half of bottom face
}; Nourrir à OpenGL est exactement la même. Une différence est que nous ne créons pas des gradations de couleur cette fois, mais sont plutôt le dessin de chaque côté du cube dans une couleur différente, donc on doit définir un éventail de couleurs, mais nous n'avons pas le nourrir à OpenGL comme un tableau de couleur, nous l'utilisons dans notre boucle de régler manuellement la couleur. Voici le tableau de couleur: const GLubyte cubeFaceColors[] = {
0, 255, 0, 255,
255, 125, 0, 255,
255, 0, 0, 255,
255, 255, 0, 255,
0, 0, 255, 255,
255, 0, 255, 255
};La chasse aux sorcières seulement ici, c'est que nous avons six couleurs, pour six faces, mais nous sommes de dessin douze triangles, donc nous devons nous rappeler de ne incrémenter l'indice de couleur tout autre triangle. Voici la boucle tirage au sort pour le cube:glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
int colorIndex = 0;
for(int i = 0; i {
glColor4ub(cubeFaceColors[colorIndex], cubeFaceColors[colorIndex+1], cubeFaceColors[colorIndex+2], cubeFaceColors[colorIndex+3]);
int face = (i / 3.0);
if (face%2 != 0.0)
colorIndex+=4;
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, &cubeVertexFaces[i]);
}
Bon, je sais que cela a été une lourde affichage. Cette annonce est probablement deux fois plus longtemps que la leçon NeHe originale. C'est le prix à payer pour la 3D sur un appareil mobile comme l'iPhone. Vous ne pouvez pas compter sur OpenGL ES à faire pour vous toutes les choses que bon OpenGL fait pour vous. Mais, une fois que vous commencez à obtenir le coup de lui, c'est vraiment pas si mal à travailler avec.
Je pense que je vais aller hors script après cette annonce. Je ne suis pas sûr qu'il ya beaucoup de valeur à aller leçon par leçon, à travers le prochain lot de tutoriels NeHe ... pas avant quelques-uns des sujets plus avancés. Je ne veux couvrir un certain nombre de choses, cependant - les normales, et les textures, par exemple. Donc je pense que je vais prendre une pause de NeHe pour les deux prochaines affectations et parler de ce normales sont et comment ils aident à dessin en 3D, et également montrer comment mapper les textures et les polygones. Puis, peut-être, je vais vous montrer comment charger un objet 3D créé dans un programme comme Maya ou Blender, au moins, une fois que j'ai compris comment le faire moi-même.
Aucun commentaire:
Enregistrer un commentaire