GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetIntegerv(GL_VIEWPORT,viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
winX = (float)x;
winY = viewport[3] - (float)y;
glReadPixels((int)winX, (int)winY, 1, 1, GL_DEPTH_COMPONENT,GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, modelview, projection, viewport,&posX, &posY,&posZ);
注:(x, y)是屏幕座標,(winX,winY, winZ)是視景體座標及深度座標,(posX, posY, posZ是OpenGL座標。
上述代碼並不保險,只針對一種特殊情況纔好使,即glViewport(0, 0,screenWidth,screenHeight),screenWidth、screenHeight分別是客戶區的寬和高,視口左下角座標恰好是(0,0),並且未經過任何模型變換。
從屏幕座標向OpenGL座標要經過兩步,第一步是屏幕座標向視景體座標轉換,第二步是視景體座標向OpenGL座標轉換。上述代碼中winX= (float)x; winY = viewport[3] -(float)y;反映第一步,gluUnProject是第二步。一般說來,gluUnProject的轉換是不會出問題的。
如何進行正確的轉換呢?首先,在glGetIntegerv之前添上模型變換的代碼,和繪圖時使用的模型變換代碼一樣,另外必須保證平移,縮放,旋轉的順序和繪圖時的一樣。其次,屏幕座標向視景體座標轉換有兩種方式(注意!在多視口情況下,活動視應當最後繪製,它將作爲當前的視口,保證glGetIntegerv等取值函數能得到正確的值)。①winx= x – viewport[0]; winy = screenHeight – viewport[1] - y;viewport[0] = viewport[1] = 0;②winx = x; winy = screenHeight –y;第一種比較直觀,前兩句是將屏幕座標轉換爲視景體內的座標,後兩句將視景體的左下角點座標改爲(0,0),因爲在設置裁剪視口時,使用glViewport設置視口的左下角點座標不一定是(0,0),而在視景體內的點其視景體座標與左下角點是相對的,即把視景體座標看作是座標系原點。第二種方式比較簡略,但是同樣的道理,只不過是glUnproject函數對winx和winy又做了一次轉換。
代碼2:
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glPushMatrix();
// 變換要繪圖函數裏的順序一樣,否則座標轉換會產生錯誤
glScalef(); // 縮放、平移、旋轉變換
glRotatef();
glTranslatef();
…………………
glGetIntegerv(GL_VIEWPORT, viewport);// 得到的是最後一個設置視口的參數
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glPopMatrix();
winX = x;
winY = screenHeight - y;
glReadPixels((int)winX, (int)winY, 1, 1, GL_DEPTH_COMPONENT,GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, modelview, projection, viewport,&posX, &posY,&posZ);
以上是通用方法,但是也有人在轉換的過程中碰到viewport,modelview和projection沒有值的情況,爲什麼呢?
我就碰到這種情況,我是這樣解決的。原因是我在代碼中多次使用wglMakeCurrent()這個函數,注意:只需要在初始化(initial或create)調用一次wglMakeCurrent(m_hDC,m_hRC),然後在釋放資源,如Destroy(MFC中)調用wglMakeCurrent(NULL,NULL)即可。
*注*:上述代碼源自網上!非本人原創。