2013年8月29日 星期四

Rendering 3D Anaglyph in OpenGL - 5

since: 2013/08/25
update: 2013/08/29

reference:
0. 原文: Rendering 3D Anaglyph in OpenGL
1. I touchs: Rendering 3D Anaglyph in OpenGL - 1
2. I touchs: Rendering 3D Anaglyph in OpenGL - 2
3. I touchs: Rendering 3D Anaglyph in OpenGL - 3
4. I touchs: Rendering 3D Anaglyph in OpenGL - 4

H.
程式碼片斷

      1. 這裡有一個片段的程式碼, 用來展示如何將之前的方程式包裹成一個
          小類別 StereoCamera:(立體攝影機)

class StereoCamera
{
public:
    // 建構子
    StereoCamera(  
        float Convergence, // 收斂距離
        float EyeSeparation, // 二眼間隔
        float AspectRatio, // 寬高比
        float FOV, // 視野角度
        float NearClippingDistance, // 近截距
        float FarClippingDistance // 遠截距
        )
    {
        mConvergence            = Convergence;
        mEyeSeparation          = EyeSeparation;
        mAspectRatio            = AspectRatio;
        mFOV                    = FOV * PI / 180.0f;
        mNearClippingDistance   = NearClippingDistance;
        mFarClippingDistance    = FarClippingDistance;
    }

    // 套用左視錐
    void ApplyLeftFrustum()
    {
        float top, bottom, left, right;

        top     = mNearClippingDistance * tan(mFOV/2);
        bottom  = -top;

        float a = mAspectRatio * tan(mFOV/2) * mConvergence;

        float b = a - mEyeSeparation/2;
        float c = a + mEyeSeparation/2;

        left    = -b * mNearClippingDistance/mConvergence;
        right   =  c * mNearClippingDistance/mConvergence;

        // 設置投影矩陣 (會後做)
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();  
        glFrustum(left, right, bottom, top,
                  mNearClippingDistance, mFarClippingDistance);

        // 設置 模型/視野 矩陣 (會先做)      
        glMatrixMode(GL_MODELVIEW);                    
        glLoadIdentity();  
        // 將世界座標往右位移: 二眼間隔的一半距離
        glTranslatef(mEyeSeparation/2, 0.0f, 0.0f);      
    }

    // 套用右視錐
    void ApplyRightFrustum()
    {
        float top, bottom, left, right;

        top     = mNearClippingDistance * tan(mFOV/2);
        bottom  = -top;

        float a = mAspectRatio * tan(mFOV/2) * mConvergence;

        float b = a - mEyeSeparation/2;
        float c = a + mEyeSeparation/2;

        left    =  -c * mNearClippingDistance/mConvergence;
        right   =   b * mNearClippingDistance/mConvergence;

        // 設置投影矩陣 (會後做)
        glMatrixMode(GL_PROJECTION);                       
        glLoadIdentity();  
        glFrustum(left, right, bottom, top,
                  mNearClippingDistance, mFarClippingDistance);

        // 設置 模型/視野 矩陣 (會先做)
        glMatrixMode(GL_MODELVIEW);                    
        glLoadIdentity();  
        // 將世界座標往左位移: 二眼間隔的一半距離
        glTranslatef(-mEyeSeparation/2, 0.0f, 0.0f);
    }

// 內部變數
private:
    float mConvergence;
    float mEyeSeparation;
    float mAspectRatio;
    float mFOV;
    float mNearClippingDistance;
    float mFarClippingDistance;
};

      2. 程式碼精確地做到了先前我們對圖示與方程式所作的描述. 一旦你建立了
          StereoCamera
物件, 就可以呼叫 ApplyLeftFrustum() 與 ApplyRightFrustum()
          來各別設置非對稱的視錐. 請注意, 在這些方法中, 實際上會先作 模型/視野 的
          轉置(沿著 X 軸作位移), 然後接著才是作投影的轉置.(與實際的程式碼流程相反)
          這樣的作用是: 將攝影機從原點位移一段距離. 如此, 在 OpenGL 中, 攝影機本身
          沒有做任何的轉置. 我們所做的是: 使用 模型/視野 的轉置, 將世界座標相對於
          概念上的攝影機向某個方向移動.           

      3. 為了要使用上方的類別, 你可以撰寫如下的 OpenGL 繪圖函式:

// 主要的繪圖函式
void DrawGLScene(GLvoid)                                   
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 設定立體攝影機系統
    StereoCamera cam(
        2000.0f,     // Convergence (收斂距離)
        35.0f,       // Eye Separation (二眼間隔)
        1.3333f,     // Aspect Ratio (寬高比)
        45.0f,       // FOV along Y in degrees (沿著 Y 軸的視野角度)
        10.0f,       // Near Clipping Distance (近截距)
        20000.0f);   // Far Clipping Distance (遠截距)

    cam.ApplyLeftFrustum();
    glColorMask(true, false, false, false); // 只將紅色寫入緩沖區(紅色遮罩)

    PlaceSceneElements(); // 在場景中放入物體

    glClear(GL_DEPTH_BUFFER_BIT) ;

    cam.ApplyRightFrustum();
    glColorMask(false, true, true, false); // 只將綠色及藍色寫入緩沖區(綠藍色遮罩)

    PlaceSceneElements(); // 在場景中放入物體

    glColorMask(true, true, true, true); // 恢復將所有色彩寫入緩沖區
}

// 在場景中放入物體
void PlaceSceneElements()
{
   // 沿著負 Z 軸, 位移到適當的深度
    glTranslatef(0.0f, 0.0f, -1800.0f);

    // 旋囀場景以便觀看
    glRotatef(-60.0f, 1.0f, 0.0f, 0.0f);
    glRotatef(-45.0f, 0.0f, 0.0f, 1.0f);

    // 繪製相交的環形圓紋曲面(tori)
    glPushMatrix();
        glTranslatef(0.0f, 0.0f, 240.0f);
        glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
        glColor3f(0.2, 0.2, 0.6);
        glutSolidTorus(40, 200, 20, 30);
        glColor3f(0.7f, 0.7f, 0.7f);
        glutWireTorus(40, 200, 20, 30);
    glPopMatrix();

    glPushMatrix();
        glTranslatef(240.0f, 0.0f, 240.0f);
        glColor3f(0.2, 0.2, 0.6);
        glutSolidTorus(40, 200, 20, 30);
        glColor3f(0.7f, 0.7f, 0.7f);
        glutWireTorus(40, 200, 20, 30);
    glPopMatrix();
}

      4. 一開始的繪圖函式先清除顏色與深度的緩衝區. 然後, 設置立體攝影機系統.
          套用左邊的視錐, 並告知 OpenGL 在顏色緩沖區中, 只允許有紅色的成分.
          接著, 呼叫一般的程序來繪製場景. 之後, 清除深度緩衝區, 但是保留顏色緩衝區
          (在其中只會有紅頻的值). 隨著深度緩衝區清除後, 我們啓用右邊的視錐, 並告知
          OpenGL 在顏色緩沖區中, 只允許有綠色和藍色的成分. 然後, 再一次呼叫一般的
          繪製程序. 注意, 左眼所看到的色彩場景顏色與右眼所看到的色彩場景, 在整個
         色彩空間中, 並沒有重疊到, 因此不需要明確地對顏色作混色(blending)或堆積
         (accumulation)的處理. 最後, 啓用所有色頻, 場景就會被繪製成浮雕的樣子.  
         注意到, 我們必須畫二次幾何圖形. 代表著: 畫面更新率(率 frame rate) 降為
         單一視錐的一半. 這是典型的立體繪製. 如果你想知道上面的程式碼片斷輸出的
         結果為何, 就是這樣子: 

                                              圖十: 上方程式列輸出的繪製結果

      5. 我(原作者)有留下一段影片, 是在撰寫此篇(稍微冗長)教學文章時所作的.
          這段影片使用我先前提過的相同理論與程式碼. 試著用 720p 全螢幕來觀看,
          以獲得最佳的效果:
           ▶ Rendering Stereoscopic 3D Anaglyph in OpenGL - YouTube

      6. 延伸閱讀:
          a. Paul Bourke: Information related to stereographics
          b. NVIDIA GTC 2010: Implementing Stereoscopic 3D in Your Applications

          盡情享受!

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。