天天看點

OpenCASCADE Camera

<a href="mailto:[email protected]">[email protected]</a>

Abstract. OpenCASCADE introduce a new class Graphic3d_Camera for the Visualization module. The camera class provides object-oriented approach to setting up projection and orientation properties of 3D view. The paper use GLUT to test the function of the new Camera class.

Key Words. OpenCASCADE, Camera, OpenGL, GLUT, Transformations

1. Introduction

在三維場景中變換的過程與用相機拍照的過程很相似,如下圖所示,拍照的過程大概如下:

OpenCASCADE Camera

Figure 1.1 The Camera Analogy (From the Red Book)

v 放好三角架并使相機對準要拍照的場景(viewing transformation);

v 布置好場景到理想的位置,以便拍照(modeling transformation);

v 選擇鏡頭或調整變焦(projection transformation);

v 确定最終的照片要有多大尺寸,例如你可能需要放大一些的照片(viewport transformation);

OpenCASCADE在6.8.0版本中引入了一個Graphic3d_Camera類來對視圖的變換進行管理,通過對camera的調整,友善對場景中的模型/視圖(Model/View)進行變換。

OpenCASCADE Camera

Figure 1.2 Camera in the Scene

如下圖所示,對象的空間坐标經過一系列的變換,最終得到顯示螢幕上的二維坐标。

OpenCASCADE Camera

Figure 1.3 Stages of Vertex Transformation (From the Red Book)

為了指定viewing, modeling和projection變換,可以構造一個4x4的矩陣與頂點的齊次坐标相乘來實作這些變換。

2.Coordinate Transforms

在計算機圖形學中,造型MODELING就是定義一個可渲染物體的數字表示。對于OpenGL而言,通常就是用多邊形來近似表示一個物體。用多邊形表示物體至少需要每個多邊形頂點的坐标和連接配接關系的資訊。附加的資訊包含頂點顔色、法向量或貼圖坐标等。

三維對象的屬性如頂點位置及面的法向量,為了造型的友善,通常定義在對象空間OBJECT SPACE中,即在局部坐标系中模組化。對象空間通常也叫作模型空間MODEL SPACE或造型坐标系統MODELING COORDINATE SYSTEM。

在一個包含大量三維對象的場景中,每一個對象都是在其對象空間中模組化的,這是我們就需要一個統一的坐标系統COMMON COORDINATE SYSTEM,通常稱之為世界空間WORLD SPACE或世界坐标系WORLD COORDINATE SYSTEM,或絕對坐标系。

定義了世界坐标系統後,場景中所有模型對象必須從其局部坐标系變換到世界坐标系中來。從對象空間變換到世界空間的變換稱之為模型變換MODELING TRANSFORMATION。若模型對象造型坐标系中的機關是feet,但是世界坐标系中是inches,對象坐标必須縮小12倍以得到世界坐标系。若對象在其對象空間中朝前的,但是在世界場景中需要其朝後,那麼需要對模型對象進行一個旋轉變換。當要将模型對象放到場景中的合适位置時,那麼就需要進行一個移動變換。所有這些單個變換也可以合成一個矩陣,即模型變換矩陣MODEL TRANSFORMATION MATRIX,就是表示将模型對象從對象坐标系變換到世界坐标系中。

當場景确定好後,視圖viewing parameters的參數就需要确定了。視圖的參數之一就是視點vantage point(eye or camera position),即在哪裡對模型進行觀察。視圖的參數還包括焦點focus point(also called the lookat point or the direction in which the camera is pointed)和向上的方向(the camera may be held sideways or upside down)。視圖的參數定義了視圖變換VIEWING TRANSFORMATION,它們也可以合成一個矩陣,稱作視圖矩陣VIEWING MATRIX。一個坐标乘以這個矩陣就可以将其從世界空間變換到觀察空間EYE SPACE,也稱為EYE COORDINATE SYSTEM。由定義可知,此坐标系的原點位于視點處。

盡管有些3D圖形API允許分别指定模型變換矩陣modeling matrix和視圖變換矩陣viewing matrix,但是在OpenGL中将這兩者合成為一個矩陣,稱為模型/視圖矩陣MODELVIEW MATRIX。這個矩陣定義了坐标從對象空間變換到觀察空間的變換。

OpenCASCADE Camera

Figure 2.1 Coordinate spaces and transforms in OpenGL(From OpenGL Shading Language)

在OpenGL中,可以調用函數glMatrixMode來選擇modelview matrix或其他的矩陣,然後可以調用函數glLoadIdentity将目前矩陣設定為機關矩陣,或調用函數glLoadMatrix将目前矩陣替換為任意一個矩陣。替換時你需要清楚地知道你要進行的變換操作,否則容易産生完全不可了解的視圖。還可以調用函數glMultMatrix來将目前矩陣與任意矩陣相乘。可以通過調用函數gluLookAt來設定modelview變換矩陣。

當坐标變換到觀察空間EYE SPACE後,下一步需要定義一個觀察體VIEWING VOLUME,即這個區域内的三維場景将出現在最終的圖像上。将模型對象放進觀察體即裁剪空間CLIP SPACE(也稱作裁剪坐标系CLIPPING COORDINATE SYSTEM)的變換稱為投影變換PROJECTION TRANSFORMATION。在OpenGL中,可以通過glMatrixModel來選擇需要設定的變換矩陣。也可以調用函數glOrtho、glFrustum、gluPerspective來設定。

通過投影變換将觀察空間内的三維模型投影到了二維空間,下一步變換就是頂點坐标的perspective divide。這個操作将觀察體内所有坐标點的各個分量除以齊次坐标分量w。得到的x, y, z的取值範圍都是[-1, 1],而齊次坐标值w為1,是以就不再需要w了。換句話說所有可見的圖元graphics primitives都變換到一個在點(-1,-1,-1)和點(1,1,1)之間的立方體區域,這就是規範化裝置坐标空間NORMALIZED DEVICE COORDINATE SPACE,他是個允許将觀察區域viewing area映射到任意尺寸及深度的視口的中間空間。

視窗顯示的像素點pixel不能指向-1到1的浮點數,通常指向視窗坐标系WINDOW COORDINATE SYSTEM中的坐标,x取值範圍從0到視窗寬度減1,y取值範圍從0到視窗高度減1,是以還需要進行一步變換處理。視口變換指定了從規範裝置坐标系到視窗坐标系的變換VIEWPORT TRANSFORMATION。在OpenGL通過函數glViewport來指定視口變換。

以上内容主要來自《OpenGL Shading Language》一書中的第一章“Review of OpenGL Basics-1.9 Coordinate Transforms”,詳細内容可參考原書。也可利用下面的程式來直覺地學習這些變換及投影的知識:

OpenCASCADE Camera

Figure 2.2 OpenGL Tutors-Projection (by Nate Robins)

3.Camera of OpenCASCADE

OpenCASCADE6.8.0版本中引入一個類Graphic3d_Camera來簡化對三維視圖的變換操作,即可以對視圖進行投影和模型視圖的相關設定。如通過設定視點Eye position和中心點可以計算得到觀察矩陣,類似gluLookAt的功能,相關代碼如下所示:

// =======================================================================

// function : UpdateOrientation

// purpose  :

template &lt;typename Elem_t&gt;

Graphic3d_Camera::TransformMatrices&lt;Elem_t&gt;&amp;

  Graphic3d_Camera::UpdateOrientation (TransformMatrices&lt;Elem_t&gt;&amp; theMatrices) const

{

  if (theMatrices.IsOrientationValid())

  {

    return theMatrices; // for inline accessors

  }

  theMatrices.InitOrientation();

  NCollection_Vec3&lt;Elem_t&gt; anEye (static_cast&lt;Elem_t&gt; (myEye.X()),

                                  static_cast&lt;Elem_t&gt; (myEye.Y()),

                                  static_cast&lt;Elem_t&gt; (myEye.Z()));

  NCollection_Vec3&lt;Elem_t&gt; aCenter (static_cast&lt;Elem_t&gt; (myCenter.X()),

                                    static_cast&lt;Elem_t&gt; (myCenter.Y()),

                                    static_cast&lt;Elem_t&gt; (myCenter.Z()));

  NCollection_Vec3&lt;Elem_t&gt; anUp (static_cast&lt;Elem_t&gt; (myUp.X()),

                                 static_cast&lt;Elem_t&gt; (myUp.Y()),

                                 static_cast&lt;Elem_t&gt; (myUp.Z()));

  NCollection_Vec3&lt;Elem_t&gt; anAxialScale (static_cast&lt;Elem_t&gt; (myAxialScale.X()),

                                         static_cast&lt;Elem_t&gt; (myAxialScale.Y()),

                                         static_cast&lt;Elem_t&gt; (myAxialScale.Z()));

  LookOrientation (anEye, aCenter, anUp, anAxialScale, *theMatrices.Orientation);

  return theMatrices; // for inline accessors

}

通過調用這個函數得到将世界坐标系變換到觀察坐标系的變換矩陣。其實作步驟如下:

v 平移觀察坐标原點到世界坐标系原點;

v 進行旋轉,分别讓觀察坐标系的Vx, Vy, Vz軸對應到世界坐标的Wx,Wy,Wz軸。

OpenCASCADE中的實作代碼如下所示:

// function : LookOrientation

void Graphic3d_Camera::LookOrientation (const NCollection_Vec3&lt;Elem_t&gt;&amp; theEye,

                                      const NCollection_Vec3&lt;Elem_t&gt;&amp; theLookAt,

                                      const NCollection_Vec3&lt;Elem_t&gt;&amp; theUpDir,

                                  const NCollection_Vec3&lt;Elem_t&gt;&amp; theAxialScale,

                                        NCollection_Mat4&lt;Elem_t&gt;&amp; theOutMx)

  NCollection_Vec3&lt;Elem_t&gt; aForward = theLookAt - theEye;

  aForward.Normalize();

  // side = forward x up

  NCollection_Vec3&lt;Elem_t&gt; aSide = NCollection_Vec3&lt;Elem_t&gt;::Cross (aForward, theUpDir);

  aSide.Normalize();

  // recompute up as: up = side x forward

  NCollection_Vec3&lt;Elem_t&gt; anUp = NCollection_Vec3&lt;Elem_t&gt;::Cross (aSide, aForward);

  NCollection_Mat4&lt;Elem_t&gt; aLookMx;

  aLookMx.SetRow (0, aSide);

  aLookMx.SetRow (1, anUp);

  aLookMx.SetRow (2, -aForward);

  theOutMx.InitIdentity();

  theOutMx.Multiply (aLookMx);

  theOutMx.Translate (-theEye);

  NCollection_Mat4&lt;Elem_t&gt; anAxialScaleMx;

  anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();

  anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();

  anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();

  theOutMx.Multiply (anAxialScaleMx);

當指定投影變換類型後,可以得到投影變換矩陣,可以指定的類型如下:

  //! Enumerates supported monographic projections.

  //! - Projection_Orthographic : orthographic projection.

  //! - Projection_Perspective  : perspective projection.

  //! - Projection_Stere        : stereographic projection.

  //! - Projection_MonoLeftEye  : mono projection for stereo left eye.

  //! - Projection_MonoRightEye : mono projection for stereo right eye.

  enum Projection

    Projection_Orthographic,

    Projection_Perspective,

    Projection_Stereo,

    Projection_MonoLeftEye,

    Projection_MonoRightEye

  };

當指定為Projection_Orthographic時即為正投影變換。使用從坐标位置到觀察平面的正投影變換,任意一點(x, y, z)的投影位置是(x, y)。是以在建立觀察體的範圍後,該矩形平行管道内部的坐标描述即為投影坐标,它們不需要另外的投影處理就可直接映射到規範化觀察體normalized view volume。有些圖形軟體包使用機關立方體作為規範化觀察體,其x,y,z坐标規範成0到1之間,另外的規範化變換方法使用坐标範圍從-1到1的對稱立方體。

由于螢幕坐标系經常指定為左手系,是以規範化觀察體也常指定為左手系統。正投影觀察體的規範化變換矩陣是:詳細推導請參考《Computer Graphics with OpenGL》。

OpenCASCADE Camera

對應OpenCASCADE中實作代碼如下所示:

// function : OrthoProj

void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,

                                  const Elem_t theRight,

                                  const Elem_t theBottom,

                                  const Elem_t theTop,

                                  const Elem_t theNear,

                                  const Elem_t theFar,

                                  NCollection_Mat4&lt;Elem_t&gt;&amp; theOutMx)

  // row 0

  theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);

  theOutMx.ChangeValue (0, 1) = Elem_t (0.0);

  theOutMx.ChangeValue (0, 2) = Elem_t (0.0);

  theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);

  // row 1

  theOutMx.ChangeValue (1, 0) = Elem_t (0.0);

  theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);

  theOutMx.ChangeValue (1, 2) = Elem_t (0.0);

  theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);

  // row 2

  theOutMx.ChangeValue (2, 0) = Elem_t (0.0);

  theOutMx.ChangeValue (2, 1) = Elem_t (0.0);

  theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);

  theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);

  // row 3

  theOutMx.ChangeValue (3, 0) = Elem_t (0.0);

  theOutMx.ChangeValue (3, 1) = Elem_t (0.0);

  theOutMx.ChangeValue (3, 2) = Elem_t (0.0);

  theOutMx.ChangeValue (3, 3) = Elem_t (1.0);

平行投影主要用于設計制圖CAD,而透視投影更有真實感覺,即近大遠小,是以在一些三維浏覽或仿真程式中主要使用透視投影。當指定投影類型為Projection_Perspective時,即指定為場景的投影變換為透視變換。一般的透視投影規範化變換矩陣如下所示:詳細推導請參考《Computer Graphics with OpenGL》。

OpenCASCADE Camera

OpenCASCADE中相關的實作代碼如下所示:

// function : PerspectiveProj

void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,

                                        const Elem_t theRight,

                                        const Elem_t theBottom,

                                        const Elem_t theTop,

                                        const Elem_t theNear,

                                        const Elem_t theFar,

  // column 0

  theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);

  // column 1

  theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);

  // column 2

  theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);

  theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);

  theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);

  theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);

  // column 3

  theOutMx.ChangeValue (0, 3) = Elem_t (0.0);

  theOutMx.ChangeValue (1, 3) = Elem_t (0.0);

  theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);

  theOutMx.ChangeValue (3, 3) = Elem_t (0.0);

其中投影方式Projection_Stereo為立體投影,在網上搜了一下,這種投影可以應用到3D影片的播放。目前來看,相關應用還是很火爆的,可以用來在電視中播放立體影片。更多介紹可參考:

<a href="http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf">http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf</a>

<a href="http://www.gali-3d.com/archive/articles/StereoOpenGL/StereoscopicOpenGLTutorial.php">http://www.gali-3d.com/archive/articles/StereoOpenGL/StereoscopicOpenGLTutorial.php</a>

4.Test the Camera in GLUT

OpenGL實用函數工具包(OpenGL Utility Toolkit, GLUT)提供了與任意螢幕視窗系統進行互動的函數庫,可以用來快速示範OpenGL中的相關概念。其中設定投影變換矩陣的方法如下代碼所示:

// projection transformation.

glMatrixMode(GL_PROJECTION);

glLoadMatrixd(theCamera-&gt;ProjectionMatrix().GetData());

設定模型/視圖變換矩陣代碼如下所示:

glMatrixMode(GL_MODELVIEW);

glLoadMatrixd(theCamera-&gt;OrientationMatrix().GetData());

glutPostRedisplay();

通過鍵盤按鍵o和p用來在平行投影和透視投影之間進行切換,切換效果如下圖所示:

OpenCASCADE Camera

Figure 4.1 Orthographic and Perspective projection test

如上圖所示,透視投影比平行投影更具立體感,但是在工程設計CAD或機械制圖CAD軟體中,平行投影更有利于設計人員使用。當處于平行投影模式時,視點的遠近對模型在視圖中的顯示沒有影響;當處于透視投影模式時,移動視點對視圖是有影響的:當視點離模型較近時,模型在視圖中顯示較大,反之較小。

按下滑鼠中鍵可以用來測試四元數Quaternion插值來對場景繞Y軸進行旋轉,其實主要用來測試OpenCASCADE的quaternion插值類的插值效果。代碼及效果圖如下所示:

    static gp_Quaternion aStartQuat(gp_Vec(0.0, 1.0, 0.0), 0.0);

    static gp_Quaternion aEndQuat(gp_Vec(0.0, 1.0, 0.0), M_PI);

    static gp_QuaternionSLerp aSlerp(aStartQuat, aEndQuat);

    gp_Quaternion aResultQuat;

    aSlerp.Interpolate(T, aResultQuat);

OpenCASCADE Camera

Figure 4.2 Test Quaternion SLERP

程式源代碼見文後附件,可下載下傳編譯後自己修改一些參數來測試效果。

5.Conclusion

OpenGL中的坐标變換涉及到對象空間,模型空間,世界空間,觀察空間及相關的坐标變換,而所有的變換操作都可以統一為一個4X4的齊次矩陣的數學運算,如矩陣加法、矩陣乘法、求逆矩陣等。

OpenCASCADE用面向對象的思想封裝了Grpahic3d_Camera用來友善對視圖的變換控制,通過設定視點及投影類型等相關參數,即可得到相關的變換矩陣,進而可對模型/視圖友善地進行變換。利用Camera的類,可以友善對視圖進行縮放、旋轉等變換操作。相關實作可參考OpenCASCADE中視圖類中的代碼。

最後,還測試了一下gp_Quaternion插值的算法。

6. References

1. Dave Shreiner. OpenGL Programming Guide(7th Edition). Addison-Wesley. 2010

2. Randi J. Rost. OpenGL Shading Language(2nd Edition). Addison-Wesley. 2006

3. Donald Hearn, M. Pauline Baker. Computer Graphics with OpenGL. 2004

6. 孫家廣等. 計算機圖形學. 清華大學出版社. 2000

繼續閱讀