Dans une tôt après J'ai parlé des normales de surface en OpenGL. Dans l'écriture de la classe de chargement Wavefront OBJ, j'ai appris beaucoup plus sur les normales que je n'ai jamais voulu savoir. Parmi ces choses que j'ai apprise est qu'il ya deux différents types de normales - normales de surface, qui sont ce dont j'ai parlé dans les normales de surface d'affichage, et les normales des sommets.
Trouver une belle explication simple de ce sommet normales est, n'est pas réellement aussi simple que ça. Ce qui est dommage, car ce n'est vraiment pas un concept difficile. Une normale au vertex est simplement la moyenne des normales de surface de tous les polygones d'un particulier, le sommet est partie. Regardez l'image ci-dessous:

Ce n'est pas un cube, par la manière dont, pour simplifier, nous recherchons dans un plat, en deux dimensions de maille de six triangles. Il ya un total de sept sommets utilisés pour faire la forme. Ce sommet a marqué un est partagé par tous les six triangles, donc la normale au vertex pour ce sommet est la moyenne des normales de surface pour les sept triangles. Moyenne est faite par élément, donc les valeurs de x sont en moyenne, les valeurs y sont en moyenne, et les valeurs z sont en moyenne, et le résultat, mis dans une nouvelle Vector3D est le vecteur moyenne.
Regardons comment elles ont été calculées dans le chargeur de Wavefront OBJ:
Si nous utilisons l'ombrage GL_FLAT, le tableau des normales nous avons besoin de construire pour le maillage photo ci-dessus aura six vecteurs, ce qui désigne dix-huit GLfloats. Chaque vecteur de ce tableau doit être la normale de surface normalisée pour un triangle, et les normales doivent être dans le tableau dans le même ordre que les triangles sont passés dans glDrawElements ().
Voici comment nous pouvons calculer la surface normale (ce qui est un algorithme légèrement différent de celui de mon affectation précédente). La deuxième fonction calcule la surface normale, mais utilise la fonction première.
Donc, pour créer le tableau des normales pour GL_FLAT, nous avons simplement besoin d'une boucle sur les triangles (dans le même ordre nous allons plus tard, en boucle à travers eux pour les attirer, et d'appeler Triangle3DCalculateSurfaceNormal (), stocker les résultats dans un tableau de GLfloats ou Vector3D (souvenez-vous, un Vector3D est le même comme un tableau de trois GLfloats.
Maintenant, si nous utilisons un modèle d'ombrage des GL_SMOOTH, cependant, nous avons besoin de passer dans un tableau différent normale - un tableau de normales des sommets. Et cette fois, au lieu de passer un tableau qui contient une normale pour chaque triangle, nous avons besoin pour passer une normale pour chaque sommet, dans le même ordre que les sommets apparaissent dans le tableau de vertex que nous passons à OpenGL en utilisant glVertexPointer (). Pour la rapidité et la simplicité, je calcule la surface et normales des sommets dans la même méthode dans le chargeur de Wavefront OBJ:
Une chose que vous avez sans doute remarqué, c'est que j'ai utilisé calloc () plutôt que malloc (). Ces deux appels faire à peu près la même chose, sauf que calloc initialise tous les éléments à 0, ce qui est important quand vous faites la course et compte ajouter des valeurs plutôt que de fixer des valeurs.
Maintenant, pour mappage de texture ...
Trouver une belle explication simple de ce sommet normales est, n'est pas réellement aussi simple que ça. Ce qui est dommage, car ce n'est vraiment pas un concept difficile. Une normale au vertex est simplement la moyenne des normales de surface de tous les polygones d'un particulier, le sommet est partie. Regardez l'image ci-dessous:
Ce n'est pas un cube, par la manière dont, pour simplifier, nous recherchons dans un plat, en deux dimensions de maille de six triangles. Il ya un total de sept sommets utilisés pour faire la forme. Ce sommet a marqué un est partagé par tous les six triangles, donc la normale au vertex pour ce sommet est la moyenne des normales de surface pour les sept triangles. Moyenne est faite par élément, donc les valeurs de x sont en moyenne, les valeurs y sont en moyenne, et les valeurs z sont en moyenne, et le résultat, mis dans une nouvelle Vector3D est le vecteur moyenne.
Regardons comment elles ont été calculées dans le chargeur de Wavefront OBJ:
Normales GL_FLAT et Surface
Si nous utilisons l'ombrage GL_FLAT, le tableau des normales nous avons besoin de construire pour le maillage photo ci-dessus aura six vecteurs, ce qui désigne dix-huit GLfloats. Chaque vecteur de ce tableau doit être la normale de surface normalisée pour un triangle, et les normales doivent être dans le tableau dans le même ordre que les triangles sont passés dans glDrawElements ().
Voici comment nous pouvons calculer la surface normale (ce qui est un algorithme légèrement différent de celui de mon affectation précédente). La deuxième fonction calcule la surface normale, mais utilise la fonction première.
static inline Vector3D Vector3DMakeWithStartAndEndPoints(Vertex3D start, Vertex3D end)
{
Vector3D ret;
ret.x = end.x - start.x;
ret.y = end.y - start.y;
ret.z = end.z - start.z;
vectorNormalize(&ret);
return ret;
}
static inline Vector3D Triangle3DCalculateSurfaceNormal(Triangle3D triangle)
{
Vector3D u = Vector3DMakeWithStartAndEndPoints(triangle.v2, triangle.v1);
Vector3D v = Vector3DMakeWithStartAndEndPoints(triangle.v3, triangle.v1);
Vector3D ret;
ret.x = (u.y * v.z) - (u.z * v.y);
ret.y = (u.z * v.x) - (u.x * v.z);
ret.z = (u.x * v.y) - (u.y * v.x);
return ret;
}
Donc, pour créer le tableau des normales pour GL_FLAT, nous avons simplement besoin d'une boucle sur les triangles (dans le même ordre nous allons plus tard, en boucle à travers eux pour les attirer, et d'appeler Triangle3DCalculateSurfaceNormal (), stocker les résultats dans un tableau de GLfloats ou Vector3D (souvenez-vous, un Vector3D est le même comme un tableau de trois GLfloats.
GL_SMOOTH et Vertex Normals
Maintenant, si nous utilisons un modèle d'ombrage des GL_SMOOTH, cependant, nous avons besoin de passer dans un tableau différent normale - un tableau de normales des sommets. Et cette fois, au lieu de passer un tableau qui contient une normale pour chaque triangle, nous avons besoin pour passer une normale pour chaque sommet, dans le même ordre que les sommets apparaissent dans le tableau de vertex que nous passons à OpenGL en utilisant glVertexPointer (). Pour la rapidité et la simplicité, je calcule la surface et normales des sommets dans la même méthode dans le chargeur de Wavefront OBJ:
- (void)calculateNormalsL'idée de base est que, comme on boucle à travers les triangles, nous gardons une trace de combien de fois chaque sommet est utilisé, et chaque fois que nous utilisons un, nous ajoutons la surface normale du triangle à l'élément du tableau de vertex correspondant au sommet étant utilisé . Quand nous sommes tous fait, on boucle par les sommets, en divisant Vertex Array, qui a la somme de toutes les normales de surface pour tous les triangles dans lequel ce sommet est utilisé, et le diviser par le nombre de triangles il est utilisé po
{
if (surfaceNormals)
free(surfaceNormals);
// Calculate surface normals and keep running sum of vertex normals
surfaceNormals = calloc(numberOfFaces, sizeof(Vector3D));
vertexNormals = calloc(numberOfVertices, sizeof(Vector3D));
NSUInteger index = 0;
NSUInteger *facesUsedIn = calloc(numberOfVertices, sizeof(NSUInteger)); // Keeps track of how many triangles any given vertex is used in
for (int i = 0; i < [groups count]; i++)
{
OpenGLWaveFrontGroup *oneGroup = [groups objectAtIndex:i];
for (int j = 0; j < oneGroup.numberOfFaces; j++)
{
Triangle3D triangle = Triangle3DMake(vertices[oneGroup.faces[j].v1], vertices[oneGroup.faces[j].v2], vertices[oneGroup.faces[j].v3]);
surfaceNormals[index] = Triangle3DCalculateSurfaceNormal(triangle);
vectorNormalize(&surfaceNormals[index]);
vertexNormals[oneGroup.faces[j].v1] = Vector3DAdd(surfaceNormals[index], vertexNormals[oneGroup.faces[j].v1]);
vertexNormals[oneGroup.faces[j].v2] = Vector3DAdd(surfaceNormals[index], vertexNormals[oneGroup.faces[j].v2]);
vertexNormals[oneGroup.faces[j].v3] = Vector3DAdd(surfaceNormals[index], vertexNormals[oneGroup.faces[j].v3]);
facesUsedIn[oneGroup.faces[j].v1]++;
facesUsedIn[oneGroup.faces[j].v2]++;
facesUsedIn[oneGroup.faces[j].v3]++;
index++;
}
}
// Loop through vertices again, dividing those that are used in multiple faces by the number of faces they are used in
for (int i = 0; i < numberOfVertices; i++)
{
if (facesUsedIn[i] > 1)
{
vertexNormals[i].x /= facesUsedIn[i];
vertexNormals[i].y /= facesUsedIn[i];
vertexNormals[i].z /= facesUsedIn[i];
}
vectorNormalize(&vertexNormals[i]);
}
free(facesUsedIn);
}
Une chose que vous avez sans doute remarqué, c'est que j'ai utilisé calloc () plutôt que malloc (). Ces deux appels faire à peu près la même chose, sauf que calloc initialise tous les éléments à 0, ce qui est important quand vous faites la course et compte ajouter des valeurs plutôt que de fixer des valeurs.
Maintenant, pour mappage de texture ...
Aucun commentaire:
Enregistrer un commentaire