Calcul des normales d'un objet 3d

Soyez le premier à donner votre avis sur cette source.

Snippet vu 10 495 fois - Téléchargée 35 fois

Contenu du snippet

on m'a demandé sur le forum de poster mon algo de tri des sommets mais sorti de son contexte il ne sert pas a grand chose donc je poste ici ma fonction complete de calcul des normales d'un objet

toutes les critiques, si elles sont constructives, sont les bienvenues ;-)

Source / Exemple :


void objet::normales()
{
float tri[4000][7];
int i;
int j;
int k=0;

log("calcul des normales");

for (i=0; i<polygons_qty; i++)
    {
    

normx[polya[i]]=(((verty[polyb[i]])-(verty[polyb[i]]))*((vertz[polyc[i]])-(vertz[polyc[i]])))-(((verty[polyb[i]])-(verty[polyb[i]]))*((vertz[

polyc[i]])-(vertz[polyc[i]])));
    

normy[polya[i]]=(((vertz[polyc[i]])-(vertz[polyc[i]]))*((vertx[polya[i]])-(vertx[polya[i]])))-(((vertz[polyc[i]])-(vertz[polyc[i]]))*((vertx[

polya[i]])-(vertx[polya[i]])));
    

normz[polya[i]]=(((vertx[polya[i]])-(vertx[polya[i]]))*((verty[polyb[i]])-(verty[polyb[i]])))-(((vertx[polya[i]])-(vertx[polya[i]]))*((verty[

polyb[i]])-(verty[polyb[i]])));
    

normx[polya[i]]=normx[polya[i]]/sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));
    

normy[polya[i]]=normy[polya[i]]/sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));
    

normz[polya[i]]=normz[polya[i]]/sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));
    //2eme point
    normx[polyb[i]]=normx[polya[i]];
    normy[polyb[i]]=normy[polya[i]];
    normz[polyb[i]]=normz[polya[i]];
    //3eme point
    normx[polyc[i]]=normx[polya[i]];
    normy[polyc[i]]=normy[polya[i]];
    normz[polyc[i]]=normz[polya[i]];
    }

log("mise a 0 du tableau de tri");

for (i=0; i<4000; i++)
    {
    for (j=0; j<7; j++)
        {
        tri[i][j]=0;
        }
    }

log("tri des sommets");

for (i=0; i<vertices_qty; i++)
    {
    k=0;
    for (j=0; j<4000; j++)
        {
        if (vertx[i]==tri[j][0] && verty[i]==tri[j][1] && vertz[i]==tri[j][2])
            {
            tri[j][3]=tri[j][3]+normx[i];
            tri[j][4]=tri[j][4]+normy[i];
            tri[j][5]=tri[j][5]+normz[i];
            tri[j][6]=tri[j][6]+1;
            k=1;
            }
        }
    if(k==0)
    for (j=0; j<4000; j++)
        {
        if (tri[j][3]==0 && tri[j][4]==0 && tri[j][5]==0)
            {
            tri[j][0]=vertx[i];
            tri[j][1]=verty[i];
            tri[j][2]=vertz[i];
            tri[j][3]=normx[i];
            tri[j][4]=normy[i];
            tri[j][5]=normz[i];
            break;
            }
        }    
    }

log("calcul de la moyenne");

for (j=0; j<4000; j++)
    {
    tri[j][3]=tri[j][3]/tri[j][6];
    tri[j][4]=tri[j][4]/tri[j][6];
    tri[j][5]=tri[j][5]/tri[j][6];
    }

log("assignation des normales");

for (i=0; i<4000; i++)
    {
    for (j=0; j<vertices_qty; j++)
        {
        if (vertx[j]==tri[i][0] && verty[j]==tri[i][1] && vertz[j]==tri[i][2])
            {
            normx[j]=tri[i][3];
            normy[j]=tri[i][4];
            normz[j]=tri[i][5];
            }
        }
    }
}

Conclusion :


