天天看點

OpenCASCADE View Manipulator

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

Abstract. When you finish modeling objects in the scene, you must want to use some operations to view the scene objects, such as Pan, Zoom and Rotate the scene. Pan and Zoom is easy to understand, rotate the 3D scene according to 2D point in the viewport is a little complicated. There are many methods to rotate the 3D scene, but the Arcball Controller is intuitive for the user and any viewport can be described. You can rotate your model at will just by using the mouse.

Key Words. OpenCASCADE Camera, View, ArcBall, Rotate

1. Introduction

當用OpenGL建立了一個模型場景後,就需要有便捷的操作來觀察場景中的物體。場景的觀察即注重于一個從三維世界轉換到二維螢幕的過程。假設場景的觀察者使用一台相機來記錄世界的變化,那麼相機的移動、角度偏轉、焦距變化都會改變底片上顯現的内容,也就是觀察這個世界的方式,這涉及到三維人機的互動。

三維使用者互動是一種與三維環境本身特性相比對的互動動作,可使使用者在虛拟場景中獲得身臨其境的直覺感受。三維世界的互動技術相當于一種“控制-顯示”的映射,使用者裝置例如滑鼠、鍵盤等向系統輸入控制資訊,然後系統向使用者輸出執行結果。是以首先要對硬體裝置的輸入資訊進行處理,然後就是根據這些資訊來改變場景資料。

三維互動涉及的任務很多,包括三維場景對象的選擇和編輯、三維世界中的導航漫遊,乃至時下流行的三維互動模組化等。本文主要介紹如何通過改變像機參數來對場景進行浏覽,如對場景的平移、縮放和旋轉操作。

OpenCASCADE View Manipulator

Figure 1.1 OpenCASCADE Viewer 

2.Translate View

場景的移動就是改變觀察相機的位置,相對容易了解,在OpenCASCADE的類V3d_View中也是這樣實作的,代碼如下所示:

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

// function : Translate

// purpose  : Internal

void V3d_View::Translate (const Handle(Graphic3d_Camera)&amp; theCamera,

                          const Standard_Real theDXv,

                          const Standard_Real theDYv) const

{

  const gp_Pnt&amp; aCenter = theCamera-&gt;Center();

  const gp_Dir&amp; aDir = theCamera-&gt;Direction();

  const gp_Dir&amp; anUp = theCamera-&gt;Up();

  gp_Ax3 aCameraCS (aCenter, aDir.Reversed(), aDir ^ anUp);

  gp_Vec aCameraPanXv = gp_Vec (aCameraCS.XDirection()) * theDXv;

  gp_Vec aCameraPanYv = gp_Vec (aCameraCS.YDirection()) * theDYv;

  gp_Vec aCameraPan = aCameraPanXv + aCameraPanYv;

  gp_Trsf aPanTrsf;

  aPanTrsf.SetTranslation (aCameraPan);

  theCamera-&gt;Transform (aPanTrsf);

}

由上述代碼可知,根據兩次滑鼠位置計算出需要移動的偏移量來對相機進行移動變換。根據滑鼠第一次按下及移動過程中的坐标點來計算偏移量。計算偏移量時,需要注意坐标系的統一,即要麼都在視口坐标系,要麼都在世界坐标系中。如下代碼是将滑鼠點變換到世界坐标系中進行移動:

void ArcballController::Translate(const gp_Pnt2d &amp;thePoint)

  gp_Pnt aCurrentPoint = Convert2World(thePoint);

  

    gp_Trsf aTrsf;

  aTrsf.SetTranslation(aCurrentPoint, mPreviousPoint);

    mCamera-&gt;Transform(aTrsf);

對相機參數進行修改後,需要更新場景資料。移動場景隻涉及到MODELVIEW變換,是以需要重新整理模型視圖MODELVIEW變換矩陣資料并重繪場景,相關代碼如下所示:

// model/view transformation for pan and rotate.

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glLoadMatrixf(theArcballController.GetOrientationMatrix());

其中theArcballController的這個函數是調用了Graphic3d_Camera的函數來設定模型視圖變換矩陣。經過測試,移動效果還可以,如下圖所示為将一個Teapot從螢幕左上角移動到了右下角:

OpenCASCADE View Manipulator

Figure 2.1 Translate the Scene

3.Zoom View

對于透視投影而言,靠模型越近,看到模型就越大,因為透視投影的特點就是近大遠小。而對平行投影而言,這種規律就不适用了。其實二者都可以統一到通過調整視口大小來對場景模型進行縮放。同樣的模型,當投影到較大的視口中時,模型的投影得到的二維圖形也會較大;當投影到較小的視口中時,模型的投影得到的二維圖形也會較小。這樣就達到對場景進行縮放的目的了。其中OpenCASCADE中的實作是通過設定Graphic3d_Camera的Scale來實作的,代碼如下圖所示:

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

//function : SetZoom

//purpose  :

void V3d_View::SetZoom(const Standard_Real Coef,const Standard_Boolean Start)

  V3d_BadValue_Raise_if( Coef &lt;= 0.,"V3d_View::SetZoom, bad coefficient");

  if (Start)

  {

    myCamStartOpEye    = myCamera-&gt;Eye();

    myCamStartOpCenter = myCamera-&gt;Center();

  }

  Standard_Real aViewWidth  = myCamera-&gt;ViewDimensions().X();

  Standard_Real aViewHeight = myCamera-&gt;ViewDimensions().Y();

  // ensure that zoom will not be too small or too big

  Standard_Real coef = Coef;

  if (aViewWidth &lt; coef * Precision::Confusion())

    coef = aViewWidth / Precision::Confusion();

  else if (aViewWidth &gt; coef * 1e12)

    coef = aViewWidth / 1e12;

  if (aViewHeight &lt; coef * Precision::Confusion())

    coef = aViewHeight / Precision::Confusion();

  else if (aViewHeight &gt; coef * 1e12)

    coef = aViewHeight / 1e12;

  myCamera-&gt;SetEye (myCamStartOpEye);

  myCamera-&gt;SetCenter (myCamStartOpCenter);

  myCamera-&gt;SetScale (myCamera-&gt;Scale() / Coef);

  View()-&gt;AutoZFit();

  ImmediateUpdate();

根據滑鼠點計算出縮改系數,通過myCamera-&gt;SetScale()來達到對場景進行縮放的目的。場景縮放操作涉及到需要更新OpenGL的投影矩陣資料,代碼如下所示:

// projection transformation for zoom.

glMatrixMode(GL_PROJECTION);

glLoadMatrixf(theArcballController.GetProjectionMatrix());

OpenCASCADE View Manipulator

Figure 3.1 Zoom the scene

4.Rotate View

通過滑鼠在二維螢幕上來旋轉三維的場景有幾種方法,如下圖所示:

OpenCASCADE View Manipulator

Figure 4.1 3D Rotation(http://www.cabiatl.com/mricro/obsolete/graphics/3d.html)

方法一是通過Euler Angles來實作,好處是使用者比較容易了解Euler角,如yaw, pitch和roll,如下圖所示:

OpenCASCADE View Manipulator

Figure 4.2 Euler Angles: Yaw, Pitch and Roll

缺點就是因為死鎖問題(gimbal lock)導緻不能指定一些視圖,當出現死鎖問題時,操作就顯得不直覺了。

比較直覺的方法就是ArcBall方式了,使用這種方法可以以任意方向來檢視場景中的模型。有個網頁版的實作,可以去體驗一下:

<a href="http://www.math.tamu.edu/~romwell/arcball_js/index.html">http://www.math.tamu.edu/~romwell/arcball_js/index.html</a>

OpenCASCADE View Manipulator

Figure 4.3 Arcball in Javascript

ArcBall的原理是将二維螢幕上滑鼠點轉換到球面上,拖動滑鼠就是在轉動這個球。根據映射到球面的兩個點,通過矢量的點乘及叉乘得到旋轉角度及旋轉軸。通過這種方式可以将二維的滑鼠位置映射到三維的場景來實作對場景觀察的直覺操作。

OpenCASCADE中場景的旋轉方式是通過先周遊場景中的模型計算出重心點,再繞三個坐标軸來旋轉,代碼如下所示:

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

//function : Rotate

void V3d_View::Rotate(const Standard_Real ax, const Standard_Real ay, const Standard_Real az,

                      const Standard_Real X, const Standard_Real Y, const Standard_Real Z, const Standard_Boolean Start)

  Standard_Real Ax = ax ;

  Standard_Real Ay = ay ;

  Standard_Real Az = az ;

  if( Ax &gt; 0. ) while ( Ax &gt; DEUXPI ) Ax -= DEUXPI ;

  else if( Ax &lt; 0. ) while ( Ax &lt; -DEUXPI ) Ax += DEUXPI ;

  if( Ay &gt; 0. ) while ( Ay &gt; DEUXPI ) Ay -= DEUXPI ;

  else if( Ay &lt; 0. ) while ( Ay &lt; -DEUXPI ) Ay += DEUXPI ;

  if( Az &gt; 0. ) while ( Az &gt; DEUXPI ) Az -= DEUXPI ;

  else if( Az &lt; 0. ) while ( Az &lt; -DEUXPI ) Az += DEUXPI ;

    myGravityReferencePoint.SetCoord (X, Y, Z);

    myCamStartOpUp = myCamera-&gt;Up();

    myCamStartOpEye = myCamera-&gt;Eye();

  const Graphic3d_Vertex&amp; aVref = myGravityReferencePoint;

  myCamera-&gt;SetUp (myCamStartOpUp);

  // rotate camera around 3 initial axes

  gp_Pnt aRCenter (aVref.X(), aVref.Y(), aVref.Z());

  gp_Dir aZAxis (myCamera-&gt;Direction().Reversed());

  gp_Dir aYAxis (myCamera-&gt;Up());

  gp_Dir aXAxis (aYAxis.Crossed (aZAxis)); 

  gp_Trsf aRot[3], aTrsf;

  aRot[0].SetRotation (gp_Ax1 (aRCenter, aYAxis), -Ax);

  aRot[1].SetRotation (gp_Ax1 (aRCenter, aXAxis), Ay);

  aRot[2].SetRotation (gp_Ax1 (aRCenter, aZAxis), Az);

  aTrsf.Multiply (aRot[0]);

  aTrsf.Multiply (aRot[1]);

  aTrsf.Multiply (aRot[2]);

  myCamera-&gt;Transform (aTrsf);

5.Conclusion

當實作三維場景的模組化後,最激動人心的應該是對場景及場景中模型的控制。通過互動操作使使用者友善地觀察場景的模型,或直覺地編輯場景中的模型。是以互動也是三維軟體中的重要功能,且是給使用者最直接的感覺的操作。

因為互動操作涉及到滑鼠鍵盤消息的處理,是以首先要設計好對這些消息的處理方式,在OpenSceneGraph中使用了擴充卡的方式來實作跨平台的消息處理,使使用者通過繼承的方式來實作對消息的處理。這種方式使程式的可擴充性及代碼的可讀性更好,OpenCASCADE中的消息的處理還是比較直接的,沒有什麼封裝。

本文主要介紹了如何實作對場景的控制,如移動、縮放及旋轉操作,這些功能的實作需要對OpenGL的渲染管線有一定的了解。在了解了對視圖/場景的控制後,為進一步了解對場景中的模型的控制打下基礎,如選擇Picking,拖拽Drag等操作。最後給出一個基于OpenCASCADE的類Graphic3d_Camera及GLUT實作的場景變換操作,功能不是很完善,僅供參考。若有好的意見,歡迎回報。

6. References

2. WikiBooks. Modern OpenGL Tutorial Arcball. 

<a href="http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball">http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball</a>

6. 王銳,錢學雷. OpenSceneGraph三維渲染引擎設計與實踐. 清華大學出版社. 2009

繼續閱讀