2011-01-20 2 views
2

Je travaille sur ce sujet depuis plusieurs jours et j'ai été bloqué. Je dois être en mesure de toucher l'écran et de renvoyer les coordonnées x, y, z du point de mon modèle le plus proche du plan proche qui coupe le rayon généré au point de sélection. Je pense qu'une partie de mon problème est que je fais un tas de transformations matricielles et de rotations à travers le code de rendu pour mon modèle, bien que la géométrie qui m'intéresse soit rendue à un état de transformation spécifique. Mon code que j'utilise est ci-dessous. Si quelqu'un peut m'aider à comprendre comment cela fonctionne, ce serait génial. checkCollision() est alimenté par le point sur lequel l'utilisateur clique, et gluUnProject() est supposé transformer mon point de prélèvement 2D en coordonnées 3D sur mes plans proches et lointains, 0 étant le plan proche et 1 étant le plan lointain. Mon utilisation est ici et est appelé juste avant la géométrie est rendue, de sorte que tous les transformations ont déjà été appliquées:Carrefour d'intersection Ray-Triangle ne fonctionnant pas

[self checkCollision:touchPoint panVector:panVec]; 

Ce code ci-dessous est le code de vérification de collision:

-(Boolean) checkCollision:(CGPoint)winPos panVector:(Vector3f*)panVec 
{ 
glGetIntegerv(GL_VIEWPORT, viewport); 

winPos.y = (float)viewport[3] - winPos.y; 

Vector3f nearPoint; 
Vector3f farPoint; 

glGetFloatv(GL_PROJECTION_MATRIX, projection); 
glGetFloatv(GL_MODELVIEW_MATRIX, modelview); 

//Retreiving position projected on near plane 
gluUnProject(winPos.x, winPos.y , 0, modelview, projection, viewport, &nearPoint.x, &nearPoint.y, &nearPoint.z); 

//Retreiving position projected on far plane 
gluUnProject(winPos.x, winPos.y, 1, modelview, projection, viewport, &farPoint.x, &farPoint.y, &farPoint.z); 

Vector3f *near = [[Vector3f alloc] initWithFloatsX:nearPoint.x Y:nearPoint.y Z:nearPoint.z]; 
Vector3f *far = [[Vector3f alloc] initWithFloatsX:farPoint.x Y:farPoint.y Z:farPoint.z]; 
Vector3f *d = [Vector3f subtractV1:far minusV2:near]; 

Vector3f *v0 = [[Vector3f alloc] init]; 
Vector3f *v1 = [[Vector3f alloc] init]; 
Vector3f *v2 = [[Vector3f alloc] init]; 
Vector3f *e1; // = [[Vector3f alloc] init]; 
Vector3f *e2; // = [[Vector3f alloc] init]; 

for (int i = 0; i < assemblyObj->numObjects; i++) { 
    for (int j = 0; j < assemblyObj->partList[i].numVertices; j+=18) { 
     v0.x = assemblyObj->partList[i].vertices[j+0]; 
     v0.y = assemblyObj->partList[i].vertices[j+1]; 
     v0.z = assemblyObj->partList[i].vertices[j+2]; 

     v1.x = assemblyObj->partList[i].vertices[j+6]; 
     v1.y = assemblyObj->partList[i].vertices[j+7]; 
     v1.z = assemblyObj->partList[i].vertices[j+8]; 

     v2.x = assemblyObj->partList[i].vertices[j+12]; 
     v2.y = assemblyObj->partList[i].vertices[j+13]; 
     v2.z = assemblyObj->partList[i].vertices[j+14]; 

     e1 = [Vector3f subtractV1:v1 minusV2:v0]; 
     e2 = [Vector3f subtractV1:v2 minusV2:v0]; 

     Vector3f *p = [[Vector3f alloc] init]; 
     [Vector3f cross:p V1:d V2:e2]; 
     float a = [Vector3f dot:e1 V2:p]; 
     if (a > -.000001 && a < .000001) { 
      continue; 
     } 

     float f = 1/a; 
     Vector3f *s = [Vector3f subtractV1:near minusV2:v0]; 
     float u = f*([Vector3f dot:s V2:p]); 
     if (u<0 || u>1) { 
      continue; 
     } 
     Vector3f *q = [[Vector3f alloc] init]; 
     [Vector3f cross:q V1:s V2:e1]; 
     float v = f*([Vector3f dot:d V2:q]); 
     if (v<0 || (u+v)>1) { 
      continue; 
     } 
     //NSLog(@"hit polygon"); 
     return true; 
    } 
} 

//NSLog(@"didn't hit polygon"); 
return FALSE; 
} 