pour la comprehension de tout le monde, quelques infos:
vous pouvez voir aparaitre ci dessous dans le code source des trucs dans ce genre la: verty[ polyb[i] ]
cela est du au fait que j'utilise comme format de fichiers pour mes objets 3d le 3ds et que donc il stock les donnes des vertices mais aussi

l'ordre des points, et ce afin de pouvoir a coup sur calculer la normal dans le bon sens ;-)

vertx verty et vertz sont les 3 coordonnes des sommets des polygones
ploya ployb et polyc sont les infos d'ordre des sommets
normx normy et normz sont les 3 elements des normals

mon tableau de tri est constitué comme cela: vertx | verty | vertz | normx | normy | normz | diviseur pour faire la moyenne (nbre de fois ou

j'ai fait un ajout dans cette ligne)

a savoir qu'ici je dispose de 4000 polygones au max

en esperant que cela en aide plus d'un ;-)

A voir également

Ajouter un commentaire

Commentaires

Messages postés
326
Date d'inscription
vendredi 13 août 2004
Statut
Membre
Dernière intervention
2 novembre 2007
2
Histoire d'être sur de ne pas laisser des trucs en suspend :
Pour calculer les angles :
pour AV00 par exemple, tu prends les vecteurs V0V1 et V0V2 normalisés puis acos(V0V1.V0V2) . étant le Dot Product. Sachant aussi qu'en C/C++, les fonctions trigos cos, sin, acos etc... bossent en radians.

Et pour finir, saches qu'au niveau mathématique, la moyenne "de base" (non pondérée) à un "coefficient de robustesse" de 0.5 c'est à dire qu'elle peut aussi bien donner le bon résultat qu'un mauvais résultat.
Messages postés
326
Date d'inscription
vendredi 13 août 2004
Statut
Membre
Dernière intervention
2 novembre 2007
2
Pour l'histoire du length==0.0, ca depend d'ou proviennent tes normales, si tu es sur que tes normales au faces ne peuvent pas etre nulles alors ok, pas besoin de verifier.
Mais imaginons que tu calcules tes normales au faces et imaginons une face triangulaire (v0,v1,v2) (avec Vi,
i c [0..2] designant un point 3D), si jamais tu as Vi==Vj avec i!=j, i,j c [0..2]² alors (v1-v0)^(v2-v0)=(0.0,0.0,0.0)
(en Counter Clock Wise au sinon faire (v2-v0)^(v1-v0) mais c'est aussi égal au vecteur nul).
(au fait ^ c'est le cross product)
Voila pour l'explication du length==0.0, a toi de voir, mais je pense qu'on est jamais trop prudent :)

Sinon une méthode qui marche bien (voire super bien...) pour le calcul des normales aux sommets c'est de pondérer la moyenne des normales aux faces par l'angle associé au sommet dans la face.
C'est pas clair ?, JE M'EXPLIQUE !!!!! :
(t'inquiètes pas c'est pas long et l'exemple est simple, prends une feuille et un crayon au cas mes explications ne soient pas assez claires).

En tenant compte qu'on est dans un repère direct orthonormé oxyz.

Notons 6 sommets Vi, i c [0..5] :
V0=(0.0,0.0,0.0)
V1=(1.0,0.0,0.0)
V2=(1.0,0.0,1.0)
V3=(0.0,1.0,1.0)
V4=(0.0,0.0,1.0)
V5=(0.0,1.0,0.0)

Notons 5 faces triangulaires (en CCW)
Fj(Vi0,Vi1,Vi2), j c [0..4].
Vik, k c [0..2] un des sommets précedents.
et leurs normales NFj(X,Y,Z).
F0(0,1,2) -> NF0(0.0,-1.0,0.0)
F1(0,2,4) -> NF1(0.0,-1.0,0.0)
F2(0,5,1) -> NF2(0.0,0.0,-1.0)
F3(0,3,5) -> NF3(-1.0,0.0,0.0)
F4(0,4,3) -> NF4(-1.0,0.0,0.0)

Dans le plan oxz on a :

z
|
V4......................V2
| . . NF0 et NF1 pointent vers
| . . toi.
| F1 . .
| . .
| . F0 .
V0------------------V1--x
o

Dans le plan oxy on a :

y
|
V5 .
| . NF2 pointe vers l'ecran
| .
| F2 .
| .
| .
V0------------------V1--x
o

Dans le plan oyz on a :

z
|
V4......................V3
| . . NF3 et NF4 pointent vers
| . . l'ecran.
| F4 . .
| . .
| . F3 .
V0------------------V5--y
o

Calculons maintenant la normale au sommet V0 (qui participe à toutes les faces), si tu visualises bien l'exemple, tu seras d'accord pour dire que la normale a trouvé est NV0=(-1.0,-1.0,-1.0).

Par la moyenne des faces on trouve donc :
NV0=(NF0+NF1+NF2+NF3+NF4)/5=(-2/5,-2/5,-1/5).
Donc ca ne marche pas....

Par la méthode que j'explique (que je tente d'expliquer...)
Notons les angles du sommet V0 dans chaque face Fi
par AV0i l'angle dans la face i
(par exemple AV00 =angle entre V0V1 et V0V2).
AV00=PI/4
AV01=PI/4
AV02=PI/2
AV03=PI/4
AV04=PI/4
avec une moyenne des normales aux faces pondérées par ces angles on a
NV0=NF0*AV00+NF1*AV01+NF2*AV02+NF3*AV03+NF4*AV04=(-2.0*PI/4 , -1.0*PI/2 , -2.0*PI/4)=
(-1.0,-1.0,-1.0) apres normalisation.

J'espère que j'ai été clair et que cela puisse t'aider.

@+, KeniiyK.
Messages postés
85
Date d'inscription
jeudi 7 février 2002
Statut
Membre
Dernière intervention
8 février 2007

"Ta méthode de calcul pour une normale a un sommet c'est quoi exactement ?.
Pour chaque sommet tu fais une moyenne des normales des faces auxquelles il appartient ?. (Si c'est ca il y a mieux...)"

je suis preneur ;-)

par contre je ne vois pas l'utilité de verifier due le lenght ne soit pas nul vue que une normale (0,0,0) n'est pas possible??
Messages postés
85
Date d'inscription
jeudi 7 février 2002
Statut
Membre
Dernière intervention
8 février 2007

"Ta méthode de calcul pour une normale a un sommet c'est quoi exactement ?.
Pour chaque sommet tu fais une moyenne des normales des faces auxquelles il appartient ?. (Si c'est ca il y a mieux...)"

je suis preneur ;-)

par contre je ne vois pas l'utilité de verifier due le lenght ne soit pas nul vue que une normale (0,0,0) n'est pas possible??
Messages postés
326
Date d'inscription
vendredi 13 août 2004
Statut
Membre
Dernière intervention
2 novembre 2007
2
Ta méthode de calcul pour une normale a un sommet c'est quoi exactement ?.
Pour chaque sommet tu fais une moyenne des normales des faces auxquelles il appartient ?. (Si c'est ca il y a mieux...)

Au sinon juste un conseil :
dans ton code tu fais ca :
normx[polya[i]]=normx[polya[i]]/sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));

normy[polya[i]]=normy[polya[i]]/sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));

normz[polya[i]]=normz[polya[i]]/sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));

range plutot dans un float ou un double le diviseur :

float/double length=sqrt((normx[polya[i]]*normx[polya[i]])+(normy[polya[i]]*normy[polya[i]])+(normz[polya[i]]*normz[polya[i]]));

puis faire :
normx[polya[i]]/=length;
normy[polya[i]]/=length;
normz[polya[i]]/=length;

Au moins tu ne calcules qu'une seule fois le length, et il faut aussi verifier qu'il ne soit pas nul.
Afficher les 8 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.

Du même auteur (cs_erazor)