Le calcul des normales dans un triangle du maillage
J'ai dessiné un triangle mesh avec 10000 sommets(100x100) et il sera l'herbe au sol. J'ai utilisé gldrawelements() pour cela. J'ai cherché toute la journée et ne peut toujours pas comprendre comment calculer les normales pour cette. Est-ce que chaque sommet ait son propre normales ou est-ce que chaque triangle a ses propres normales? Quelqu'un peut me pointer dans la bonne direction sur la façon de modifier mon code pour intégrer les normales?
struct vertices {
GLfloat x;
GLfloat y;
GLfloat z;
}vertices[10000];
GLuint indices[60000];
/*
99..9999
98..9998
........
01..9901
00..9900
*/
void CreateEnvironment() {
int count=0;
for (float x=0;x<10.0;x+=.1) {
for (float z=0;z<10.0;z+=.1) {
vertices[count].x=x;
vertices[count].y=0;
vertices[count].z=z;
count++;
}
}
count=0;
for (GLuint a=0;a<99;a++){
for (GLuint b=0;b<99;b++){
GLuint v1=(a*100)+b;indices[count]=v1;count++;
GLuint v2=(a*100)+b+1;indices[count]=v2;count++;
GLuint v3=(a*100)+b+100;indices[count]=v3;count++;
}
}
count=30000;
for (GLuint a=0;a<99;a++){
for (GLuint b=0;b<99;b++){
indices[count]=(a*100)+b+100;count++;//9998
indices[count]=(a*100)+b+1;count++;//9899
indices[count]=(a*100)+b+101;count++;//9999
}
}
}
void ShowEnvironment(){
//ground
glPushMatrix();
GLfloat GroundAmbient[]={0.0,0.5,0.0,1.0};
glMaterialfv(GL_FRONT,GL_AMBIENT,GroundAmbient);
glEnableClientState(GL_VERTEX_ARRAY);
glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );
glVertexPointer(3,GL_FLOAT,0,vertices);
glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}
EDIT 1
Voici le code que j'ai écrit. J'ai juste utilisé des tableaux au lieu de vecteurs et j'ai stocké tous les normales de la struct appelé normales. Il ne fonctionne toujours pas, cependant. J'obtiens une exception non gérée à *indices.
struct Normals {
GLfloat x;
GLfloat y;
GLfloat z;
}normals[20000];
Normals* normal = normals;
//***************************************ENVIRONMENT*************************************************************************
struct vertices {
GLfloat x;
GLfloat y;
GLfloat z;
}vertices[10000];
GLuint indices[59403];
/*
99..9999
98..9998
........
01..9901
00..9900
*/
void CreateEnvironment() {
int count=0;
for (float x=0;x<10.0;x+=.1) {
for (float z=0;z<10.0;z+=.1) {
vertices[count].x=x;
vertices[count].y=rand()%2-2;;
vertices[count].z=z;
count++;
}
}
//calculate normals
GLfloat vector1[3];//XYZ
GLfloat vector2[3];//XYZ
count=0;
for (int x=0;x<9900;x+=100){
for (int z=0;z<99;z++){
vector1[0]= vertices[x+z].x-vertices[x+z+1].x;//vector1x
vector1[1]= vertices[x+z].y-vertices[x+z+1].y;//vector1y
vector1[2]= vertices[x+z].z-vertices[x+z+1].z;//vector1z
vector2[0]= vertices[x+z+1].x-vertices[x+z+100].x;//vector2x
vector2[1]= vertices[x+z+1].y-vertices[x+z+100].y;//vector2y
vector2[2]= vertices[x+z+1].z-vertices[x+z+100].z;//vector2z
normals[count].x= vector1[1] * vector2[2]-vector1[2]*vector2[1];
normals[count].y= vector1[2] * vector2[0] - vector1[0] * vector2[2];
normals[count].z= vector1[0] * vector2[1] - vector1[1] * vector2[0];count++;
}
}
count=10000;
for (int x=100;x<10000;x+=100){
for (int z=0;z<99;z++){
vector1[0]= vertices[x+z].x-vertices[x+z+1].x;//vector1x -- JUST ARRAYS
vector1[1]= vertices[x+z].y-vertices[x+z+1].y;//vector1y
vector1[2]= vertices[x+z].z-vertices[x+z+1].z;//vector1z
vector2[0]= vertices[x+z+1].x-vertices[x+z-100].x;//vector2x
vector2[1]= vertices[x+z+1].y-vertices[x+z-100].y;//vector2y
vector2[2]= vertices[x+z+1].z-vertices[x+z-100].z;//vector2z
normals[count].x= vector1[1] * vector2[2]-vector1[2]*vector2[1];
normals[count].y= vector1[2] * vector2[0] - vector1[0] * vector2[2];
normals[count].z= vector1[0] * vector2[1] - vector1[1] * vector2[0];count++;
}
}
count=0;
for (GLuint a=0;a<99;a++){
for (GLuint b=0;b<99;b++){
GLuint v1=(a*100)+b;indices[count]=v1;count++;
GLuint v2=(a*100)+b+1;indices[count]=v2;count++;
GLuint v3=(a*100)+b+100;indices[count]=v3;count++;
}
}
count=30000;
for (GLuint a=0;a<99;a++){
for (GLuint b=0;b<99;b++){
indices[count]=(a*100)+b+100;count++;//9998
indices[count]=(a*100)+b+1;count++;//9899
indices[count]=(a*100)+b+101;count++;//9999
}
}
}
void ShowEnvironment(){
//ground
glPushMatrix();
GLfloat GroundAmbient[]={0.0,0.5,0.0,1.0};
GLfloat GroundDiffuse[]={1.0,0.0,0.0,1.0};
glMaterialfv(GL_FRONT,GL_AMBIENT,GroundAmbient);
glMaterialfv(GL_FRONT,GL_DIFFUSE,GroundDiffuse);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer( GL_FLOAT, 0, normal);
glVertexPointer(3,GL_FLOAT,0,vertices);
glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glPopMatrix();
}
//***************************************************************************************************************************
- Les sommets ne peuvent pas avoir les normales (sauf si elles sont une moyenne du visage normales pour les visages autour d'eux). Je n'ai jamais utilisé openGL, donc je ne sais pas si il y a une façon de les obtenir, tout simplement. J'imagine que oui, mais si vous voulez le faire vous-même, vous pouvez toujours calculer le bord des vecteurs de sommets et de calculer les normales en prenant le produit vectoriel des deux bords (puis la normalisation de magnitude 1).
- J'ai juste édité. J'ai apporté quelques modifications du code, mais il ne compile pas.
glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
va courir jusqu'à la fin de votre 59403-élémentindices
tableau.- À l'aide de 10000 sommets pour un sol herbe semble plutôt grand, à moins que c'est une très grande région avec beaucoup de choses intéressantes sur les caractéristiques du terrain. Vous pourriez envisager un maillage grossier avec le mapping de texture et le normal mapping. Juste une idée.
Vous devez vous connecter pour publier un commentaire.
Comme souvent, la réponse est: "Ça dépend". Depuis une normale est définie comme étant le vecteur perpendiculaire à tous les vecteurs dans un plan donné (en N dimensions), vous avez besoin d'un avion pour calculer la normale. Un sommet position est juste un point et donc singulier, si vous avez réellement besoin d'un visage pour calculer la normale. Donc, naïvement, on pourrait supposer que les normales sont par face comme la première étape dans le mode de calcul des normales est la détermination du visage normales, en évaluant le produit croisé des visages bords.
Dire que vous avez un triangle avec des points Un, B, C, alors ces points, la position des vecteurs ↑Un, ↑B, ↑C et les bords ont vecteurs ↑B ↑Un et ↑C ↑Un de sorte que le visage de vecteur normal est ↑Nf = (↑B ↑A) × (↑C ↑A)
Noter que l'ampleur de ↑Nf comme il est indiqué ci-dessus est directement proportionnelle à la face de la zone.
Dans les surfaces lisses sommets sont partagés entre les faces (ou l'on pourrait dire de ces faces, partagent un sommet). Dans ce cas, la normale au sommet n'est pas l'un des normales des faces de les visages elle est partie, mais une combinaison linéaire:
↑Nv = ∑ p ↑Nf ; où p est une pondération pour chaque face.
On pourrait adopter une pondération égale entre les participants du visage normales. Mais il est plus logique de supposer que plus un visage est grand, plus il contribue à la normale.
Maintenant, rappelez-vous que vous normaliser par un vecteur ↑v par le passage à l'échelle avec recipocal longueur: ↑vi = ↑v/|↑v|. Mais comme déjà dit, la longueur du visage normales dépend déjà le visage de la région. Ainsi, le facteur de pondération p donné ci-dessus, est déjà contenue dans le vecteur lui-même: Sa longueur, aka ampleur. Si nous pouvons obtenir le vertex vecteur normal par la simple addition de tous les normales des faces.
Dans les calculs d'illumination le vecteur normal doit être l'unité de longueur, c'est à dire normalisée afin d'être utilisable. Ainsi, après en résumé, nous normaliser les nouveaux vertex normal et l'utiliser.
L'attention de lecteur peut être remarqué que j'ai précisément dit lisse surfaces de partager des sommets. Et en fait, si vous avez quelques plis /bords durs dans votre géométrie, puis les visages de chaque côté ne partagent pas les sommets. Dans OpenGL l'un des sommets est l'ensemble de la combinaison de
Vous modifiez l'une de ces et vous avez complètement différent de ce sommet. Maintenant, certains modélisateurs 3D voir un sommet comme un point de la position et de stocker le reste de ces attributs par face (Blender est un modeleur). Cela permet d'économiser de la mémoire (ou de la mémoire considérable, en fonction du nombre d'attributs). Mais OpenGL besoins de l'ensemble de la chose, donc si vous travaillez avec une telle mixte paradigme fichier que vous allez avoir à le décomposer en OpenGL compatible avec les données de la première. Jetez un oeil à l'un de Blender exporter des scripts, comme les PLIS de l'exportateur pour voir comment c'est fait.
Maintenant pour couvrir quelque chose d'autre. Dans votre code, vous avez ceci:
L'index pointeur a rien à faire avec vertex indices de tableau! C'est un anachronsim de l'époque, lorsque les graphismes sont toujours palettes utilisées au lieu de la vraie couleur. Un des pixels de la couleur n'était pas mis en lui donnant de l'valeurs RVB, mais par un numéro unique de compensation dans une palette limitée de couleurs. Les couleurs de la Palette peuvent encore être trouvés dans plusieurs formats de fichiers graphiques, mais aucun décent morceau de matériel utilise plus.
Veuillez effacer glIndexPointer (et glIndex) à partir de votre mémoire et de votre code, ils ne font pas ce que vous pensez qu'ils font tout Le mode couleurs indexées est arcane utilisé, et franchement, je ne sais pas du tout de matériel construits après 1998 qui toujours soutenu.
Per-vertex.
L'utilisation de la croix-des produits pour calculer les normales des faces pour les triangles autour d'un même sommet, ajoutez-les ensemble, et de normaliser.
Thumbs up pour datenwolf! Je suis complètement d'accord avec son approche. Ajouter les vecteurs normaux des triangles adjacents pour chaque sommet, puis la normalisation est le chemin à parcourir. Je veux juste pousser la réponse un peu et de regarder de plus près le particulier mais assez fréquents cas d'un rectangulaire, lisse maillage qui a un constante de x/y de l'étape. En d'autres termes, une forme rectangulaire x/y de la grille avec une hauteur variable à chaque point.
Un tel maillage est créé en boucle sur x et y, et de fixer une valeur pour z et peut représenter des choses comme la surface d'une colline. De sorte que chaque point du maillage est représenté par un vecteur
où f(x,y) est une fonction donnant le z de chaque point de la grille.
Généralement pour dessiner un tel maillage, nous utilisons un TriangleStrip ou un TriangleFan mais toute technique devrait permettre de donner une topographie semblable pour la suite de triangles.
Pour un triangleStrip chaque point P=(x0, y0, z0) a 6 sommets adjacents notée
où ax/ay est la constante de la grille de l'étape sur l'axe x/y, respectivement. Sur un carré de la grille ax = ay.
Ainsi, chaque sommet est de 6 triangles adjacents, chacune avec son propre vecteur normal (notée N1 à N6). Ceux-ci peuvent être calculés en utilisant le produit vectoriel des deux vecteurs définissant le côté du triangle et en faisant attention à l'ordre dans lequel nous faisons la croix du produit. Si le vecteur normal de points dans la direction Z vers vous :
Et le vecteur normal pour chaque point P est la somme de N1 à N6. Nous normaliser après sommation. Il est très facile de créer une boucle, calculer les valeurs de chaque vecteur normal, les ajouter et puis normaliser. Toutefois, comme l'a souligné M. Shickadance, cela peut prendre un certain temps, en particulier pour les grandes mailles et/ou sur les appareils embarqués.
Si nous avons un oeil de plus près et effectuer les calculs à la main, nous allons découvrir que la plupart des termes s'annulent les uns les autres, nous laissant avec un très élégant et facile à calculer la solution finale pour le vecteur résultant N. Le point ici est d'accélérer les calculs en évitant de calculer les coordonnées de N1 à N6, faire 6 de la croix-produits et 6 ajouts pour chaque point. L'algèbre nous permet de sauter directement à la solution, utiliser moins de mémoire et moins de temps PROCESSEUR.
Je ne vais pas montrer les détails des calculs que c'est long mais simple et permet de passer à l'ultime expression du vecteur Normal pour n'importe quel point de la grille. Seulement N1 est décomposé par souci de clarté, les autres vecteurs de se ressembler. Après sommation, nous obtenons N qui n'est pas encore normalisé :
Là vous allez! Juste normaliser ce vecteur et vous avez le vecteur normal pour n'importe quel point de la grille, à condition de connaître les valeurs Z de ses voisins et l'horizontale/verticale étape de votre grille.
Noter que c'est la moyenne pondérée des environs triangles' vecteurs normaux. Le poids est l'aire des triangles et est déjà inclus dans le produit croisé.
Vous pouvez même le simplifier de plus, en ne prenant en compte les valeurs Z de quatre entourant les points (haut,bas,gauche et droite). Dans ce cas, vous bénéficiez de :
qui est encore plus élégant et encore plus rapide à calculer.
Espère que cela va faire quelques mailles plus rapide.
Cheers
Aussi simple que cela puisse paraître, le calcul de la normale d'un triangle n'est qu'une partie du problème. Le produit vectoriel des 2 côtés du polygone est suffisante dans les cas triangulaires, à moins que le triangle est effondré sur lui-même et dégénérés; dans ce cas, il n'est pas permis à la normale, de sorte que vous pouvez sélectionner un à votre goût.
Alors pourquoi est le produit vectoriel normalisé seulement une partie du problème? Le enroulement de commande des sommets de ce polygone définit la direction de la normale, c'est à dire si une paire de sommets est échangé en place, la normale pointe dans la direction opposée. Donc, en fait, cela peut être problématique si la maille elle-même contient des incohérences dans ce domaine, c'est à dire des parties de lui supposer une commande, tandis que d'autres supposent différents rangements. Un exemple célèbre est l'original Stanford Bunny modèle, où certaines parties de la surface dirigée vers l'intérieur, tandis que d'autres vers l'extérieur. La raison pour cela est parce que le modèle a été construit à l'aide d'un scanner, et aucun soin n'a été prise pour produire des triangles avec un remontage régulier de modèles. (évidemment, propres versions du lapin existent également)
L'enroulement problème est encore plus important si les polygones peuvent avoir plusieurs sommets, parce que dans ce cas, vous seriez en moyenne partielle normales de l'semi-triangulation de ce polygone. Considérons le cas où partielle normales sont pointant dans des directions opposées, résultant en des vecteurs normaux de longueur 0 lors de la prise de la moyenne!
Dans le même sens, déconnecté polygone des soupes et des nuages de points présentent des défis pour la reconstruction exacte en raison de la mal définis nombre d'enroulement.
Une stratégie souvent utilisée pour résoudre ce problème est de tirer hasard des rayons à partir de l'extérieur vers le centre de chaque demi-triangulation (c'est à dire ray de poignard dans le). Mais on ne peut pas supposer que la triangulation est valable que si les polygones peuvent contenir plusieurs sommets, de sorte que les rayons peuvent manquer que le sous-triangle. Si un rayon frappe, puis normal, opposé à la direction du rayon, c'est à dire avec point(ray, n) < .5 satisfait, peut être utilisé comme normal pour l'ensemble du polygone. Évidemment, cela est assez cher et les échelles, avec le nombre de sommets par polygone.
Heureusement, il existe un grand de nouveaux travaux qui décrit une méthode alternative qui n'est pas seulement plus rapide (pour les grandes et les objets complexes), mais aussi généralise la de clôture de " l'ordre concept pour les constructions au-delà des maillages polygonaux, comme les nuages de points et des polygones les soupes, les iso-surfaces, et le point-définir les surfaces, où la connectivité peut-être même pas défini!
Comme indiqué dans le document, la méthode construit un hiérarchique fractionnement de l'arbre représentation qui est raffiné progressivement, en prenant le parent " dipôle de l'orientation en compte à chaque opération de scission. Un polygone normal serait alors simplement de l'intégration (moyenne) sur l'ensemble de di-pôles (point+normal paires) du polygone.
Pour les personnes qui sont aux prises avec impur mesh/pcl données de Lidar scanners ou d'autres sources, ce qui pourrait def. être un changeur de jeu.
Pour ceux comme moi qui est venu à travers cette question, votre réponse pourrait être :