lundi 13 février 2012

OpenGL ES à partir de zéro, Partie 1: Concepts de base

J'ai fait un certain nombre d'affichages sur la programmation OpenGL ES pour l'iPhone, mais la plupart des messages que j'ai fait ont été ciblées sur les personnes qui connaissent déjà au moins un peu plus sur la programmation 3D.

Si vous n'avez pas déjà fait, prenez une copie de mon modèle d'OpenGL Empty project Xcode. 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/
Il ya un certain nombre de bons tutoriaux et livres sur OpenGL. Malheureusement, il ne sont pas très nombreuses sur OpenGL ES, et aucune (au moins pendant que j'écris ceci) qui sont spécifiquement conçus pour l'apprentissage de la programmation 3D sur l'iPhone. Parce que la plupart des matériels disponibles pour l'apprentissage OpenGL commence enseignement en utilisant ce qu'on appelle mode direct, Qui fait partie de la fonctionnalité d'OpenGL qui n'est pas dans OpenGL ES, il peut être vraiment difficile pour un dev iPhone sans expérience 3D pour se lever et courir à l'aide des livres existants et des tutoriels. J'ai eu un certain nombre de personnes en font la demande, donc j'ai décidé de commencer une série de messages de blog conçu pour le débutant absolu en 3D. C'est la première fois dans cette série. Si vous avez lu et compris mon précédent affichages OpenGL, vous trouverez probablement cette série à être un peu trop basique.

Types de données OpenGL


La première chose que nous allons parler sont les types de données d'OpenGL. Parce que OpenGL est une API multi-plateforme, et la taille des types de données peuvent varier en fonction du langage de programmation utilisé ainsi que le processeur sous-jacent (64 bits vs 32 bits vs 16 bits), OpenGL déclare ses propres types de données personnalisés . Lors du passage de valeurs dans OpenGL, vous devez toujours utiliser ces types de données OpenGL pour s'assurer que vous êtes de passage des valeurs de la bonne taille ou de précision. Ne pas le faire pourrait provoquer des résultats inattendus ou des ralentissements causés par la conversion des données à l'exécution. Chaque mise en œuvre de l'OpenGL, indépendamment de la plateforme ou du langage, déclare les types de données standard OpenGL de telle manière qu'ils seront de la même taille sur toutes les plateformes, ce qui rend le portage OpenGL de code d'une plateforme à une autre plus facile.

Voici les types de données OpenGL ES:
  • GLenum: Un entier non signé utilisé pour GL spécifiques énumérations. Les plus couramment utilisés pour raconter OpenGL, le type de données stockées dans un tableau transmis par le pointeur (par exemple GL_FLOAT) pour indiquer que le tableau est constitué de GLfloats.
  • GLboolean: Utilisé pour tenir une seule des valeurs booléennes. OpenGL ES déclare également ses propres valeurs vrai et faux (et GL_TRUE GL_FALSE) pour éviter les différences plateforme et du langage. Lors du passage booléens dans OpenGL, utilisez ces plutôt que OUI ou NON (bien qu'il ne fera pas mal si vous utilisez accidentellement OUI ou TRUE, car ils sont réellement défini les mêmes. Mais, il est de bon ton d'utiliser les valeurs définies par GL.
  • GLbitfield: Ce sont des entiers sur quatre octets utilisés pour emballer les multiples valeurs booléennes (jusqu'à 32) en une seule variable en utilisant des opérateurs bit à bit. Nous allons discuter de cela plus la première fois que nous utilisons une variable bitfield, mais vous pouvez lire sur l'idée de base plus sur Wikipedia
  • GLbyte: A signé un octet entier capable de contenir une valeur de -128 à 127
  • GLshort: A signé de deux octets entiers capable de contenir une valeur comprise entre -32768 à 32767
  • GLint: A signé quatre octets capable de contenir une valeur comprise entre -2147483648 et 2147483647
  • GLsizei: A signé, quatre octets utilisés pour représenter la taille (en octets) de données, similaire à size_t en C.
  • GLubyte: Un non signé, un octet entier capable de contenir une valeur comprise entre 0 et 255.
  • GLushort: Un non signé, de deux octets entiers capable de contenir une valeur comprise entre 0 et 65535
  • GLuint: Un non signé, quatre octets capable de contenir une valeur comprise entre 0 et 4294967295
  • GLfloat: Une précision de quatre octets IEEE 754-1985 flottantes variable en virgule.
  • GLclampf: Ceci est aussi une variable à quatre octets de précision en virgule flottante, mais quand OpenGL utilise GLclampf, il est ce qui indique que la valeur de cette variable particulière doit toujours être compris entre 0,0 et 1,0.
  • GLvoid: Une valeur nulle utilisée pour indiquer qu'une fonction ne retourne aucune valeur, ou ne prend aucun argument.
  • GLfixed: Nombres à virgule fixe sont un moyen de stocker des nombres réels en utilisant des nombres entiers. Ce fut une optimisation courante dans les systèmes 3D utilisés car la plupart des processeurs d'ordinateur sont beaucoup plus rapides à faire des maths avec des entiers qu'avec variables à virgule flottante. Parce que l'iPhone a des processeurs vectoriels qui utilise OpenGL pour faire vite mathématiques à virgule flottante, nous ne serons pas discuter arithmétique en virgule fixe ou le type de données GLfixed.
  • GLclampx: Une autre variable en virgule fixe, utilisé pour représenter des nombres réels compris entre 0,0 et 1,0 en utilisant l'arithmétique virgule fixe. Comme GLfixed, nous ne serons pas d'utiliser ou de discuter de ce type de données.

OpenGL ES (au moins la version utilisée sur l'iPhone) ne supporte aucune de 8 octets (64 bits) des types de données tels que long ou double. OpenGL ne sont plus ces types de données, mais étant donné la taille de l'écran la plupart des périphériques embarqués, et les types d'applications que vous êtes susceptibles d'être écrit pour eux, la décision a été prise de les exclure de l'OpenGL ES sous l'hypothèse qu'il y aurait besoin de peu pour eux, et que leur utilisation pourrait avoir un effet néfaste sur la performance.

Le point ou un sommet


L'unité atomique dans le graphisme 3D est appelé le point or vertex. Celles-ci représentent un seul point dans un espace tridimensionnel et sont utilisés pour construire des objets plus complexes. Les polygones sont construits à partir de ces points, et les objets sont construits en plusieurs polygones. Bien OpenGL régulière supporte de nombreux types de polygones, OpenGL ES ne supporte que l'utilisation de trois faces de polygones (triangles aka).

Si vous vous souvenez de retour à l'école secondaire la géométrie, vous vous souvenez probablement quelque chose qui s'appelle Coordonnées cartésiennes. L'idée de base est que vous sélectionnez un point arbitraire dans l'espace et l'appellent le origin. Vous pouvez ensuite désigner n'importe quel point dans l'espace en référençant l'origine et l'aide de trois nombres, un pour chacune des trois dimensions, qui sont représentés par trois lignes imaginaires qui traversent l'origine. La ligne imaginaire allant de gauche à droite est appelé l'axe des x. Voyager le long de l'axe x, comme vous allez à la droite le long de l'axe des x, la valeur est plus élevée et que vous allez vers la gauche, ils obtiennent plus bas. Gauche de l'origine sont des valeurs de x négatives, et à droite sont les valeurs de x positives. Les deux autres axes de travail exactement de la même manière. En remontant le long de l'axe Y, la valeur de y augmente, et de descendre, il diminue. Les valeurs supérieures à l'origine ont une valeur y positifs, et ceux ci-dessous l'origine ont une valeur y négatifs. Avec Z, comme des objets s'éloigner du spectateur, la valeur est faible, et comme ils se déplacent vers le spectateur (ou continuer derrière le spectateur), les valeurs deviennent plus importants. Les points qui sont en face de l'origine ont une valeur positive z, et ceux qui sont derrière l'origine ont une valeur négative z. L'illustration suivante pourrait rendre l'aide ces mots un sens un peu plus:
cartesian.png

Note: Core Graphics, qui est un autre cadre pour faire des images sur l'iPhone utilise un système de coordonnées légèrement différentes dans ce que l'axe Y diminue car il passe de l'origine, et augmente comme il va vers le bas.


La valeur qui augmente ou diminue le long de ces axes sont dans une échelle arbitraire - ils ne représentent aucune mesure réelle, comme les pieds, pouces ou mètres. Vous pouvez sélectionner n'importe quelle échelle qui fait sens pour vos propres programmes. Si vous souhaitez concevoir un jeu où chaque unité est un pied, vous pouvez le faire. si vous voulez faire de chaque unité d'un micron, vous pouvez le faire aussi bien. OpenGL ne prend pas garde à ce qu'ils représentent pour l'utilisateur final, qu'il estime juste d'entre eux comme des unités, et s'assurer qu'ils sont tous des distances égales.

Depuis l'emplacement d'un objet dans l'espace tridimensionnel peut être représenté par trois valeurs, la position d'un objet est généralement représentée en OpenGL par l'utilisation de trois variables GLfloat, généralement en utilisant un tableau de trois flotteurs, là où le premier élément du tableau (indice 0 ) est la position x, la seconde (indice 1) est la position y, et le troisième (index 2) est la position z. Voici un exemple très simple de créer un sommet pour une utilisation en OpenGL ES:

    GLfloat vertex[3];
vertex[0] = 10.0; // x
vertex[1] = 23.75; // y
vertex[2] = -12.532; // z


En OpenGL ES, vous devez généralement soumettre tous les sommets qui composent une partie ou l'ensemble des objets dans votre scène comme un Vertex Array. Un large vertex est tout simplement un tableau de valeurs (généralement GLfloats) qui contient les données de vertex pour certains ou de tous les objets dans le monde. Nous allons voir comment ce processus fonctionne dans le prochain post de cette série, mais la seule chose à retenir à propos des Vertex Arrays, c'est que leur taille est basée sur le nombre de sommets d'être soumis soit multiplié par trois (pour le dessin en trois dimensions d'espace) ou deux (pour le dessin en deux dimensions d'espace). Ainsi, un tableau de vertex qui détient six triangles dans l'espace tridimensionnel serait composé d'un tableau de 54 GLfloats, parce que chaque triangle a trois sommets, et chaque sommet a trois coordonnées et 6 x 3 x 3 = 54.

Faire face à toutes ces GLfloats peut être une douleur, cependant, parce que vous êtes constamment avoir à multiplier les choses dans votre tête et essayez de penser à ces tableaux en termes de sommets et de polygones qu'ils représentent. Heureusement, il existe un moyen plus facile. Nous pouvons définir une structure de données pour organiser un seul sommet, comme ceci:

typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
}
Vertex3D;

En faisant cela, notre code devient plus lisible:

Vertex3D vertex;
vertex.x = 10.0;
vertex.y = 23.75;
vertex.z = -12.532;

Maintenant, parce que notre structure Vertex3D est composé de trois GLfloats, en passant un pointeur sur une Vertex3D est exactement le même que le passage d'un pointeur vers un tableau de trois GLfloats. Il n'y a aucune différence pour l'ordinateur, les deux ont la même taille et le même nombre d'octets dans le même ordre que OpenGL attend d'eux. Le regroupement des données dans ces structures de données rend juste plus facile pour nous en tant que programmeur de visualiser et de traiter les données. Si vous téléchargez mon template Xcode depuis le début de cet article, cette structure de données et les fonctions de soutien, je vais discuter la prochaine ont déjà été définis dans le fichier nommé OpenGLCommon.h. Il ya aussi une fonction inline pour la création de sommets unique:

static inline Vertex3D Vertex3DMake(CGFloat inX, CGFloat inY, CGFloat inZ)
{
Vertex3D ret;
ret.x = inX;
ret.y = inY;
ret.z = inZ;
return ret;
}


Si vous vous souvenez de retour à la géométrie (ou peut-être vous n'avez pas, ce qui est correct), la distance entre deux points sur un plan est calculé en utilisant cette formule:

distance formula.png


Nous pouvons appliquer cette formule pour calculer la distance en ligne droite entre deux points quelconques dans un espace tridimensionnel avec cette fonction inline simple:

static inline GLfloat Vertex3DCalculateDistanceBetweenVertices (Vertex3D first, Vertex3D second)
{
GLfloat deltaX = second.x - first.x;
GLfloat deltaY = second.y - first.y;
GLfloat deltaZ = second.z - first.z;
return sqrtf(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ );
}
;


Triangles


Depuis OpenGL ES ne supporte que les triangles, on peut également créer une structure de données pour le groupe trois sommets en un objet unique triangle.

typedef struct {
Vertex3D v1;
Vertex3D v2;
Vertex3D v3;
}
Triangle3D;


Encore une fois, une seule Triangle3D est exactement le même comme un tableau de neuf GLfloats, c'est juste plus facile pour nous de traiter avec elle dans notre code, car nous pouvons construire des objets hors de sommets et de triangles plutôt que sur des tableaux de GLfloats.

Il ya un peu plus de choses que vous devez savoir sur les triangles, cependant. En OpenGL, il s'agit d'un concept connu sous le nom winding, Ce qui signifie simplement que l'ordre dans lequel les sommets sont dessinés questions. Contrairement aux objets dans le monde réel, les polygones en OpenGL n'ont généralement pas les deux faces d'eux. Ils ont un côté, ce qui est considéré comme le face avant, Et d'un triangle ne peut être vu si sa face avant qui fait face au spectateur. Alors qu'il est possible de configurer OpenGL pour traiter les polygones comme deux faces, par défaut, les triangles ont un seul côté visible. En sachant ce qui est la face avant ou visible du polygone, OpenGL est capable de faire la moitié de la quantité de calculs par polygone qu'il aurait à faire si les deux parties étaient visibles.

Bien qu'il existe des moments où un polygone voler de ses propres, et vous pourriez très bien vouloir le dos tirées, généralement un triangle fait partie d'un objet plus grand, et l'un des côtés du polygone sera face à l'intérieur de l'objet et ne sera jamais être vu. Le côté qui n'est pas établi est appelé un backface, Et détermine OpenGL qui est la face avant d'être tiré et qui est la contreface en regardant l'ordre de dessin des sommets. La face avant est celle qui serait établie en suivant les sommets dans le sens antihoraire de commande (par défaut, il peut être changé). Depuis OpenGL peut déterminer facilement quels triangles sont visibles pour l'utilisateur, il peut utiliser un processus appelé Contreface Culling pour éviter de faire le travail pour les polygones qui ne sont pas face à l'avant de la fenêtre et, par conséquent, ne peut pas être vu. Nous allons discuter de la fenêtre dans la prochaine affectation, mais vous pouvez penser que c'est la caméra virtuelle, ou une fenêtre virtuelle à la recherche dans le monde OpenGL.

winding.png


Dans l'illustration ci-dessus, le triangle cyan sur la gauche est une contreface et ne seront pas tirés parce que l'ordre que les sommets seraient établis en relation avec le spectateur est dans le sens horaire. D'autre part, le triangle sur la droite est un FrontFace qui sera tiré parce que l'ordre des sommets est anti-horaire par rapport au spectateur.

Dans la prochaine affectation de cette série, nous allons examiner la mise en place dans le monde virtuel en OpenGL et faire un peu de dessin simple à l'aide Vertex3D et Triangle3D. Dans le poste après cela, nous allons examiner transformations qui sont une façon d'utiliser l'algèbre linéaire pour déplacer des objets autour dans le monde virtuel.

Aucun commentaire: