Bon, il ya encore beaucoup de théorie pour parler, mais avant que nous passons trop de temps de s'enliser dans les mathématiques complexes ou des concepts difficiles, passons nos pieds humides avec un dessin très simple dans OpenGL ES.
Si vous n'avez pas déjà fait, prenez une copie de mon vide OpenGL Xcode modèle de projet. Nous allons utiliser ce modèle comme point de départ plutôt que celui fourni d'Apple. Vous pouvez l'installer en copiant le dossier décompressé à cet endroit:
Ce modèle est conçu pour une application OpenGL en plein écran, et offre une vue OpenGL et un contrôleur de vue correspondante. La vue est conçue pour être belles mains-off et vous ne devriez pas besoin de toucher la plupart du temps. Il gère certaines des choses que nous gnarley allons parler plus tard, comme un tampon échangisme, mais il appelle à sa classe de contrôleur pour deux choses.
D'abord, il appelle à la fois le contrôleur lorsque la vue est en cours de configuration. SetupView Le contrôleur est tenu: la méthode est appelée une fois pour laisser le contrôleur ajouter tout le travail de configuration qu'il doit faire. C'est là que vous configurez votre fenêtre, ajouter des lumières, et ne l'installation d'autres pertinents pour votre projet. Pour aujourd'hui, ignorer cette méthode. Il ya une configuration très de base déjà en place qui vous permettra de faire simple dessin. Ce qui nous amène à l'autre méthode.
DrawView Le contrôleur: méthode sera appelée à intervalles réguliers basés sur la valeur d'une constante appelée kRenderingFrequency. La valeur initiale de cette est de 15,0, ce qui signifie que drawView: sera appelé quinze fois par seconde. Si vous voulez changer la fréquence de rendu, vous pouvez trouver cette constante définie dans le fichier appelé ConstantsAndMacros.h.
Pour notre première astuce, nous allons ajouter le code suivant à l'drawView existants: la méthode de GLViewController.m:
Avant de parler de ce qui se passe, allez-y et exécutez-le, et vous devriez obtenir quelque chose qui ressemble à ceci:

