首先, 在開始前有些基本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實作上有困難(至少我做的時候是那樣....哈).








沒有留言:
張貼留言