Author Topic: Orienter un quad selon un vecteur  (Read 5212 times)

0 Members and 1 Guest are viewing this topic.

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Orienter un quad selon un vecteur
« on: 05 June 2013 à 10:38:48 »
Salut,

J'ai conscience que c'est une question très basique mais je n'ai pas envie de chercher plus longtemps. Marre de faire de la mécanique, je veux faire des effets ;)

Donc mon problème est le suivant : je veux pouvoir créer un quad selon n'importe quelle orientation, en me basant sur sa normale.

Je pense qu'il faut que je construise un repère à partir de cette normale pour pouvoir ensuite placer mes vertices correctement, mais toutes les solutions que j'imagine me semblent bien compliquées, voilà pourquoi je m'adresse à vous.

Pour l'instant, ce que j'imagine :

Mon quad est défini par sa taille selon les trois axes dans un vec3 (size) et sa normale (N).
1) construire le repère relatif au quad en partant de N : quadX, quadY, quadZ
2) créer les vertices en fonction de ce repère et de sa taille (size.x * quadX, size.y * quadY, size.z *quadY)

Je pense que mon raisonnement est bon, c'est pour le point 1 que je n'arrive pas à mettre mes idées en ordre.

Ou alors j'ai toujours l'option de le créer dans le plan XY et ensuit de multiplier les vertices par une matrice de rotation qui le remettra dans l'orientation voulue, mais là pareil il faudrait que je crée cette matrice en fonction de N, ce qui revient à peu près à faire le point 1, non ?

Merci d'avance ! :)

Offline Zavie

Re : Orienter un quad selon un vecteur
« Reply #1 on: 05 June 2013 à 11:31:12 »
Ton raisonnement est bon, si ce n'est que je vois pas trop ce que sont size.xyz (un quad n'a que deux dimensions).

Effectivement il te faut déterminer un triplet de vecteurs {X, Y, Z} qui formeront une base. Tu places ton quad, tu l'orientes suivant la base, et voilà. Comme tu as N, on peut déjà définir Z = N, et il reste à sortir les deux autres de ton chapeau.