GLint gluUnProject(GLfloat winx, GLfloat winy, GLfloat winz, 
     const GLfloat model[16], const GLfloat proj[16], 
     const GLint viewport[4], 
     GLfloat * objx, GLfloat * objy, GLfloat * objz) 
{ 
/* matrice de transformation */ 
GLfloat m[16], A[16]; 
GLfloat in[4], out[4]; 

/* transformation coordonnees normalisees entre -1 et 1 */ 
in[0] = (winx - viewport[0]) * 2/viewport[2] - 1.f; 
in[1] = (winy - viewport[1]) * 2/viewport[3] - 1.f; 
in[2] = 2 * winz - 1.f; 
in[3] = 1.f; 

/* calcul transformation inverse */ 
matmul(A, proj, model); 
invert_matrix(A, m); 

/* d'ou les coordonnees objets */ 
transform_point(out, m, in); 
if (out[3] == 0.f) 
    return GL_FALSE; 
*objx = out[0]/out[3]; 
*objy = out[1]/out[3]; 
*objz = out[2]/out[3]; 
return GL_TRUE; 
} 


void transform_point(GLfloat out[4], const GLfloat m[16], const GLfloat in[4]) 
{ 
#define M(row,col) m[col*4+row] 
out[0] = 
M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3]; 
out[1] = 
M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3]; 
out[2] = 
M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3]; 
out[3] = 
M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3]; 
#undef M 
} 

void matmul(GLfloat * product, const GLfloat * a, const GLfloat * b) 
{ 
/* This matmul was contributed by Thomas Malik */ 
GLfloat temp[16]; 
GLint i; 

#define A(row,col) a[(col<<2)+row] 
#define B(row,col) b[(col<<2)+row] 
#define T(row,col) temp[(col<<2)+row] 

/* i-te Zeile */ 
for (i = 0; i < 4; i++) { 
    T(i, 0) = 
    A(i, 0) * B(0, 0) + A(i, 1) * B(1, 0) + A(i, 2) * B(2, 0) + A(i, 
                    3) * 
    B(3, 0); 
    T(i, 1) = 
    A(i, 0) * B(0, 1) + A(i, 1) * B(1, 1) + A(i, 2) * B(2, 1) + A(i, 
                    3) * 
    B(3, 1); 
    T(i, 2) = 
    A(i, 0) * B(0, 2) + A(i, 1) * B(1, 2) + A(i, 2) * B(2, 2) + A(i, 
                    3) * 
    B(3, 2); 
    T(i, 3) = 
    A(i, 0) * B(0, 3) + A(i, 1) * B(1, 3) + A(i, 2) * B(2, 3) + A(i, 
                    3) * 
    B(3, 3); 
} 

#undef A 
#undef B 
#undef T 
memcpy(product, temp, 16 * sizeof(GLfloat)); 
} 

int invert_matrix(const GLfloat * m, GLfloat * out) 
{ 
/* NB. OpenGL Matrices are COLUMN major. */ 
#define SWAP_ROWS(a, b) { GLfloat *_tmp = a; (a)=(b); (b)=_tmp; } 
#define MAT(m,r,c) (m)[(c)*4+(r)] 

GLfloat wtmp[4][8]; 
GLfloat m0, m1, m2, m3, s; 
GLfloat *r0, *r1, *r2, *r3; 

r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; 

r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1), 
r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3), 
r0[4] = 1.f, r0[5] = r0[6] = r0[7] = 0.f, 
r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1), 
r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3), 
r1[5] = 1.f, r1[4] = r1[6] = r1[7] = 0.f, 
r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1), 
r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3), 
r2[6] = 1.f, r2[4] = r2[5] = r2[7] = 0.f, 
r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1), 
r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3), 
r3[7] = 1.f, r3[4] = r3[5] = r3[6] = 0.f; 

/* choose pivot - or die */ 
if (fabsf(r3[0]) > fabsf(r2[0])) 
    SWAP_ROWS(r3, r2); 
if (fabsf(r2[0]) > fabsf(r1[0])) 
    SWAP_ROWS(r2, r1); 
if (fabsf(r1[0]) > fabsf(r0[0])) 
    SWAP_ROWS(r1, r0); 
if (0.f == r0[0]) 
    return GL_FALSE; 

/* eliminate first variable  */ 
m1 = r1[0]/r0[0]; 
m2 = r2[0]/r0[0]; 
m3 = r3[0]/r0[0]; 
s = r0[1]; 
r1[1] -= m1 * s; 
r2[1] -= m2 * s; 
r3[1] -= m3 * s; 
s = r0[2]; 
r1[2] -= m1 * s; 
r2[2] -= m2 * s; 
r3[2] -= m3 * s; 
s = r0[3]; 
r1[3] -= m1 * s; 
r2[3] -= m2 * s; 
r3[3] -= m3 * s; 
s = r0[4]; 
if (s != 0.f) { 
    r1[4] -= m1 * s; 
    r2[4] -= m2 * s; 
    r3[4] -= m3 * s; 
} 
s = r0[5]; 
if (s != 0.f) { 
    r1[5] -= m1 * s; 
    r2[5] -= m2 * s; 
    r3[5] -= m3 * s; 
} 
s = r0[6]; 
if (s != 0.f) { 
    r1[6] -= m1 * s; 
    r2[6] -= m2 * s; 
    r3[6] -= m3 * s; 
} 
s = r0[7]; 
if (s != 0.f) { 
    r1[7] -= m1 * s; 
    r2[7] -= m2 * s; 
    r3[7] -= m3 * s; 
} 

/* choose pivot - or die */ 
if (fabsf(r3[1]) > fabsf(r2[1])) 
    SWAP_ROWS(r3, r2); 
if (fabsf(r2[1]) > fabsf(r1[1])) 
    SWAP_ROWS(r2, r1); 
if (0.f == r1[1]) 
    return GL_FALSE; 

/* eliminate second variable */ 
m2 = r2[1]/r1[1]; 
m3 = r3[1]/r1[1]; 
r2[2] -= m2 * r1[2]; 
r3[2] -= m3 * r1[2]; 
r2[3] -= m2 * r1[3]; 
r3[3] -= m3 * r1[3]; 
s = r1[4]; 
if (0.f != s) { 
    r2[4] -= m2 * s; 
    r3[4] -= m3 * s; 
} 
s = r1[5]; 
if (0.f != s) { 
    r2[5] -= m2 * s; 
    r3[5] -= m3 * s; 
} 
s = r1[6]; 
if (0.f != s) { 
    r2[6] -= m2 * s; 
    r3[6] -= m3 * s; 
} 
s = r1[7]; 
if (0.f != s) { 
    r2[7] -= m2 * s; 
    r3[7] -= m3 * s; 
} 

/* choose pivot - or die */ 
if (fabs(r3[2]) > fabs(r2[2])) 
    SWAP_ROWS(r3, r2); 
if (0.f == r2[2]) 
    return GL_FALSE; 

/* eliminate third variable */ 
m3 = r3[2]/r2[2]; 
r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], 
r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; 

/* last check */ 
if (0.f == r3[3]) 
    return GL_FALSE; 

s = 1.f/r3[3];  /* now back substitute row 3 */ 
r3[4] *= s; 
r3[5] *= s; 
r3[6] *= s; 
r3[7] *= s; 

m2 = r2[3];   /* now back substitute row 2 */ 
s = 1.f/r2[2]; 
r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), 
r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); 
m1 = r1[3]; 
r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, 
r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; 
m0 = r0[3]; 
r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, 
r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; 

m1 = r1[2];   /* now back substitute row 1 */ 
s = 1.f/r1[1]; 
r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), 
r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); 
m0 = r0[2]; 
r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, 
r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; 

m0 = r0[1];   /* now back substitute row 0 */ 
s = 1.f/r0[0]; 
r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), 
r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); 

MAT(out, 0, 0) = r0[4]; 
MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6]; 
MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4]; 
MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6]; 
MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4]; 
MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6]; 
MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4]; 
MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6]; 
MAT(out, 3, 3) = r3[7]; 

return GL_TRUE; 

#undef MAT 
#undef SWAP_ROWS 
} 

Edit:

J'ai suivi la suggestion de Justin Meiners de montrer des points pour montrer où mon rayon de prise est généré et je peux voir ce qui se passe maintenant, mais je ne sais pas pourquoi. Ma scène implémente la rotation d'arcball, le zoom et le panoramique à travers les quaternions. Je vais énoncer à peu près ce que ma scène est en train de faire, alors ce qui se passe avec mon rayon.

D'abord, configurer ma fenêtre:

glViewport(0, 0, scene.width, scene.height); 
glOrthof(-11.25, 11.25, -14.355, 14.355, -1000, 1000); 

Ensuite, je prends la matrice de l'élément 16 que j'utilise dans le cadre de ma méthode arcball pour naviguer ma scène et multiplier ma matrice modelview par celle-ci:

float mat[16]; 
[arcball get_Renamed:mat]; 
glMultMatrixf(mat); 

maintenant, je fais mon choix ray:

glGetIntegerv(GL_VIEWPORT, viewport); 
glGetFloatv(GL_PROJECTION_MATRIX, projection); 
glGetFloatv(GL_MODELVIEW_MATRIX, modelview); 

