mardi 14 février 2012

OpenGL ES à partir de zéro, Partie 3: Fenêtres en perspective

Maintenant que vous avez un avant-goût de la façon de dessiner en OpenGL, prenons un peu de recul et de parler de quelque chose de très important: l'OpenGL viewport. Beaucoup de gens qui sont nouveaux à la programmation 3D, mais qui ont travaillé avec des programmes graphiques en 3D comme Maya, Blender, ou Lightwave, s'attendre à trouver un objet dans le monde virtuel d'OpenGL appelle un "appareil photo". Il n'est pas bête, tel. Qu'est-ce qu'il ya, c'est un morceau défini de l'espace 3D qui peuvent être vus. Le monde virtuel est infini, mais les ordinateurs ne traitent pas bien avec l'infini, donc OpenGL nous demande de définir un morceau d'espace qui peut être vu par le spectateur.

Si nous pensons à lui en termes de l'objet caméra qui la plupart des programmes 3D ont, au milieu d'une extrémité de la fenêtre se trouve la caméra. C'est le point où le spectateur est debout. C'est une fenêtre virtuelle dans le monde virtuel. Il ya une certaine quantité d'espace que le spectateur peut voir. Elle ne peut pas voir les choses derrière elle. Elle ne peut pas voir les choses en dehors de son angle de vue. Et elle ne peut pas voir des choses qui sont trop loin. Pensez à la fenêtre comme une forme déterminée par les paramètres "ce que le spectateur peut voir». Cela semble assez simple, non?

Malheureusement, il n'est pas. Pour expliquer pourquoi, nous devons d'abord parler du fait qu'il ya deux différents types de fenêtres que vous pouvez créer en OpenGL ES: orthographique et en perspective.

Orthographique vs perspective


Pour mieux comprendre cela, nous allons parler de voies ferrées, d'accord? Maintenant, les deux rails d'une voie ferrée, afin de fonctionner correctement, à être exactement une certaine distance de faille de l'autre. La distance exacte varie en fonction de là où les pistes sont, et ce type de voyages en train sur eux, mais il est important que les rails (et les roues sur le train) soit la même distance. Si tel n'était pas le cas, les trains ne serait tout simplement pas être en mesure de fonctionner.

Ce fait est évident, si vous regardez voie ferrée par le haut.

tracks.jpg


Mais qu'advient-il si vous vous tenez sur la voie ferrée et regarder vers le bas entre eux. Ne dites pas «vous êtes touché par le train", je suppose que vous êtes assez intelligent pour faire cela quand le train ne vient pas.

tracks-perspective.jpg


Ouais, les pistes ressemblent ils se rapprochent car ils s'éloignent de nous. C'est, comme vous êtes sans doute grâce à votre bien conscient professeur d'art de deuxième année, est due à quelque chose qui s'appelle perspective.

L'une des deux manières que les fenêtres OpenGL peuvent être mis en place est d'utiliser la perspective. Lorsque vous configurez une fenêtre de cette façon, les objets deviennent plus petits car ils s'éloignent, et les lignes convergent en s'éloignant du spectateur. Cela simulera vraie vision, le peuple ainsi voir les choses dans le monde réel.

L'autre façon vous pouvez configurer un port vue est appelée une orthogonal viewport. Dans ce type de fenêtre, les lignes ne convergent et les choses ne changent pas de taille. Il n'y a aucune perspective. Ceci est pratique pour les programmes de CAO et un certain nombre d'autres fins, mais il ne semble pas réel, car ce n'est pas la façon dont nos yeux le travail, il est donc généralement pas ce que vous voulez.

Avec une fenêtre orthogonale, vous pouvez mettre votre caméra virtuelle sur la voie ferrée, mais ces rails ne sera jamais converger. Ils resteront la même distance comme ils s'éloignent de vous. Même si vous avez défini une fenêtre infiniment grand (que vous ne pouvez pas faire dans OpenGL ES) ces lignes resterait la même distance.

La bonne chose à propos des fenêtres orthogonales, c'est qu'ils sont faciles à définir. Comme les lignes ne divergent, il vous suffit de définir un morceau du monde en 3D qui ressemble à une boîte, comme ceci:

viewport.jpg


Mise en place d'une orthogonale Viewport


Vous pouvez dire à OpenGL ES que vous souhaitez configurer une fenêtre orthogonale en utilisant le glOrthof function () avant de vous mettre déclarer votre fenêtre en utilisant les glViewport () fonction. Voici un exemple simple:
    CGRect rect = view.bounds;     