La seule chose que l'on sait, c'est qu'ils forment une base orthonormée. Ils sont donc de norme 1 et vérifient X^Y = Z, Y^Z = X et Z^X=Y (avec ^ l'opérateur du produit vectoriel). Le problème, c'est qu'il y a une infinité de bases qui vérifient ça (toutes les bases autour de l'axe N). Il te faut donc fixer un vecteur (par exemple Y).

Tu peux le fixer par rapport au Y de l'écran : tes billboards se comporteront comme des point sprite.
Tu peux le fixer par rapport au Y monde : ça entraîne un cas qu'il faut gérer à part quand N et Y sont colinéaires.

Bref, appelons le vecteur que tu as choisi Y0.

Tu en déduis X = Y0^N et finalement Y = N^X. Et voilà.
Note que finalement, N est une contrainte forte et Y0 une contrainte secondaire. Mais tu pourrais aussi considérer que c'est Y la contrainte absolue, et N la contrainte secondaire, pour obtenir ces billboards utilisés pour les arbres et autres objets axiaux. :)

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #2 on: 05 June 2013 à 11:41:59 »
Merci Zavie !

En effet, size doit être un vec2 et non un vec3, j'ai tapé mon message un peu vite :)

Mon N peut-être complètement quelconque, donc effectivement, je pense qu'il faut que je le fixe par rapport au Y du monde. J'avais l'intuition de faire comme tu le décris, mais que faire quand N et Y sont colinéaires ? Le cas ne devrait pas se produire souvent, mais quand même, dans ce cas je le fixe par rapport à un autre axe ? X ou Z ?

Ou alors je le choisis en fonction de l'orientation globale de N ? Je me demandais si je ne devais pas choisir cet axe en fonction de la plus grande composante de N...

Offline Zavie

Re : Orienter un quad selon un vecteur
« Reply #3 on: 05 June 2013 à 13:51:25 »
Tout dépend de comment tu veux que ton billboard se comporte.

Si N et Y sont colinéaires, est-ce que ça correspond à une situation qui a du sens ? Pour les arbres c'est typiquement le cas quand on les regarde du dessus, et là soit on ne les voit presque plus, soit il y a un quad horizontal en plus pour masquer le problème.

Note que même sans que les vecteurs soient exactement colinéaires, au voisinage de cette situation le billboard va avoir tendance à se comporter de façon pas très esthétique. Tu as déjà remarqué dans certaines démos comme la caméra se retourne d'un coup en passant au dessus ou au dessous d'un objet ? C'est exactement le même problème.

Si N est l'axe de la caméra (je suppose, mais ça pourrait être autre chose), en choisissant le Y de la caméra le problème n'existe plus. En contre partie le billboard va tourner avec elle, ce qui n'est pas toujours souhaitable. Ça marche pour des particules dont on ne distingue pas l'orientation, par contre pour un nuage c'est une catastrophe.

En résumé, est-ce que tu veux :
A - Un billboard qui fait face à la caméra et reste aligné avec l'écran ? (texte, élément du HUD) ?
B - Un billboard qui fait face à la caméra mais a son orientation propre ? (fumée, éclaboussures)
C - Un billboard qui est aligné avec autre chose ? (arbre, rayon laser, ligne téléphonique...)

Dans les cas B ou C, est-ce que ça a du sens d'avoir la caméra alignée avec l'axe ?
Si oui que veux tu dans cette situation ?

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #4 on: 05 June 2013 à 14:00:23 »
Ben en fait, mon quad c'est pas forcément un billboard. C'est un quad que je veux pouvoir orienter dans n'importe quel sens.
C'est sûr que s'il est aligné avec la caméra, c'est un billboard.
Mais par exemple si je veux créer un objet à partir plusieurs quads, orientés de telle sorte qu'au final ça fasse quelque chose de cohérent, c'est ce cas là qui m'intéresse...

Offline Zavie

Re : Orienter un quad selon un vecteur
« Reply #5 on: 05 June 2013 à 14:37:13 »
Si je comprends bien ce serait le cas B.
Ayant N il te reste à choisir arbitrairement un Y. Ça peut être celui du monde ou celui de l'objet par exemple. Et pour le cas dégénéré N = aY, il te faut choisir un autre Y, par exemple le dernier qui fonctionnait, ou vivre avec le fait que le quad disparaît dans cette situation.

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #6 on: 05 June 2013 à 14:57:22 »
En fait je crois que je suis un peu con : si N est colinéaire avec un des axes du repère du monde... Alors les deux autres axes qui feront le repère de mon quad, sont les deux autres axes du monde, tout simplement. Je n'ai que 3 cas (x2 en fait, si N=-X par exemple) à gérer.

Merci bien en tout cas pour ton aide !

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #7 on: 05 June 2013 à 21:19:17 »
C'est exactement ce qu'on avait pour l'objet qui se décompose en particules : chaque particule était initialement mappée sur l'objet mais pour pas avoir le problème que tu as, je passais la normale ET la tangente (ce qui a du sens sur un objet mappé qui possède tangent et bitangente).

Est-ce que ça a du sens de n'avoir que la normale ? Ca risque de donner des quads un peu bizarres et parfois pas forcément super cohérents, surtout quand effectivement ta normale commence à se retrouver alignée avec le Y...

Bref, est-ce que tu as envisagé de filer également une tangent à ton quad ?
.  Pom  .

Offline phaazon

Re : Orienter un quad selon un vecteur
« Reply #8 on: 05 June 2013 à 23:03:32 »
Je pense que si tu veux avoir des quads en passant par un changement de repère, il te faut au moins une normale n et un vecteur u tels que u · n = 0 et u × n = v avec v dans le plan du quad :)

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #9 on: 06 June 2013 à 00:56:44 »
En fait, je pensais pouvoir déduire le vecteur tangent de la normale et du repère monde.

Je m'explique par des cas particuliers. Si la normale est colinéaire à l'axe X, alors le vecteur tangent sera -Z (je suis dans un repère OpenGL). Si la normale est oblique avec un z positif et qu'elle est dans le plan ZY, alors le vecteur tangent sera X... etc.

Du coup les quads que je veux faire seraient définis par leur normale, et n'auraient pas de rotation par rapport à l'axe représenté par cette normale.

Mais là je pense que j'ai toutes les infos pour résoudre ce problème. Cela dit effectivement, si je donne le vecteur tangent avec la normale, alors le problème devient trivial en fait.

Offline Ziple

  • Base
    • View Profile
  • Ancienneté: 2013
  • Ville: Paris
Re : Orienter un quad selon un vecteur
« Reply #10 on: 10 June 2013 à 11:05:04 »
On peut utiliser les coordonnées sphériques pour ça!
« Last Edit: 12 June 2013 à 20:57:22 by Ziple »

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #11 on: 10 June 2013 à 11:15:17 »
Pour info j'ai résolu mon problème, je posterai mon code ce soir, si ça intéresse quelqu'un... C'est certainement pas optimal, mais en tout cas je m'y retrouve.

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #12 on: 10 June 2013 à 19:15:59 »
Comme promis, voici mon code, qui marche ! C'est cracra et sans commentaire, mais ça fait ce que je veux :)