touchPoint.y = (float)viewport[3] - touchPoint.y; 

Vector3f nearPoint, farPoint; 

//Retreiving position projected on near plane 
gluUnProject(touchPoint.x, touchPoint.y , 0, modelview, projection, viewport, &nearPoint.x, &nearPoint.y, &nearPoint.z); 

//Retreiving position projected on far plane 
gluUnProject(touchPoint.x, touchPoint.y, 1, modelview, projection, viewport, &farPoint.x, &farPoint.y, &farPoint.z); 

float coords[3] = {nearPoint.x, nearPoint.y, nearPoint.z}; 
float coords2[3] = {farPoint.x, farPoint.y, farPoint.z}; 

glPointSize(100); 
glColor4f(1, 0, 0, 1); 
glEnableClientState(GL_VERTEX_ARRAY); 
glVertexPointer(3, GL_FLOAT, sizeof(coords[0])*3, coords); 
glDrawArrays(GL_POINTS, 0, 1); 

glPointSize(150); 
glColor4f(0, 0, 1, 1); 
glEnableClientState(GL_VERTEX_ARRAY); 
glVertexPointer(3, GL_FLOAT, sizeof(coords2[0])*3, coords2); 
glDrawArrays(GL_POINTS, 0, 1); 
glDisableClientState(GL_VERTEX_ARRAY); 

je fais cela, et il fonctionne très bien avant que je fais tourner ma scène , mais dès que je commence à tourner ma scène, le point lointain commence à bouger. Si je fais pivoter la scène d'exactement 180 degrés, le point éloigné est de retour au point le plus proche. Une idée de ce qui se passe? L'arcball est basé sur l'algorithme de Ken Shoemake.

Répondre

1

J'ai finalement compris ce que je faisais mal. Vous devez obtenir l'état de GL_VIEWPORT et GL_PROJECTION_MATRIX juste après leur création, ce qui est juste après les Si vous faites cela plus tard, alors vos transformations de matrice

glMatrixMode(GL_PROJECTION); 
glLoadIdentity(); 

appels. affectent votre une viewport d les matrices de projection. Il ne semble pas qu'ils devraient, mais ils le font.La seule matrice dont vous voulez obtenir l'état lorsque vous effectuez votre sélection est la matrice modelview, et vous voulez le faire lorsque votre modelview a les mêmes transformations que la géométrie sur laquelle vous essayez d'exécuter gluUnProject. Une fois que j'ai compris cela, l'algorithme de sélection de rayons a très bien fonctionné. Sur l'iPad, il peut effectuer une intersection de rayons triangulaires sur 15 000 triangles en 19 ms, ce qui équivaut à environ 800 000 intersections par seconde. Pas si mal pour un iPad, même si je suis sûr que la performance pourrait être améliorée en précalculant les équations du triangle. Merci pour les suggestions, ils m'ont aidé à comprendre ce qui se passait afin que je puisse le réparer.

3

Vous devez dessiner votre rayon de souris avec GL_LINES pour vous assurer qu'il est correct. Cela va vous sauver la vie et vous pouvez tweek matrices et trucs pour essayer de faire les choses correctement. Aussi, si vous faites beaucoup de transformations, alors vous devez appeler glUnProject à l'intérieur de ceux-ci afin qu'il en tienne compte. Vous devrez peut-être enregistrer la position de la souris jusqu'à ce que vous puissiez l'annuler dans la prochaine boucle de rendu. EG

glPushMatrix(); 

// Rotate world 
glRotate(...) 

// Mouse glGetIntv, and glGetFloatV here 

drawObject() 

Code exemple de dessin (donner ce vecteur est juste un struct avec float x, y et composants z.

glEnableClientState(GL_VERTEX_ARRAY); 
    glVertexPointer(3, GL_FLOAT, sizeof(Vector), points); 

    glDrawArrays(GL_POINTS, 0, 2); 

    glDisableClientState(GL_VERTEX_ARRAY); 
+0

Comment dessinez-vous votre rayon de souris en utilisant GL_LINES? J'essayais de le faire, mais je n'arrivais pas à comprendre comment. C'est OpenGL-ES dont vous parlez, n'est-ce pas? En outre, où dois-je effectuer glGetIntegerv et glGetFloatv pour obtenir les valeurs de matrice correctes pour viewport, projection et modelview? – Davido

+0

Il est difficile à décrire, mais il doit être dans la boucle de rendu où vous commencez à rendre les choses. Ainsi, toutes les transformations de scène et de projection sont appliquées lorsque vous l'obtenez. –

+0

Je l'ai édité pour ajouter le code de dessin –