glOrthof(-1.0, // Left
1.0, // Right
-1.0 / (rect.size.width / rect.size.height)
, // Bottom
1.0 / (rect.size.width / rect.size.height), // Top
0.01, // Near
10000.0);
// Far
glViewport(0, 0, rect.size.width, rect.size.height);

Ce n'est pas vraiment trop difficile à comprendre. Nous obtenons d'abord la taille de notre vue. Nous faisons de notre morceau de l'espace que nous recherchons en deux unités de large, la course de -1,0 à 1,0 sur l'axe des abscisses. Assez facile.

Ensuite, ce qui se passe avec le bas et le haut? Eh bien, nous voulons que les coordonnées X et Y de notre morceau d'espace pour avoir le même ratio d'aspect que notre point de vue (ce qui, dans une application en plein écran est le ratio d'aspect de l'écran de l'iPhone). Depuis la largeur de l'iPhone et la hauteur sont différentes, nous avons besoin pour s'assurer les coordonnées x et y de notre point de vue sont différents aussi, dans la même proportion.

Après cela, nous définissons une limite près ou de loin à délimiter la profondeur de notre volume de visualisation. Le paramètre proximité est l'endroit où commence la fenêtre. Si nous sommes assis sur l'origine, la fenêtre commence juste en face de lui, donc il est d'usage d'utiliser ou de 0,01 0.001 comme le début d'une fenêtre orthogonales. Cela commence une fraction minuscule de «front» de l'origine. La coordonnée Jusqu'où peut-être réglé sur la base des besoins de l'application que vous écrivez. Si vous n'aurez jamais un objet plus loin que 20 unités, vous n'avez pas besoin de fixer un jour de 20.000 unités. Exactement ce numéro que vous utilisez va varier d'un programme à.

Après l'appel à glOrthof (), nous appelons glViewport () avec un rectangle de la vue, et nous avons fini.

Ce fut le cas facile.

Mise en place la fenêtre en perspective

L'autre cas n'est pas aussi simple, et voici pourquoi. Si les objets sont plus petits car ils s'éloignent de vous, qu'est-ce que faire pour la forme du morceau de l'espace que vous pouvez voir. Vous pouvez voir plus de monde que le plus loin de vous, de sorte que le morceau de l'espace dont vous avez besoin de définir n'est pas un cube si vous utilisez la perspective. Non, la forme de l'espace que vous pouvez voir lorsque vous utilisez perspective est appelé un frustum. Ouais, je sais. Mot étrange, non? Mais c'est une chose réelle. Notre tronc va ressembler à ceci:

frustum.jpg


Notez que lorsque nous nous éloignons du point de vue (en d'autres termes, tant que la valeur de z diminue), le volume devient plus grande vision sur les deux coordonnées x et y.

Pour configurer une fenêtre en perspective, nous n'utilisons pas glOrthof (), nous utilisons une fonction différente appelée glFrustumf (). Cette méthode prend les mêmes six paramètres. C'est assez facile à comprendre, mais comment pouvons-nous comprendre ce que nombres de passer dans glFrustumf ()?

Eh bien, proches et lointains sont faciles. Vous les comprendre de la même façon. Utilisez quelque chose comme 0.001 pour les proches, puis la base jusqu'à présent sur les besoins de votre programme spécifique.

Mais qu'en est-gauche, droite, bas et haut. Pour définir celles-ci, nous allons avoir besoin de faire un peu de maths.

Pour calculer notre tronc, nous devons d'abord comprendre notre champ de vision, Qui est définie par deux angles. Faisons-le: Stick deux bras de la vôtre à droite en face de vous, les paumes ensemble. Vos bras sont maintenant pointant vers le bas de l'axe z de votre tronc personnelle, non? Bon, maintenant, déplacez vos mains en dehors lentement. Parce que vos épaules rester dans la même position que vos mains se séparent, vous définissez un angle plus large. C'est l'un des deux angles qui définit votre tronc propre visionnement. C'est l'angle qui définit la largeur de votre champ de vision, l'autre serait le cas si vous ne l'avez exactement la même chose, mais déplacé votre part de haut en bas, par opposition à gauche et à droite .. Si vos mains sont trois pouces de distance, le X-angle est assez petit.

narrow_field.jpg

Un champ de vision étroit.


Si vous déplacez les deux pieds en dehors, vous créez un angle beaucoup plus large, et un large champ de vision.

wide_field.jpg

Un large champ de vision.


Si vous êtes dans la photographie, vous pouvez penser du champ de vision que la focale de la lentille de notre caméra virtuelle virtuel. Un champ de vision étroit est un peu comme un téléobjectif, la création d'un tronc de longs cierges lentement. Un large champ de vision est comme un objectif grand angle et crée un tronc qui augmente en taille beaucoup plus rapide.

Reprenons un beau moyen-de-la valeur de route pour commencer, disons 45 ^ 0. Maintenant que nous avons cette valeur, comment nous l'utilisons pour calculer notre tronc visualisation? Eh bien, regardons l'un des deux angles. Imaginez, si vous voulez, ce que le tronc ressemble à du haut. Heck, vous n'avez pas à imaginer, voici un schéma:

topview.png


Bon, d'en haut, il ressemble un peu comme un triangle, avec un peu d'un point de ébranchés, n'est-ce pas? Eh bien, c'est assez proche d'un triangle à nos fins. Maintenant, vous souvenez-vous tangents de la classe trigo? La fonction tangente est définie comme le rapport de la jambe opposée d'un triangle rectangle à la jambe adjacentes.



D'accord, mais nous n'avons pas un triangle rectangle, avons-nous?

En fait, nous avons deux ... Si nous traçons une ligne droite vers le bas de l'axe z:
split_triangle.png


C'est la ligne pointillée au centre est la «jambe adjacentes» des deux triangles rectangles nous venons de créer en dessinant cette ligne. Ainsi, la moitié de la largeur de l'extrémité du tronc est la tangente de la moitié de l'angle de notre champ de vision. Si nous prenons cette valeur et le multiplier par la valeur proche, nous avons la valeur à transmettre en tant que droit. Nous passons à l'inverse de ce nombre comme à gauche.

Nous voulons que notre champ de vision pour avoir le ratio d'aspect identique à l'écran, afin que nous puissions calculer les valeurs haut et en bas exactement comme nous l'avons fait avec glOrthof () - en multipliant la juste valeur par le ratio d'aspect de l'écran. Dans le code, qui devrait ressembler à ceci:

CGRect rect = view.bounds; 
GLfloat size = .01 * tanf(DEGREES_TO_RADIANS(45.0) / 2.0);

glFrustumf(-size, // Left
size, // Right
-size / (rect.size.width / rect.size.height)
, // Bottom
size / (rect.size.width / rect.size.height), // Top
.01, // Near
1000.0);
// Far

Note: Une discussion sur la façon glFrustum () utilise les paramètres passés à calculer la forme du tronc allez avoir à attendre jusqu'à ce que nous avons discuté des matrices. Pour l'instant, il suffit de prendre sur la foi que ce calcul fonctionne, d'accord?

Voyons voir en action. J'ai modifié le drawView final: la méthode de la dernière affectation de sorte qu'au lieu d'un icosaèdre, il montre thirty icosaèdres s'étendant jusqu'à l'axe des z. Voici la nouvelle drawView: méthode.

- (void)drawView:(GLView*)view;
{
static GLfloat rot = 0.0;

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();
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);
for (int i = 1; i <= 30; i++)
{
glLoadIdentity();
glTranslatef(0.0f,-1.5,-3.0f * (GLfloat)i);
glRotatef(rot, 1.0, 1.0, 1.0);
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];
}

Si vous laissez tomber ce code dans un projet créé à partir de Modèle de projet OpenGL pour Xcode, Qui met en place une fenêtre en perspective utilisant glFrustumf () avec un 45 ^ 0 champ de vision, vous obtenez quelque chose qui ressemble à ceci:

frustum_simulator.jpg


Nice, non? Ils deviennent plus petits comme ils vont loin de vous, très semblables en apparence à ceux rails comme ils s'éloignent de vous.

Si nous ne faisons rien d'autre que changer le glFrustumf () appellent à une glOrthof () appel, il semble bien différente:

iPhone SimulatorScreenSnapz002.jpg


Sans perspective, la icosaèdres vingt à neuf derrière le premier sont obscurcies par la première. Il n'ya pas de perspective, de sorte que chaque forme se trouve exactement derrière l'un en face de lui sur l'axe z.

Ok, c'était un sujet lourd, et la vérité de la question est que vous pouvez oublier tout sur le déclen maintenant. Il suffit de copier les deux lignes de code qui permettent de calculer un tronc repose sur un champ de l'angle de vision, et vous n'aurez probablement jamais besoin de se rappeler pourquoi cela fonctionne.

Restez à l'écoute pour l'aventure passionnante de la semaine prochaine ...


Dans le prochain épisode, nous allons faire la lumière sur notre icosaèdre et la faire ressembler à une vraie, honnête à la bonté forme tridimensionnelle plutôt que colorée, mais objet plat.

lights.jpg


Aucun commentaire: