2010-01-19 11 views
30

Ho un renderer che utilizza directx e openGL e una scena 3D. Il viewport e la finestra hanno le stesse dimensioni.Implementazione di Ray Picking

Come si implementa il prelievo delle coordinate del mouse xey in una piattaforma indipendente?

risposta

27

Se è possibile, effettuare il prelievo sulla CPU calcolando un raggio dall'occhio attraverso il puntatore del mouse e intersecandolo con i modelli.

Se questa non è un'opzione, andrei con qualche tipo di rendering ID. Assegna a ciascun oggetto che desideri selezionare un colore unico, renderizza gli oggetti con questi colori e infine leggi il colore dal framebuffer sotto il puntatore del mouse.

EDIT: Se la domanda è come costruire il raggio dalle coordinate mouse è necessario quanto segue: una matrice di proiezione P e la fotocamera trasformare C. Se le coordinate del puntatore del mouse è (x, y) e la dimensione della finestra è (larghezza, altezza) una posizione nello spazio clip lungo il raggio è:

mouse_clip = [ 
    float(x) * 2/float(width) - 1, 
    1 - float(y) * 2/float(height), 
    0, 
    1] 

(Si noti che capovolto l'asse y in quanto spesso l'origine delle coordinate del mouse sono in alto a sinistra)

il seguente è anche vero:

mouse_clip = P * C * mouse_worldspace 

che assicura:

mouse_worldspace = inverse(C) * inverse(P) * mouse_clip 

Ora abbiamo:

p = C.position(); //origin of camera in worldspace 
n = normalize(mouse_worldspace - p); //unit vector from p through mouse pos in worldspace 
+0

Lo so! la domanda è come !!!! –

+3

@Tom Questo non era completamente chiaro dalla domanda. Ad ogni modo, ho modificato la mia risposta, spero che sia di aiuto. –

+0

Vale la pena notare che se si utilizzano matrici simili a DirectX, l'ordine di moltiplicazione viene invertito. – Goz

1

Beh, abbastanza semplice, la teoria alla base di questo è sempre lo stesso

1) Unproject due volte coordinare il vostro 2D sullo spazio 3D. (ogni API ha una sua funzione, ma puoi implementarla tu se lo desideri). Uno a Min Z, uno alla Max Z.

2) Con questi due valori calcolano il vettore che va dal minimo Z e scegliere Max Z.

3) Con il vettore e un punto di calcolare il raggio che va da Min Z alla MaxZ

4) Ora si ha un raggio, con questo si può fare un raggio-triangolo/ray-plane/ray-qualcosa incrocio e ottenere il risultato ...

1

ho poco Esperienza DirectX, ma sono sicuro che è simile a OpenGL. Quello che vuoi è la chiamata gluUnproject.

Supponendo di avere un buffer Z valida è possibile interrogare il contenuto del buffer di Z in una posizione del mouse con:

// obtain the viewport, modelview matrix and projection matrix 
// you may keep the viewport and projection matrices throughout the program if you don't change them 
GLint viewport[4]; 
GLdouble modelview[16]; 
GLdouble projection[16]; 
glGetIntegerv(GL_VIEWPORT, viewport); 
glGetDoublev(GL_MODELVIEW_MATRIX, modelview); 
glGetDoublev(GL_PROJECTION_MATRIX, projection); 

// obtain the Z position (not world coordinates but in range 0 - 1) 
GLfloat z_cursor; 
glReadPixels(x_cursor, y_cursor, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_cursor); 

// obtain the world coordinates 
GLdouble x, y, z; 
gluUnProject(x_cursor, y_cursor, z_cursor, modelview, projection, viewport, &x, &y, &z); 

se non si desidera utilizzare Glu è anche possibile implementare il gluUnProject si potrebbe anche implementare da soli, è la funzionalità è relativamente semplice ed è descritta in opengl.org

+0

grazie, anche se Id aveva bisogno di un metodo agnostico API era possibile –

+0

@Tom come ho detto, se non vuoi usare la funzione glu puoi semplicemente implementare la funzionalità di te stesso, tutto ciò che ti servirà è ottenere la modelview e la proiezione matrici per ciascuna e ottengono la posizione della finestra z per ciascuna. – wich

+0

che mi richiederebbe di capire lo stesso xyz, potrei quindi postare xyz e segnare che come risposta invece di questa, vedi il mio ragionamento? Qualcun altro ha comunque pubblicato la matematica –

20

Ecco il tronco di visualizzazione:

viewing frustum

In primo luogo è necessario determinare dove sul nearplane clic del mouse è accaduto:

  1. riscalare le coordinate della finestra (0..640,0..480) per [-1,1], con (-1 , -1) nell'angolo in basso a sinistra e (1,1) in alto a destra.
  2. 'annulla' la proiezione moltiplicando le coordinate in scala con quella che io chiamo la matrice 'unview': unview = (P * M).inverse() = M.inverse() * P.inverse(), dove M è la matrice ModelView e P è la matrice di proiezione.

