OpenGL中屏幕座標轉爲OpenGL座標

  在opengl中我們經常碰到需要把屏幕座標轉換成opengl座標,例如我們做拾取,旋轉,平移等操作。網上有很多相關代碼:
   原理:
  鼠標點擊屏幕中的某點,然後獲取該點屏幕座標對應的OpenGL座標。鼠標點的座標很容易獲得,直接從鼠標點擊消息中獲取就可以,但是OpenGL座標卻需要做一定計算轉換。需要用到的核心OpenGL函數是gluUnProject()
   代碼1:

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)即可

*注*:上述代碼源自網上!非本人原創。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章