首先, 在開始前有些基本OpenGL功能必須要清楚, 像是一些基本的幾何知識,以及glFrustum的使用等等...
1.glFrustum
OpenGL API中的glFrustum函式主要負責投影矩陣的處理, 可由圖一看出Camera在3D空間中的投影成像狀況,可以看出是投影的成像是透過一個六面體 , glFrustum負責處理投影成像的矩陣運算,另外,OpenGL程式設計常使用到的gluPerspective是glFrustum的一種常用例子,其六面體是對稱的且前後投影面是平行的(如圖一), 沒有特別需求的話一般可以使用gluPerspective處理即可...
圖一,glFrustum Perspective View
詳細函式可以參考
http://www.opengl.org/sdk/docs/man/xhtml/glFrustum.xml
2.Stereo View的計算
詳細可以參考網址:
http://www.binocularity.org/page26.php
http://www.orthostereo.com/geometryopengl.html
左右眼在空間中的視角上視圖如圖二,實際單眼的視角在空間中會如圖一那樣,也就是說, 只要處理眼睛的移動以及投影的計算, 就可以產生出左右眼的影像.
圖二, 左右眼視角上視圖
實際實做需要注意的重點有幾個地方
.Frustum投影的計算
.平移3D環境的Camera
.重新計算Frustum投影
3.程式實做
從上面參考網址的程式碼來改, 基礎部分省略, 以下列出幾個重點部分
//這個是degrees to radians #define DTR 0.0174532925 struct camera { GLdouble leftfrustum; GLdouble rightfrustum; GLdouble bottomfrustum; GLdouble topfrustum; GLfloat modeltranslation; } leftCam, rightCam; double aspect = 1.0; double nearZ = 0.1; //near clipping plane //畫布(display plane)的z位置, Frustum效果平移的主要基準frame double screenZ = 600.0; //眼(鏡頭)相距距離, 處理時由原點各左右移IOD/2處理左右眼影像, 值需要依狀況做調整, //另外, 有paper說最佳值為width的5%或2.5%, 不過沒試過我也不知道效果 double IOD = 5;
以上參數有了, 再來是計算Frustum的參數, 注意雖然不做也沒關係, 不過依照比例下去計算各個投影參數, 出來的形狀比較不會變形太多, frustumshift也依照比例去算
void setFrustum() { double top = nearZ*tan(DTR*fovy/2); //sets top of frustum based on fovy and near clipping plane double right = aspect*top; //sets right of frustum based on aspect ratio double frustumshift = (IOD/2)*nearZ/screenZ; leftCam.topfrustum = top; leftCam.bottomfrustum = -top; leftCam.leftfrustum = -right + frustumshift; leftCam.rightfrustum = right + frustumshift; leftCam.modeltranslation = IOD/2; rightCam.topfrustum = top; rightCam.bottomfrustum = -top; rightCam.leftfrustum = -right - frustumshift; rightCam.rightfrustum = right - frustumshift; rightCam.modeltranslation = -IOD/2; }
.處理左右影像繪圖
OpenGL基礎部分省略, 雖然畫幾個簡單幾何圖形表示就可以了, 不過感覺好像z值不夠變化, 所以找個DepthMap影像來畫一畫...
圖三, 材質
圖四, DepthMap
(圖的來源應該是某個學術網站, 臨時忘了網址, 想起來之後會補上)
.簡單依照DepthMap畫出3D模型
畫完了之後可以開始準備處理左右眼影像了...
圖五, 前視圖
圖六, 上視圖
.左右眼影像繪圖處理GLvoid drawFrustumView(GLvoid) { glDrawBuffer(GL_BACK); //draw into both back buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear color and depth buffers if(nChangeSideView%2 == 0) { glDrawBuffer(GL_BACK_LEFT); //draw into back left buffer glMatrixMode(GL_PROJECTION); glLoadIdentity(); //reset projection matrix glFrustum(leftCam.leftfrustum, leftCam.rightfrustum, //set left view frustum leftCam.bottomfrustum, leftCam.topfrustum, nearZ, farZ); glTranslatef(leftCam.modeltranslation, 0.0, 0.0); //translate to cancel parallax glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 0, 0, 0, -1*screenZ, 0, 1, 0); glPushMatrix(); { glTranslated(xTranslation,yTranslation,-1*screenZ);//translate to screenplane RenderHeightMap(g_HeightMap); } glPopMatrix(); } else if(nChangeSideView%2 == 1) { glDrawBuffer(GL_BACK_RIGHT); //draw into back right buffer glMatrixMode(GL_PROJECTION); glLoadIdentity(); //reset projection matrix glFrustum(rightCam.leftfrustum, rightCam.rightfrustum, //set left view frustum rightCam.bottomfrustum, rightCam.topfrustum, nearZ, farZ); glTranslatef(rightCam.modeltranslation, 0.0, 0.0); //translate to cancel parallax glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 0, 0, 0, -1*screenZ, 0, 1, 0); glPushMatrix(); { glTranslated(xTranslation,yTranslation,-1*screenZ); RenderHeightMap(g_HeightMap); } glPopMatrix(); } }
.實作結果
以上為用OpenGL實做左右眼影像, 如果要用DirectX處理的話, 不很確定作法, 網路大概找了一下, 有可能是
D3DXMATRIX* WINAPI D3DXMatrixLookAtLH ( D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pEye, CONST D3DXVECTOR3 *pAt, CONST D3DXVECTOR3 *pUp ) // Set up our view matrix. A view matrix can be defined given an eye point, // a point to lookat, and a direction for which way is up. Here, we set the // eye five units back along the z-axis and up three units, look at the // origin, and define "up" to be in the y-direction. D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f ); D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f ); D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f ); D3DXMATRIXA16 matView; D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec ); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
4.後續
如果要利用左右影像做3D影像, 紅藍記得是alpha兩邊都設0.5, 顏色處理一下, 找個想要讓眼睛焦點對上的地方當對位點對好即可,如果是要處理交錯, 就比較麻煩了, X86上可以用render to texture然後從gpu取貼圖到pc做處理, 或者是比較有效率的直接在shader做處理, 後者在OpenGL ES也可以實作, 前者會比較受限於OpenGL ES實作上有困難(至少我做的時候是那樣....哈).
原帖:
http://arkkk.blogspot.com/2011/09/stereoscopic-3d-in-opengl.html
沒有留言:
張貼留言