Code: [Select]
Mesh* MeshUtils::BuildPlane(fmatrix::Vec3 normal, fmatrix::Vec2 size, uint32 subdiv, bool triangles, bool dynamic)
{
Mesh* result = new Mesh(triangles ? Mesh::TRIANGLE : Mesh::QUAD, dynamic);

bool axisFound = false;
normal.normalize();
fmatrix::Vec3 up = fmatrix::Vec3::unitY;
fmatrix::Vec3 right = fmatrix::Vec3::unitX;

if(normal.colinear(fmatrix::Vec3::unitY, 0.0001f))
{
up = fmatrix::Vec3::unitX;
right = fmatrix::Vec3::unitZ;
if(normal.mY < 0)
{
up = -up;
right = -right;
}
axisFound = true;
}
if(normal.colinear(fmatrix::Vec3::unitX, 0.0001f))
{
up = fmatrix::Vec3::unitY;
right = -fmatrix::Vec3::unitZ;
if(normal.mX < 0)
{
up = -up;
right = -right;
}
axisFound = true;
}
if(normal.colinear(fmatrix::Vec3::unitZ, 0.0001f))
{
up = fmatrix::Vec3::unitY;
right = fmatrix::Vec3::unitX;
if(normal.mZ < 0)
{
up = -up;
right = -right;
}
axisFound = true;
}

if(!axisFound)
{
if((FABS(normal.mX) > FABS(normal.mY)) && (FABS(normal.mX) > FABS(normal.mZ)))
{
up = fmatrix::Vec3::unitY;
right = -fmatrix::Vec3::unitZ;
if(normal.mX < 0)
{
up = -up;
right = -right;
}
}
else if((FABS(normal.mY) > FABS(normal.mX)) && (FABS(normal.mY) > FABS(normal.mZ)))
{
up = fmatrix::Vec3::unitY;
right = -fmatrix::Vec3::unitZ;
if(normal.mY < 0)
{
up = -up;
right = -right;
}
}
else if((FABS(normal.mZ) > FABS(normal.mX)) && (FABS(normal.mZ) > FABS(normal.mY)))
{
up = fmatrix::Vec3::unitY;
right = fmatrix::Vec3::unitX;
if(normal.mZ < 0)
{
up = -up;
right = -right;
}
}
}

up = normal.cross(right);
right = up.cross(normal);
up.normalize();
right.normalize();

fmatrix::Vec3 upleft, downright;
upleft = (up*size.mY - right*size.mX)/2.0f;
downright = -upleft;

uint32 nbVerticesPerSide = uint32(msys_powf(2.0f, (float)subdiv)) + 1;
fmatrix::Vec3 rightinc = right * size.mX / (float)(nbVerticesPerSide - 1);
fmatrix::Vec3 downinc = -up * size.mY / (float)(nbVerticesPerSide - 1);

float uinc = 1.0f / (float)(nbVerticesPerSide - 1);
float vinc = 1.0f / (float)(nbVerticesPerSide - 1);
float u = 0.0f;
fmatrix::Vec3 ptmp = upleft;
for(uint32 ix = 0; ix < nbVerticesPerSide; ix++)
{
float v = 1.0f;
ptmp = upleft + rightinc * (float)ix;
for(uint32 iz = 0; iz < nbVerticesPerSide; iz++)
{
fmatrix::Vec3 p(ptmp);
fmatrix::Vec3 n(normal);
fmatrix::Vec3 t(u, v, 0.0f);

result->AddVertex(Vertex(p, n, t));

v -= vinc;
ptmp = ptmp + downinc;
}

u += uinc;
}

for(uint32 ix = 0; ix < nbVerticesPerSide - 1; ix++)
{
for(uint32 iz = 0; iz < nbVerticesPerSide - 1; iz++)
{
uint32 p0 = ix + iz * nbVerticesPerSide;
uint32 p1 = p0 + 1;
uint32 p2 = p1 + nbVerticesPerSide;
uint32 p3 = p2 - 1;

if(triangles)
{
result->AddIndex(p0);
result->AddIndex(p1);
result->AddIndex(p2);

result->AddIndex(p0);
result->AddIndex(p2);
result->AddIndex(p3);
}
else // QUAD
{
result->AddIndex(p0);
result->AddIndex(p1);
result->AddIndex(p2);
result->AddIndex(p3);
}
}
}

return result;
}

Offline phaazon

Re : Orienter un quad selon un vecteur
« Reply #13 on: 11 June 2013 à 10:01:28 »
Je trouve ça très long pour ce que ça fait, non ? En tout cas si ça marche c’est déjà ça :)

Offline flure

  • Base
    • Pouet.net
    • View Profile
  • Ancienneté: 1998
  • Groupe: PoPsY TeAm
  • Rôle: Codeur Linux
  • Ville: Lyon
Re : Orienter un quad selon un vecteur
« Reply #14 on: 11 June 2013 à 10:32:16 »
C'est sur que c'est pas optimal et un peu long ! Par contre c'est générique... Et si quelqu'un a des idées pour optimiser / améliorer ça, bien évidemment je suis preneur :)