Quindi determinare dove si trova la telecamera nello spazio del mondo e disegnare un raggio che inizia dalla fotocamera e passa attraverso il punto trovato sul piano vicino.

La telecamera è a M.inverse().col(4), ovvero la colonna finale della matrice ModelView inversa.

pseudocodice finale:

normalised_x = 2 * mouse_x/win_width - 1 
normalised_y = 1 - 2 * mouse_y/win_height 
// note the y pos is inverted, so +y is at the top of the screen 

unviewMat = (projectionMat * modelViewMat).inverse() 

near_point = unviewMat * Vec(normalised_x, normalised_y, 0, 1) 
camera_pos = ray_origin = modelViewMat.inverse().col(4) 
ray_dir = near_point - camera_pos 
+0

che cos'è la matrice "modelView" a cui fai riferimento? È questa la combinazione della matrice modelToWorld del modello che stiamo cercando di colpire e della viewMatrix della fotocamera? – Jubei

+1

È passato un po 'di tempo da quando ho scritto questo, ma penso che sia la matrice che trasforma le coordinate del mondo in coordinate della telecamera. Se c'è una linea nel vertex shader come 'gl_Position = proiezione * modelView * vertexPos;', è il bit nel mezzo, dove la matrice 'projection' è la traduzione dalla telecamera alle coordinate del viewport. HTH: / – nornagon

0

Ok, questo argomento è vecchio ma è stato il migliore che ho trovato sul tema, e mi ha aiutato un po ', quindi mi post qui per coloro che sono stanno seguendo ;-)

Questo è il modo ho preso a lavorare senza dover calcolare l'inversa della matrice di proiezione:

void Application::leftButtonPress(u32 x, u32 y){ 
    GL::Viewport vp = GL::getViewport(); // just a call to glGet GL_VIEWPORT 
vec3f p = vec3f::from(      
     ((float)(vp.width - x)/(float)vp.width), 
     ((float)y/(float)vp.height), 
      1.); 
    // alternatively vec3f p = vec3f::from(      
    //  ((float)x/(float)vp.width), 
    //  ((float)(vp.height - y)/(float)vp.height), 
    //  1.); 

    p *= vec3f::from(APP_FRUSTUM_WIDTH, APP_FRUSTUM_HEIGHT, 1.); 
    p += vec3f::from(APP_FRUSTUM_LEFT, APP_FRUSTUM_BOTTOM, 0.); 

    // now p elements are in (-1, 1) 
    vec3f near = p * vec3f::from(APP_FRUSTUM_NEAR); 
    vec3f far = p * vec3f::from(APP_FRUSTUM_FAR); 

    // ray in world coordinates 
    Ray ray = { _camera->getPos(), -(_camera->getBasis() * (far - near).normalize()) }; 

    _ray->set(ray.origin, ray.dir, 10000.); // this is a debugging vertex array to see the Ray on screen 

    Node* node = _scene->collide(ray, Transform()); 
    cout << "node is : " << node << endl; 
} 

Questa assume una proiezione prospettica, ma il problema non si pone per th e uno ortografico in primo luogo.

0

Ho la stessa situazione con la normale raccolta di raggi, ma qualcosa non va. Ho eseguito l'operazione non progetto nel modo corretto, ma semplicemente non funziona. Penso di aver sbagliato, ma non riesco a capire dove. La mia moltiplicazione matix, l'inverso e il vettore delle moltiplicazioni di matix, tutte viste per funzionare bene, li ho testati. Nel mio codice sto reagendo su WM_LBUTTONDOWN. Quindi lParam restituisce le coordinate [Y] [X] come 2 parole in una dword. Li estraggo, quindi converto in spazio normalizzato, ho controllato che anche questa parte funzioni bene. Quando clicco sull'angolo in basso a sinistra - sto ottenendo valori vicini a -1 -1 e buoni valori per tutti e 3 gli altri angoli. Sto quindi usando l'array linepoins.vtx per il debug e non è nemmeno vicino alla realtà.

unsigned int x_coord=lParam&0x0000ffff; //X RAW COORD 
unsigned int y_coord=client_area.bottom-(lParam>>16); //Y RAW COORD 

double xn=((double)x_coord/client_area.right)*2-1; //X [-1 +1] 
double yn=1-((double)y_coord/client_area.bottom)*2;//Y [-1 +1] 

_declspec(align(16))gl_vec4 pt_eye(xn,yn,0.0,1.0); 
gl_mat4 view_matrix_inversed; 
gl_mat4 projection_matrix_inversed; 
cam.matrixProjection.inverse(&projection_matrix_inversed); 
cam.matrixView.inverse(&view_matrix_inversed); 

gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&projection_matrix_inversed); 
gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&view_matrix_inversed); 

line_points.vtx[line_points.count*4]=pt_eye.x-cam.pos.x; 
line_points.vtx[line_points.count*4+1]=pt_eye.y-cam.pos.y; 
line_points.vtx[line_points.count*4+2]=pt_eye.z-cam.pos.z; 
line_points.vtx[line_points.count*4+3]=1.0;