C'est une méthode simple, vous pouvez probablement imaginer ce qui se passe si vous essayez, mais nous allons parcourir ensemble. Depuis notre méthode dessine un triangle, nous avons besoin de trois sommets, donc nous créons trois de ces objets Vertex3D nous avons parlé dans le affectation précédente de cette série:
Vous remarquerez que la valeur z pour tous les trois sommets est la même, et que cette valeur (-3,0) est "derrière" l'origine. Parce que nous n'avons pas fait quelque chose pour changer cela, nous recherchons dans notre monde virtuel, comme si nous étions debout sur l'origine, qui est l'emplacement par défaut de départ. En plaçant le triangle à une position z de -3, nous nous assurons que nous pouvons le voir sur l'écran.
Après cela, nous créons un objet Triangle3D composé de ces trois sommets.
Maintenant, c'est le code assez facile à comprendre, non? Mais, dans les coulisses, à quoi il ressemble à l'ordinateur est un tableau de 9 GLfloats. Nous aurions pu accomplir la même chose en faisant cela:
Eh bien, pas tout à fait exactement la même chose - il ya une différence très minime, mais importante. Dans notre premier exemple, nous avons à transmettre l'adresse de notre objet Triangle3D dans OpenGL (par exemple, et triangle), mais dans le second exemple en utilisant le tableau, nous serions simplement passer dans le tableau, car les tableaux sont des pointeurs C. Mais, ne vous inquiétez pas trop à ce sujet, parce que cet exemple sera la dernière fois que nous déclarons un objet Triangle3D cette façon. Je vais vous expliquer pourquoi dans un instant, mais nous allons finir en passant par notre code. La prochaine chose que nous faisons est de charger la matrice identité. Je vais consacrer au moins un ensemble d'affichage des matrices de transformation, ce qu'ils sont et comment ils sont utilisés, mais il suffit de penser à cet appel comme un "bouton reset" pour OpenGL. Il se débarrasse de toute rotations, le mouvement, ou d'autres changements dans le monde virtuel et nous remet à l'origine debout.
Après cela, nous disons que tous OpenGL dessin doit être fait sur un fond gris clair. OpenGL s'attend généralement à définir les couleurs en utilisant quatre valeurs serrée. Rappelez-vous de la post précédent, les flotteurs sont serré les valeurs flottantes qui vont de 0.0 à 1.0. Ainsi, nous définissons les couleurs par leurs composantes rouge, vert et bleu, avec un autre composant appelé alpha, qui définit la quantité de ce qui se cache derrière la couleur montre à travers. Ne vous préoccupez pas l'alpha pour l'instant - pour le moment, nous allons simplement toujours mis l'alpha à 1.0, qui définit une couleur opaque.
Pour définir blanche en OpenGL, nous aimerions passer de 1,0 pour les quatre composantes. Pour définir un noir opaque, nous aimerions passer de 0,0 pour les composantes rouge, verte et bleue et de 1,0 pour alpha. La deuxième ligne de code dans ce dernier exemple est celui qui dit en fait OpenGL pour effacer tout ce qui a été établi avant et efface tout à la couleur claire.
Vous vous demandez probablement ce que les deux arguments à la glClear () appel sont. Eh bien, encore une fois, nous ne voulons pas prendre trop d'avance sur nous, mais ceux qui sont des constantes qui font référence aux valeurs stockées dans un champ de bits. OpenGL gère un certain nombre de buffers, Qui sont simplement des morceaux de mémoire utilisée pour les différents aspects du dessin. En logique binaire entre ces deux valeurs particulières ensemble, nous disent OpenGL pour effacer deux tampons différents - le tampon des couleurs et le tampon de profondeur. Le tampon stocke les couleurs de couleur pour chaque pixel de l'image courante. C'est essentiellement ce que vous voyez sur l'écran. Le tampon de profondeur (parfois aussi appelé le "z-buffer") contient des informations sur la proximité ou à proximité du spectateur potentiel de chaque pixel est. Il utilise ces informations pour déterminer si un pixel doit être utilisée ou non. Ce sont les deux tampons, vous verrez le plus souvent en OpenGL. Il existe d'autres, tels que le stencil buffer et le tampon d'accumulation, mais nous n'allons pas parler de ceux, du moins pour un temps. Pour l'instant, rappelez-vous juste avant que vous dessinez un cadre, vous devez effacer ces deux tampons de sorte que le contenu précédent ne plaisante pas les choses pour vous.
Après cela, nous permettons à l'une des caractéristiques d'OpenGL appelé vertex arrays. Cette fonctionnalité pourrait probablement juste être activé une fois dans le setupView: méthode, mais en règle générale, je tiens à activer et désactiver la fonctionnalité que j'utilise. Vous ne savez jamais quand un autre morceau de code pourrait être faire les choses différemment. si vous mettez ce que vous avez besoin sur et ensuite repartir, les risques de problèmes sont considérablement réduits. Dans cet exemple, dire que nous avions une autre classe qui n'a pas utilisé les tableaux de vertex pour dessiner, mais utilisé à la place des objets vertex buffer. Si l'un des morceaux de code oublié quelque chose ou de permis n'a pas explicitement activer quelque chose dont ils avaient besoin, une ou deux méthodes pourrait se retrouver avec des résultats inattendus.
La prochaine chose que nous faisons est de définir la couleur que nous allons dessiner po Cette ligne de code définit la couleur de dessin à un rouge vif.
Maintenant, tout dessin fait jusqu'à ce qu'un autre appel à glColor4f () sera fait en utilisant la couleur rouge. Il ya quelques exceptions à cela, comme le code qui dessine une forme de texture, mais fondamentalement, définissant la couleur comme celle-ci définit la couleur pour tous les appels qui le suivent.
Comme nous sommes de dessin avec les vertex arrays, nous devons dire à OpenGL où le tableau des sommets est. Rappelez-vous, un tableau de sommets est simplement un tableau C de l'GLfloats, avec chaque ensemble de trois valeurs qui représentent un sommet. Nous avons créé un objet Triangle3D, mais dans la mémoire, c'est exactement la même que neuf GLfloats consécutifs, afin que nous puissions simplement passer dans l'adresse du triangle.
Le premier paramètre à glVertexPointer () indique combien GLfloats représentent chaque sommet. Vous pouvez passer 2 ou 3 ici, selon que vous faites le dessin en deux dimensions ou en trois dimensions. Même si notre objet existe dans un plan, nous le dessin dans un monde virtuel en trois dimensions et ont défini en utilisant trois valeurs par sommet, nous passons donc en 3 ici. Ensuite, nous passons dans une énumération qui indique à OpenGL que nos sommets sont constitués de GLfloats. Ils n'ont pas à être - OpenGL ES est très heureux de vous laisser utiliser un type de données les plus dans un tableau de vertex, mais il est rare de voir autre chose que GL_FLOAT. Le paramètre suivant ... bien, ne vous inquiétez pas le paramètre suivant. C'est un sujet de discussion à venir. Pour l'instant, il sera toujours, toujours, toujours être à 0. Dans une future affectation, je vais vous montrer comment utiliser ce paramètre pour entrelacer les différents types de données sur le même objet dans une structure de données unique, mais c'est plus lourd que les juju je suis prêt à parler maintenant.
Après cela, nous disons à OpenGL pour dessiner des triangles entre les sommets dans le tableau que nous avons déjà présenté.
Comme vous l'aurez deviné, le premier paramètre est un enum qui raconte ce que OpenGL pour dessiner. Bien que l'OpenGL ES ne supporte pas les quads ou autre polygone en plus des triangles, il ne reste en charge une variété de modes de dessin, y compris la capacité ne point attirer l', des lignes, des boucles de ligne, les bandes triangle, triangle et les fans. Nous allons parler des différents modes de dessin plus tard. Pour l'instant, disons simplement coller avec des triangles.
Enfin, nous désactiver la fonction de celui que nous avons permis plus tôt afin de ne pas gâcher le code d'autres ailleurs. Encore une fois, il n'ya pas d'autre code dans cet exemple, mais généralement quand tu es en utilisant OpenGL, le dessin est potentiellement se passe à partir d'objets multiples.
Et c'est tout. Il fonctionne. Ce n'est pas très impressionnant, et ce n'est pas très efficace, mais il fonctionne. Vous êtes le dessin en OpenGL. Yay! Un certain nombre de fois chaque seconde, cette méthode est appelée obtenir, et c'est le dessin. Ne me croyez pas? Ajoutez le code suivant à la méthode audacieuse et exécutez-le à nouveau:
Lorsque vous l'exécutez à nouveau, le triangle devrait lentement tournent autour de l'origine. Ne vous inquiétez pas trop sur la mécanique de rotation, je voulais juste vous montrer que votre code de dessin devenait appelé plusieurs fois par seconde.
Que si nous voulons dessiner un carré? Eh bien, OpenGL ES ne dispose pas de places, nous avons donc à définir un carré de triangles. C'est assez facile à faire - un carré peut être créé à partir de deux triangles rectangles. Comment peut-on modifier le code ci-dessus pour dessiner deux triangles, plutôt qu'un seul? Peut-on créer deux Triangle3Ds et soumettre ceux-là? Eh bien, oui, nous avons pu. Mais ce serait inefficace. Il serait mieux si nous avons soumis deux triangles dans le cadre du même tableau de vertex. Nous pouvons le faire en déclarant un tableau d'objets Triangle3D, ou en allouant une portion de mémoire qui se trouve être la même taille que deux ou dix-huit Triangle3Ds GLfloats.
Voici une façon:
Lancez-le maintenant, et vous devriez obtenir quelque chose comme ceci:

Ce code est loin d'être idéales, cependant, parce que nous allouons notre géométrie sur la pile, et nous sommes provoquant une mémoire supplémentaire pour être utilisée parce que nos Vertex3DMake () crée une nouvelle Vertex3D sur la pile, puis copie les valeurs dans le tableau.
Pour un exemple simple de ce genre, qui fonctionne très bien, mais dans les cas plus complexes, la géométrie pour définir les objets 3D sera assez grand pour que vous ne voulez pas que ce soit l'allocation sur la pile et que vous ne voulez pas être allouer de la mémoire plus d'une fois pour un sommet donné, donc c'est une bonne idée de prendre l'habitude de répartir vos sommets sur le tas en utilisant notre malloc vieil ami () (même si j'aime parfois à utiliser calloc () au lieu parce que, par définition de toutes les valeurs de à zéro, certaines erreurs sont plus faciles à suivre vers le bas). Premièrement, nous avons besoin d'une fonction pour définir les valeurs d'un vertex existant au lieu d'en créer un nouveau chemin de la Vertex3DMake () ne. Cette allons travailler:
Maintenant, voici exactement le même code ré-écrit pour allouer les deux triangles sur le tas en utilisant cette nouvelle fonction:
Ok, nous avons couvert beaucoup de terrain, mais nous allons obtenu un peu plus loin. Rappelez-vous comment j'ai dit que l'OpenGL ES a plus d'un mode de dessin? Eh bien, cette forme carrée qui exige actuellement que 6 sommets (18 GLfloats) pour dessiner peut effectivement être tirée avec seulement quatre sommets (12 GLfloats) en utilisant le mode de dessin connu sous le nom bandes triangle (GL_TRIANGLE_STRIP).
Voici l'idée de base derrière une bande de triangle: le premier triangle de la bande est composée des trois premiers sommets (index 0, 1, 2). Le second triangle est composé de deux des sommets du triangle précédent avec le prochain sommet dans le tableau, et ainsi de suite à travers la matrice. Cette image pourrait faire plus de sens - le premier triangle est une sommets, 2, 3, le prochain est sommets 2, 3, 4, etc:

Ainsi, notre carré ne peut être faite comme ceci:

Le code pour faire de cette façon, ressemble à ceci:
Allons à l'exemple de code d'abord pour voir quelque chose. Rappelez-vous comment nous avons dessiné ce premier triangle? Nous avons utilisé glColor4f () pour définir une couleur et dit qu'il serait de définir la couleur pour tous les appels qui suivent. Cela signifie que chaque objet défini dans un tableau de vertex doit être établi dans la même couleur? Qu'est-ce? C'est assez limitant, n'est ce pas?
Eh bien, non. Tout comme OpenGL ES vous permettra de passer tous les sommets à la fois dans un tableau, il vous permettra également de passer dans un éventail de couleurs pour spécifier la couleur à utiliser pour chaque vertex. Si vous choisissez d'utiliser un tableau de couleur, vous avez besoin d'une seule couleur (quatre GLfloats) pour chaque vertex. Tableaux de couleurs doivent être activés à l'aide
Sinon, le processus est essentiellement le même que le passage au vertex arrays. Nous pouvons utiliser la même astuce est de définir une struct Color3D qui contient quatre membres GLfloat. Voici comment vous pourriez passer dans une couleur différente pour chaque tableau de ce triangle d'origine, nous a attiré:
Si vous exécutez cela, il devrait créer un triangle qui ressemble à ceci:

Regardons encore une chose aujourd'hui. Un problème avec la façon dont nous avons fait des choses est que si un sommet est utilisée plus d'une fois (sauf dans les conjoindre triangles dans une bande de triangle ou d'un ventilateur triangle), vous devez passer le même sommet dans les temps OpenGL multiples. Ce n'est pas une grosse affaire, mais vous voulez généralement à minimiser la quantité de données que vous poussant dans OpenGL, donc pousser les mêmes quatre-octet valeur en virgule flottante plus et n'est plus loin d'être idéales. Dans certains maillages, un sommet pourrait éventuellement être utilisée dans sept ou plusieurs triangles différents, donc votre tableau de vertex pourrait être plusieurs fois plus grande qu'elle doit être.
Lorsque vous traitez avec ces géométries complexes, il existe un moyen d'éviter d'envoyer le même sommet plusieurs fois, et c'est en utilisant quelque chose qui s'appelle elements se référer à des sommets par leur index dans le tableau de vertex. Comment cela fonctionne est que vous me créer un tableau de vertex qui a chaque sommet une fois et une seule fois. Ensuite, vous créer un autre tableau d'entiers en utilisant le type de données plus petit entier non signé qui va contenir le nombre de sommets uniques que vous avez. En d'autres termes, si votre tableau de vertex a moins de 256 sommets, alors vous créez un tableau de GLubytes, si c'est plus de 256, mais moins de 65 536, l'utilisation GLushort. Vous construisez vos triangles (ou autre forme) dans ce second tableau en se référant à des sommets dans le premier tableau par leur indice. Donc, si vous créer une matrice sommets avec 12 sommets, alors vous référer au premier sommet du tableau, vous vous référez à 0. Vous soumettez votre sommets de la même façon que vous avez fait avant, mais au lieu d'appeler glDrawArrays (), vous appelez une fonction différente appelée glDrawElements () et passer dans le tableau entier.
Finissons notre tutoriel avec un vrai, la forme honnêtes à la bonté 3D: un icosaèdre. Tout le monde ne cubes, mais nous allons être geek et faire un mort vingt-verso (numéros sans). Remplacer drawView: avec cette nouvelle version:
Avant de parler de ce qui se passe, nous allons l'exécuter et de voir le spin forme très joli:

Ce n'est pas entièrement en 3D à la recherche car il n'y a pas de feux, et même si on avait des lumières, nous n'avons pas dit OpenGL ce qu'il faut savoir pour calculer comment la lumière doit refléter hors de notre forme (c'est un sujet d'une future affectation - mais si vous souhaitez, vous pouvez lire quelques messages existants sur le sujet here and here).
Alors, qu'est-ce que nous faisons ici? Tout d'abord, nous avons créé une variable statique pour suivre la rotation de l'objet.
Puis nous avons défini notre tableau de vertex. Nous l'avons fait un peu différemment qu'avant, mais le résultat est le même. Depuis notre géométrie ne change pas du tout, nous pouvons faire const plutôt que d'allouer et de désallocation de mémoire chaque image, et de fournir les valeurs entre accolades Curley:
Ensuite, nous avons créer un tableau de couleur de la même façon. Cela crée un tableau d'objets Color3D, un pour chacun des sommets dans le tableau précédent:
Enfin, nous créons le tableau qui définit en fait la forme de l'icosaèdre. Ces douze sommets ci-dessus, par eux-mêmes, ne décrivent pas cette forme. OpenGL a besoin de savoir comment les connecter, donc pour cela, nous créons un tableau d'entiers (dans ce cas, GLubytes) qui pointent vers les sommets qui composent chaque triangle.
Ainsi, les trois premiers numéros dans icosahedronFaces sont 1,2,6, ce qui signifie pour dessiner un triangle entre les sommets au indices 1 (0.850651, 0, 0.525731), 2 (0.850651, 0, 0.525731), et 6 (0.525731, 0.850651 , 0).
Le morceau suivant est rien de nouveau, nous venons de charger la matrice identité (réinitialiser toutes les transformations), déplacer la forme loin de la caméra et la faire pivoter, régler la couleur de fond, vider les buffers, permettent vertex arrays et la couleur, puis l'alimentation OpenGL notre vertex tableau. Tout ce qui est exactement comme dans certains des exemples précédents.
Mais, alors, nous ne tirons pas glDrawArrays (). Nous appelons glDrawElements ():
Après cela, nous venons de désactiver tout, puis incrémenter la variable de rotation basé sur combien de temps s'est écoulé depuis la dernière image a été élaboré:
Alors, rappelez-vous: si vous fournissez les sommets dans le bon ordre à tirer, vous utilisez glDrawArrays (), mais si vous fournissez un tableau de sommets, puis un ensemble distinct d'indices pour identifier l'ordre dont ils ont besoin d'être aspiré, puis vous utilisez glDrawElements ().
D'accord, c'est assez pour aujourd'hui. J'ai couvert du sol beaucoup plus que je voulais, et je doute de moi-même pris de l'avance ici, mais nous espérons que cela a été utile. Dans le prochain épisode, nous revenons à des choses conceptuelles.
S'il vous plaît n'hésitez pas à jouer avec le code de dessin, ajouter plus de polygones, changer les couleurs, etc Il ya beaucoup plus à dessiner en OpenGL que nous avons couvert ici, mais vous avez maintenant vu l'idée de base derrière le dessin des objets 3D sur l'iPhone: vous créer un morceau de mémoire pour contenir tous les sommets, passer le tableau de vertex dans OpenGL, et ensuite lui dire de dessiner ces sommets.
Si vous n'avez pas déjà fait, prenez une copie de mon vide OpenGL Xcode modèle de projet. Nous allons utiliser ce modèle comme point de départ plutôt que celui fourni d'Apple. Vous pouvez l'installer en copiant le dossier décompressé à cet endroit:
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application/
Ce modèle est conçu pour une application OpenGL en plein écran, et offre une vue OpenGL et un contrôleur de vue correspondante. La vue est conçue pour être belles mains-off et vous ne devriez pas besoin de toucher la plupart du temps. Il gère certaines des choses que nous gnarley allons parler plus tard, comme un tampon échangisme, mais il appelle à sa classe de contrôleur pour deux choses.
D'abord, il appelle à la fois le contrôleur lorsque la vue est en cours de configuration. SetupView Le contrôleur est tenu: la méthode est appelée une fois pour laisser le contrôleur ajouter tout le travail de configuration qu'il doit faire. C'est là que vous configurez votre fenêtre, ajouter des lumières, et ne l'installation d'autres pertinents pour votre projet. Pour aujourd'hui, ignorer cette méthode. Il ya une configuration très de base déjà en place qui vous permettra de faire simple dessin. Ce qui nous amène à l'autre méthode.
DrawView Le contrôleur: méthode sera appelée à intervalles réguliers basés sur la valeur d'une constante appelée kRenderingFrequency. La valeur initiale de cette est de 15,0, ce qui signifie que drawView: sera appelé quinze fois par seconde. Si vous voulez changer la fréquence de rendu, vous pouvez trouver cette constante définie dans le fichier appelé ConstantsAndMacros.h.
Pour notre première astuce, nous allons ajouter le code suivant à l'drawView existants: la méthode de GLViewController.m:
- (void)drawView:(GLView*)view;
{
Vertex3D vertex1 = Vertex3DMake(0.0, 1.0, -3.0);
Vertex3D vertex2 = Vertex3DMake(1.0, 0.0, -3.0);
Vertex3D vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);
Triangle3D triangle = Triangle3DMake(vertex1, vertex2, vertex3);
glLoadIdentity();
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, &triangle);
glDrawArrays(GL_TRIANGLES, 0, 9);
glDisableClientState(GL_VERTEX_ARRAY);
}Avant de parler de ce qui se passe, allez-y et exécutez-le, et vous devriez obtenir quelque chose qui ressemble à ceci:

C'est une méthode simple, vous pouvez probablement imaginer ce qui se passe si vous essayez, mais nous allons parcourir ensemble. Depuis notre méthode dessine un triangle, nous avons besoin de trois sommets, donc nous créons trois de ces objets Vertex3D nous avons parlé dans le affectation précédente de cette série:
Vertex3D vertex1 = Vertex3DMake(0.0, 1.0, -3.0);
Vertex3D vertex2 = Vertex3DMake(1.0, 0.0, -3.0);
Vertex3D vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);Vous remarquerez que la valeur z pour tous les trois sommets est la même, et que cette valeur (-3,0) est "derrière" l'origine. Parce que nous n'avons pas fait quelque chose pour changer cela, nous recherchons dans notre monde virtuel, comme si nous étions debout sur l'origine, qui est l'emplacement par défaut de départ. En plaçant le triangle à une position z de -3, nous nous assurons que nous pouvons le voir sur l'écran.
Après cela, nous créons un objet Triangle3D composé de ces trois sommets.
Triangle3D triangle = Triangle3DMake(vertex1, vertex2, vertex3);Maintenant, c'est le code assez facile à comprendre, non? Mais, dans les coulisses, à quoi il ressemble à l'ordinateur est un tableau de 9 GLfloats. Nous aurions pu accomplir la même chose en faisant cela:
GLfloat triangle[] = {0.0, 1.0, -3.0, 1.0, 0.0, -3.0, -1.0, 0.0, -3.0};Eh bien, pas tout à fait exactement la même chose - il ya une différence très minime, mais importante. Dans notre premier exemple, nous avons à transmettre l'adresse de notre objet Triangle3D dans OpenGL (par exemple, et triangle), mais dans le second exemple en utilisant le tableau, nous serions simplement passer dans le tableau, car les tableaux sont des pointeurs C. Mais, ne vous inquiétez pas trop à ce sujet, parce que cet exemple sera la dernière fois que nous déclarons un objet Triangle3D cette façon. Je vais vous expliquer pourquoi dans un instant, mais nous allons finir en passant par notre code. La prochaine chose que nous faisons est de charger la matrice identité. Je vais consacrer au moins un ensemble d'affichage des matrices de transformation, ce qu'ils sont et comment ils sont utilisés, mais il suffit de penser à cet appel comme un "bouton reset" pour OpenGL. Il se débarrasse de toute rotations, le mouvement, ou d'autres changements dans le monde virtuel et nous remet à l'origine debout.
glLoadIdentity();Après cela, nous disons que tous OpenGL dessin doit être fait sur un fond gris clair. OpenGL s'attend généralement à définir les couleurs en utilisant quatre valeurs serrée. Rappelez-vous de la post précédent, les flotteurs sont serré les valeurs flottantes qui vont de 0.0 à 1.0. Ainsi, nous définissons les couleurs par leurs composantes rouge, vert et bleu, avec un autre composant appelé alpha, qui définit la quantité de ce qui se cache derrière la couleur montre à travers. Ne vous préoccupez pas l'alpha pour l'instant - pour le moment, nous allons simplement toujours mis l'alpha à 1.0, qui définit une couleur opaque.
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);Pour définir blanche en OpenGL, nous aimerions passer de 1,0 pour les quatre composantes. Pour définir un noir opaque, nous aimerions passer de 0,0 pour les composantes rouge, verte et bleue et de 1,0 pour alpha. La deuxième ligne de code dans ce dernier exemple est celui qui dit en fait OpenGL pour effacer tout ce qui a été établi avant et efface tout à la couleur claire.
Vous vous demandez probablement ce que les deux arguments à la glClear () appel sont. Eh bien, encore une fois, nous ne voulons pas prendre trop d'avance sur nous, mais ceux qui sont des constantes qui font référence aux valeurs stockées dans un champ de bits. OpenGL gère un certain nombre de buffers, Qui sont simplement des morceaux de mémoire utilisée pour les différents aspects du dessin. En logique binaire entre ces deux valeurs particulières ensemble, nous disent OpenGL pour effacer deux tampons différents - le tampon des couleurs et le tampon de profondeur. Le tampon stocke les couleurs de couleur pour chaque pixel de l'image courante. C'est essentiellement ce que vous voyez sur l'écran. Le tampon de profondeur (parfois aussi appelé le "z-buffer") contient des informations sur la proximité ou à proximité du spectateur potentiel de chaque pixel est. Il utilise ces informations pour déterminer si un pixel doit être utilisée ou non. Ce sont les deux tampons, vous verrez le plus souvent en OpenGL. Il existe d'autres, tels que le stencil buffer et le tampon d'accumulation, mais nous n'allons pas parler de ceux, du moins pour un temps. Pour l'instant, rappelez-vous juste avant que vous dessinez un cadre, vous devez effacer ces deux tampons de sorte que le contenu précédent ne plaisante pas les choses pour vous.
Après cela, nous permettons à l'une des caractéristiques d'OpenGL appelé vertex arrays. Cette fonctionnalité pourrait probablement juste être activé une fois dans le setupView: méthode, mais en règle générale, je tiens à activer et désactiver la fonctionnalité que j'utilise. Vous ne savez jamais quand un autre morceau de code pourrait être faire les choses différemment. si vous mettez ce que vous avez besoin sur et ensuite repartir, les risques de problèmes sont considérablement réduits. Dans cet exemple, dire que nous avions une autre classe qui n'a pas utilisé les tableaux de vertex pour dessiner, mais utilisé à la place des objets vertex buffer. Si l'un des morceaux de code oublié quelque chose ou de permis n'a pas explicitement activer quelque chose dont ils avaient besoin, une ou deux méthodes pourrait se retrouver avec des résultats inattendus.
glEnableClientState(GL_VERTEX_ARRAY);La prochaine chose que nous faisons est de définir la couleur que nous allons dessiner po Cette ligne de code définit la couleur de dessin à un rouge vif.
glColor4f(1.0, 0.0, 0.0, 1.0);Maintenant, tout dessin fait jusqu'à ce qu'un autre appel à glColor4f () sera fait en utilisant la couleur rouge. Il ya quelques exceptions à cela, comme le code qui dessine une forme de texture, mais fondamentalement, définissant la couleur comme celle-ci définit la couleur pour tous les appels qui le suivent.
Comme nous sommes de dessin avec les vertex arrays, nous devons dire à OpenGL où le tableau des sommets est. Rappelez-vous, un tableau de sommets est simplement un tableau C de l'GLfloats, avec chaque ensemble de trois valeurs qui représentent un sommet. Nous avons créé un objet Triangle3D, mais dans la mémoire, c'est exactement la même que neuf GLfloats consécutifs, afin que nous puissions simplement passer dans l'adresse du triangle.
glVertexPointer(3, GL_FLOAT, 0, &triangle);Le premier paramètre à glVertexPointer () indique combien GLfloats représentent chaque sommet. Vous pouvez passer 2 ou 3 ici, selon que vous faites le dessin en deux dimensions ou en trois dimensions. Même si notre objet existe dans un plan, nous le dessin dans un monde virtuel en trois dimensions et ont défini en utilisant trois valeurs par sommet, nous passons donc en 3 ici. Ensuite, nous passons dans une énumération qui indique à OpenGL que nos sommets sont constitués de GLfloats. Ils n'ont pas à être - OpenGL ES est très heureux de vous laisser utiliser un type de données les plus dans un tableau de vertex, mais il est rare de voir autre chose que GL_FLOAT. Le paramètre suivant ... bien, ne vous inquiétez pas le paramètre suivant. C'est un sujet de discussion à venir. Pour l'instant, il sera toujours, toujours, toujours être à 0. Dans une future affectation, je vais vous montrer comment utiliser ce paramètre pour entrelacer les différents types de données sur le même objet dans une structure de données unique, mais c'est plus lourd que les juju je suis prêt à parler maintenant.
Après cela, nous disons à OpenGL pour dessiner des triangles entre les sommets dans le tableau que nous avons déjà présenté.
glDrawArrays(GL_TRIANGLES, 0, 9);Comme vous l'aurez deviné, le premier paramètre est un enum qui raconte ce que OpenGL pour dessiner. Bien que l'OpenGL ES ne supporte pas les quads ou autre polygone en plus des triangles, il ne reste en charge une variété de modes de dessin, y compris la capacité ne point attirer l', des lignes, des boucles de ligne, les bandes triangle, triangle et les fans. Nous allons parler des différents modes de dessin plus tard. Pour l'instant, disons simplement coller avec des triangles.
Enfin, nous désactiver la fonction de celui que nous avons permis plus tôt afin de ne pas gâcher le code d'autres ailleurs. Encore une fois, il n'ya pas d'autre code dans cet exemple, mais généralement quand tu es en utilisant OpenGL, le dessin est potentiellement se passe à partir d'objets multiples.
glDisableClientState(GL_VERTEX_ARRAY);Et c'est tout. Il fonctionne. Ce n'est pas très impressionnant, et ce n'est pas très efficace, mais il fonctionne. Vous êtes le dessin en OpenGL. Yay! Un certain nombre de fois chaque seconde, cette méthode est appelée obtenir, et c'est le dessin. Ne me croyez pas? Ajoutez le code suivant à la méthode audacieuse et exécutez-le à nouveau:
- (void)drawView:(GLView*)view;
{
static GLfloat rotation = 0.0;
Vertex3D vertex1 = Vertex3DMake(0.0, 1.0, -3.0);
Vertex3D vertex2 = Vertex3DMake(1.0, 0.0, -3.0);
Vertex3D vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);
Triangle3D triangle = Triangle3DMake(vertex1, vertex2, vertex3);
glLoadIdentity();
glRotatef(rotation, 0.0, 0.0, 1.0);
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, &triangle);
glDrawArrays(GL_TRIANGLES, 0, 9);
glDisableClientState(GL_VERTEX_ARRAY);
rotation+= 0.5;
}Lorsque vous l'exécutez à nouveau, le triangle devrait lentement tournent autour de l'origine. Ne vous inquiétez pas trop sur la mécanique de rotation, je voulais juste vous montrer que votre code de dessin devenait appelé plusieurs fois par seconde.
Que si nous voulons dessiner un carré? Eh bien, OpenGL ES ne dispose pas de places, nous avons donc à définir un carré de triangles. C'est assez facile à faire - un carré peut être créé à partir de deux triangles rectangles. Comment peut-on modifier le code ci-dessus pour dessiner deux triangles, plutôt qu'un seul? Peut-on créer deux Triangle3Ds et soumettre ceux-là? Eh bien, oui, nous avons pu. Mais ce serait inefficace. Il serait mieux si nous avons soumis deux triangles dans le cadre du même tableau de vertex. Nous pouvons le faire en déclarant un tableau d'objets Triangle3D, ou en allouant une portion de mémoire qui se trouve être la même taille que deux ou dix-huit Triangle3Ds GLfloats.
Voici une façon:
- (void)drawView:(GLView*)view;
{
Triangle3D triangle[2];
triangle[0].v1 = Vertex3DMake(0.0, 1.0, -3.0);
triangle[0].v2 = Vertex3DMake(1.0, 0.0, -3.0);
triangle[0].v3 = Vertex3DMake(-1.0, 0.0, -3.0);
triangle[1].v1 = Vertex3DMake(-1.0, 0.0, -3.0);
triangle[1].v2 = Vertex3DMake(1.0, 0.0, -3.0);
triangle[1].v3 = Vertex3DMake(0.0, -1.0, -3.0);
glLoadIdentity();
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, &triangle);
glDrawArrays(GL_TRIANGLES, 0, 18);
glDisableClientState(GL_VERTEX_ARRAY);
}Lancez-le maintenant, et vous devriez obtenir quelque chose comme ceci:

Ce code est loin d'être idéales, cependant, parce que nous allouons notre géométrie sur la pile, et nous sommes provoquant une mémoire supplémentaire pour être utilisée parce que nos Vertex3DMake () crée une nouvelle Vertex3D sur la pile, puis copie les valeurs dans le tableau.
Pour un exemple simple de ce genre, qui fonctionne très bien, mais dans les cas plus complexes, la géométrie pour définir les objets 3D sera assez grand pour que vous ne voulez pas que ce soit l'allocation sur la pile et que vous ne voulez pas être allouer de la mémoire plus d'une fois pour un sommet donné, donc c'est une bonne idée de prendre l'habitude de répartir vos sommets sur le tas en utilisant notre malloc vieil ami () (même si j'aime parfois à utiliser calloc () au lieu parce que, par définition de toutes les valeurs de à zéro, certaines erreurs sont plus faciles à suivre vers le bas). Premièrement, nous avons besoin d'une fonction pour définir les valeurs d'un vertex existant au lieu d'en créer un nouveau chemin de la Vertex3DMake () ne. Cette allons travailler:
static inline void Vertex3DSet(Vertex3D *vertex, CGFloat inX, CGFloat inY, CGFloat inZ)
{
vertex->x = inX;
vertex->y = inY;
vertex->z = inZ;
}Maintenant, voici exactement le même code ré-écrit pour allouer les deux triangles sur le tas en utilisant cette nouvelle fonction:
- (void)drawView:(GLView*)view;
{
Triangle3D *triangles = malloc(sizeof(Triangle3D) * 2);
Vertex3DSet(&triangles[0].v1, 0.0, 1.0, -3.0);
Vertex3DSet(&triangles[0].v2, 1.0, 0.0, -3.0);
Vertex3DSet(&triangles[0].v3, -1.0, 0.0, -3.0);
Vertex3DSet(&triangles[1].v1, -1.0, 0.0, -3.0);
Vertex3DSet(&triangles[1].v2, 1.0, 0.0, -3.0);
Vertex3DSet(&triangles[1].v3, 0.0, -1.0, -3.0);
glLoadIdentity();
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, triangles);
glDrawArrays(GL_TRIANGLES, 0, 18);
glDisableClientState(GL_VERTEX_ARRAY);
if (triangles != NULL)
free(triangles);
}Ok, nous avons couvert beaucoup de terrain, mais nous allons obtenu un peu plus loin. Rappelez-vous comment j'ai dit que l'OpenGL ES a plus d'un mode de dessin? Eh bien, cette forme carrée qui exige actuellement que 6 sommets (18 GLfloats) pour dessiner peut effectivement être tirée avec seulement quatre sommets (12 GLfloats) en utilisant le mode de dessin connu sous le nom bandes triangle (GL_TRIANGLE_STRIP).
Voici l'idée de base derrière une bande de triangle: le premier triangle de la bande est composée des trois premiers sommets (index 0, 1, 2). Le second triangle est composé de deux des sommets du triangle précédent avec le prochain sommet dans le tableau, et ainsi de suite à travers la matrice. Cette image pourrait faire plus de sens - le premier triangle est une sommets, 2, 3, le prochain est sommets 2, 3, 4, etc:
Ainsi, notre carré ne peut être faite comme ceci:

Le code pour faire de cette façon, ressemble à ceci:
- (void)drawView:(GLView*)view;
{
Vertex3D *vertices = malloc(sizeof(Vertex3D) * 4);
Vertex3DSet(&vertices[0], 0.0, 1.0, -3.0);
Vertex3DSet(&vertices[1], 1.0, 0.0, -3.0);
Vertex3DSet(&vertices[2], -1.0, 0.0, -3.0);
Vertex3DSet(&vertices[3], 0.0, -1.0, -3.0);
glLoadIdentity();
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 12);
glDisableClientState(GL_VERTEX_ARRAY);
if (vertices != NULL)
free(vertices);
}Allons à l'exemple de code d'abord pour voir quelque chose. Rappelez-vous comment nous avons dessiné ce premier triangle? Nous avons utilisé glColor4f () pour définir une couleur et dit qu'il serait de définir la couleur pour tous les appels qui suivent. Cela signifie que chaque objet défini dans un tableau de vertex doit être établi dans la même couleur? Qu'est-ce? C'est assez limitant, n'est ce pas?
Eh bien, non. Tout comme OpenGL ES vous permettra de passer tous les sommets à la fois dans un tableau, il vous permettra également de passer dans un éventail de couleurs pour spécifier la couleur à utiliser pour chaque vertex. Si vous choisissez d'utiliser un tableau de couleur, vous avez besoin d'une seule couleur (quatre GLfloats) pour chaque vertex. Tableaux de couleurs doivent être activés à l'aide
glEnableClientState(GL_COLOR_ARRAY);
Sinon, le processus est essentiellement le même que le passage au vertex arrays. Nous pouvons utiliser la même astuce est de définir une struct Color3D qui contient quatre membres GLfloat. Voici comment vous pourriez passer dans une couleur différente pour chaque tableau de ce triangle d'origine, nous a attiré:
- (void)drawView:(GLView*)view;
{
Vertex3D vertex1 = Vertex3DMake(0.0, 1.0, -3.0);
Vertex3D vertex2 = Vertex3DMake(1.0, 0.0, -3.0);
Vertex3D vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);
Triangle3D triangle = Triangle3DMake(vertex1, vertex2, vertex3);
Color3D *colors = malloc(sizeof(Color3D) * 3);
Color3DSet(&colors[0], 1.0, 0.0, 0.0, 1.0);
Color3DSet(&colors[1], 0.0, 1.0, 0.0, 1.0);
Color3DSet(&colors[2], 0.0, 0.0, 1.0, 1.0);
glLoadIdentity();
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, &triangle);
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawArrays(GL_TRIANGLES, 0, 9);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
if (colors != NULL)
free(colors);
}Si vous exécutez cela, il devrait créer un triangle qui ressemble à ceci:

Regardons encore une chose aujourd'hui. Un problème avec la façon dont nous avons fait des choses est que si un sommet est utilisée plus d'une fois (sauf dans les conjoindre triangles dans une bande de triangle ou d'un ventilateur triangle), vous devez passer le même sommet dans les temps OpenGL multiples. Ce n'est pas une grosse affaire, mais vous voulez généralement à minimiser la quantité de données que vous poussant dans OpenGL, donc pousser les mêmes quatre-octet valeur en virgule flottante plus et n'est plus loin d'être idéales. Dans certains maillages, un sommet pourrait éventuellement être utilisée dans sept ou plusieurs triangles différents, donc votre tableau de vertex pourrait être plusieurs fois plus grande qu'elle doit être.
Lorsque vous traitez avec ces géométries complexes, il existe un moyen d'éviter d'envoyer le même sommet plusieurs fois, et c'est en utilisant quelque chose qui s'appelle elements se référer à des sommets par leur index dans le tableau de vertex. Comment cela fonctionne est que vous me créer un tableau de vertex qui a chaque sommet une fois et une seule fois. Ensuite, vous créer un autre tableau d'entiers en utilisant le type de données plus petit entier non signé qui va contenir le nombre de sommets uniques que vous avez. En d'autres termes, si votre tableau de vertex a moins de 256 sommets, alors vous créez un tableau de GLubytes, si c'est plus de 256, mais moins de 65 536, l'utilisation GLushort. Vous construisez vos triangles (ou autre forme) dans ce second tableau en se référant à des sommets dans le premier tableau par leur indice. Donc, si vous créer une matrice sommets avec 12 sommets, alors vous référer au premier sommet du tableau, vous vous référez à 0. Vous soumettez votre sommets de la même façon que vous avez fait avant, mais au lieu d'appeler glDrawArrays (), vous appelez une fonction différente appelée glDrawElements () et passer dans le tableau entier.
Finissons notre tutoriel avec un vrai, la forme honnêtes à la bonté 3D: un icosaèdre. Tout le monde ne cubes, mais nous allons être geek et faire un mort vingt-verso (numéros sans). Remplacer drawView: avec cette nouvelle version:
- (void)drawView:(GLView*)view;
{
static GLfloat rot = 0.0;
// This is the same result as using Vertex3D, just faster to type and
// can be made const this way
static const Vertex3D vertices[]= {
{0, -0.525731, 0.850651}, // vertices[0]
{0.850651, 0, 0.525731}, // vertices[1]
{0.850651, 0, -0.525731}, // vertices[2]
{-0.850651, 0, -0.525731}, // vertices[3]
{-0.850651, 0, 0.525731}, // vertices[4]
{-0.525731, 0.850651, 0}, // vertices[5]
{0.525731, 0.850651, 0}, // vertices[6]
{0.525731, -0.850651, 0}, // vertices[7]
{-0.525731, -0.850651, 0}, // vertices[8]
{0, -0.525731, -0.850651}, // vertices[9]
{0, 0.525731, -0.850651}, // vertices[10]
{0, 0.525731, 0.850651} // vertices[11]
};
static const Color3D colors[] = {
{1.0, 0.0, 0.0, 1.0},
{1.0, 0.5, 0.0, 1.0},
{1.0, 1.0, 0.0, 1.0},
{0.5, 1.0, 0.0, 1.0},
{0.0, 1.0, 0.0, 1.0},
{0.0, 1.0, 0.5, 1.0},
{0.0, 1.0, 1.0, 1.0},
{0.0, 0.5, 1.0, 1.0},
{0.0, 0.0, 1.0, 1.0},
{0.5, 0.0, 1.0, 1.0},
{1.0, 0.0, 1.0, 1.0},
{1.0, 0.0, 0.5, 1.0}
};
static const GLubyte icosahedronFaces[] = {
1, 2, 6,
1, 7, 2,
3, 4, 5,
4, 3, 8,
6, 5, 11,
5, 6, 10,
9, 10, 2,
10, 9, 3,
7, 8, 9,
8, 7, 0,
11, 0, 1,
0, 11, 4,
6, 2, 10,
1, 6, 11,
3, 5, 10,
5, 4, 11,
2, 7, 9,
7, 1, 0,
3, 9, 8,
4, 8, 0,
};
glLoadIdentity();
glTranslatef(0.0f,0.0f,-3.0f);
glRotatef(rot,1.0f,1.0f,1.0f);
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawElements(GL_TRIANGLES, 60, GL_UNSIGNED_BYTE, icosahedronFaces);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rot+=50 * timeSinceLastDraw;
}
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
}Avant de parler de ce qui se passe, nous allons l'exécuter et de voir le spin forme très joli:

Ce n'est pas entièrement en 3D à la recherche car il n'y a pas de feux, et même si on avait des lumières, nous n'avons pas dit OpenGL ce qu'il faut savoir pour calculer comment la lumière doit refléter hors de notre forme (c'est un sujet d'une future affectation - mais si vous souhaitez, vous pouvez lire quelques messages existants sur le sujet here and here).
Alors, qu'est-ce que nous faisons ici? Tout d'abord, nous avons créé une variable statique pour suivre la rotation de l'objet.
static GLfloat rot = 0.0;Puis nous avons défini notre tableau de vertex. Nous l'avons fait un peu différemment qu'avant, mais le résultat est le même. Depuis notre géométrie ne change pas du tout, nous pouvons faire const plutôt que d'allouer et de désallocation de mémoire chaque image, et de fournir les valeurs entre accolades Curley:
static const Vertex3D vertices[]= {
{0, -0.525731, 0.850651}, // vertices[0]
{0.850651, 0, 0.525731}, // vertices[1]
{0.850651, 0, -0.525731}, // vertices[2]
{-0.850651, 0, -0.525731}, // vertices[3]
{-0.850651, 0, 0.525731}, // vertices[4]
{-0.525731, 0.850651, 0}, // vertices[5]
{0.525731, 0.850651, 0}, // vertices[6]
{0.525731, -0.850651, 0}, // vertices[7]
{-0.525731, -0.850651, 0}, // vertices[8]
{0, -0.525731, -0.850651}, // vertices[9]
{0, 0.525731, -0.850651}, // vertices[10]
{0, 0.525731, 0.850651} // vertices[11]
};Ensuite, nous avons créer un tableau de couleur de la même façon. Cela crée un tableau d'objets Color3D, un pour chacun des sommets dans le tableau précédent:
static const Color3D colors[] = {
{1.0, 0.0, 0.0, 1.0},
{1.0, 0.5, 0.0, 1.0},
{1.0, 1.0, 0.0, 1.0},
{0.5, 1.0, 0.0, 1.0},
{0.0, 1.0, 0.0, 1.0},
{0.0, 1.0, 0.5, 1.0},
{0.0, 1.0, 1.0, 1.0},
{0.0, 0.5, 1.0, 1.0},
{0.0, 0.0, 1.0, 1.0},
{0.5, 0.0, 1.0, 1.0},
{1.0, 0.0, 1.0, 1.0},
{1.0, 0.0, 0.5, 1.0}
};Enfin, nous créons le tableau qui définit en fait la forme de l'icosaèdre. Ces douze sommets ci-dessus, par eux-mêmes, ne décrivent pas cette forme. OpenGL a besoin de savoir comment les connecter, donc pour cela, nous créons un tableau d'entiers (dans ce cas, GLubytes) qui pointent vers les sommets qui composent chaque triangle.
static const GLubyte icosahedronFaces[] = {
1, 2, 6,
1, 7, 2,
3, 4, 5,
4, 3, 8,
6, 5, 11,
5, 6, 10,
9, 10, 2,
10, 9, 3,
7, 8, 9,
8, 7, 0,
11, 0, 1,
0, 11, 4,
6, 2, 10,
1, 6, 11,
3, 5, 10,
5, 4, 11,
2, 7, 9,
7, 1, 0,
3, 9, 8,
4, 8, 0,
};Ainsi, les trois premiers numéros dans icosahedronFaces sont 1,2,6, ce qui signifie pour dessiner un triangle entre les sommets au indices 1 (0.850651, 0, 0.525731), 2 (0.850651, 0, 0.525731), et 6 (0.525731, 0.850651 , 0).
Le morceau suivant est rien de nouveau, nous venons de charger la matrice identité (réinitialiser toutes les transformations), déplacer la forme loin de la caméra et la faire pivoter, régler la couleur de fond, vider les buffers, permettent vertex arrays et la couleur, puis l'alimentation OpenGL notre vertex tableau. Tout ce qui est exactement comme dans certains des exemples précédents.
glLoadIdentity();
glTranslatef(0.0f,0.0f,-3.0f);
glRotatef(rot,1.0f,1.0f,1.0f);
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glColor4f(1.0, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);Mais, alors, nous ne tirons pas glDrawArrays (). Nous appelons glDrawElements ():
glDrawElements(GL_TRIANGLES, 60, GL_UNSIGNED_BYTE, icosahedronFaces);Après cela, nous venons de désactiver tout, puis incrémenter la variable de rotation basé sur combien de temps s'est écoulé depuis la dernière image a été élaboré:
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rot+=50 * timeSinceLastDraw;
}
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];Alors, rappelez-vous: si vous fournissez les sommets dans le bon ordre à tirer, vous utilisez glDrawArrays (), mais si vous fournissez un tableau de sommets, puis un ensemble distinct d'indices pour identifier l'ordre dont ils ont besoin d'être aspiré, puis vous utilisez glDrawElements ().
D'accord, c'est assez pour aujourd'hui. J'ai couvert du sol beaucoup plus que je voulais, et je doute de moi-même pris de l'avance ici, mais nous espérons que cela a été utile. Dans le prochain épisode, nous revenons à des choses conceptuelles.
S'il vous plaît n'hésitez pas à jouer avec le code de dessin, ajouter plus de polygones, changer les couleurs, etc Il ya beaucoup plus à dessiner en OpenGL que nous avons couvert ici, mais vous avez maintenant vu l'idée de base derrière le dessin des objets 3D sur l'iPhone: vous créer un morceau de mémoire pour contenir tous les sommets, passer le tableau de vertex dans OpenGL, et ensuite lui dire de dessiner ces sommets.
Aucun commentaire:
Enregistrer un commentaire