VTK中世界坐标系到显示坐标系下的流程分析


水平有限,欢迎各位大神指正!

///

关于坐标相互转换的理论部分可以参阅 http://blog.csdn.net/popy007/article/details/1797121


解析vtk下世界坐标系world到显示坐标系下的转换:

首先呢在vtkInteractorObserver中存在函数ComputeWorldToDisplay()函数供其子类调用,源代码如下:


//----------------------------------------------------------------------------

// Description:

// Transform from world to display coordinates.

// displayPt has to be allocated as 3 vector

void vtkInteractorObserver::ComputeWorldToDisplay(double x, double y, double z,  double displayPt[3])

{

  if ( !this->CurrentRenderer ) 

    {

    return;

    }

   this->ComputeWorldToDisplay(this->CurrentRenderer, x, y, z, displayPt);

}


// Description:

// Transform from world to display coordinates.

// displayPt has to be allocated as 3 vector

void vtkInteractorObserver::ComputeWorldToDisplay(vtkRenderer *ren, double x, double y, double z,  double displayPt[3])

{

  ren->SetWorldPoint(x, y, z, 1.0);

  ren->WorldToDisplay();

  ren->GetDisplayPoint(displayPt);

}

///

借助于上边两个函数最终还是靠vtkRenderer来完成世界坐标系向显示坐标系下的转换,在函数中,首先在世界坐标系下定义了一个点(x,y,z,1),然后调用vtkViewport(vtkRenderer的父类)的WorldToDisplay()函数,下边我们解剖这个函数内部功能。

  // Description:

  // Convert world point coordinates to display (or screen) coordinates.

  void WorldToDisplay() {this->WorldToView(); this->ViewToDisplay();};

这个函数先将world坐标系转换为view坐标系,然后再将view坐标系转换为显示坐标系

(1)下边解剖WorldToView()函数

// Convert world point coordinates to view coordinates.

void vtkRenderer::WorldToView()

{

  double result[3];

  result[0] = this->WorldPoint[0];

  result[1] = this->WorldPoint[1];

  result[2] = this->WorldPoint[2];

  this->WorldToView(result[0], result[1], result[2]);

  this->SetViewPoint(result[0], result[1], result[2]);

}

从函数流程可以看到,先获取我们之前设置的那个world世界坐标,然后把它转换为view坐标系下的坐标,并且存储起来,以便在转换为display坐标系的时候使用。我们看这个函数的主要部分:

// Convert world point coordinates to view coordinates.

void vtkRenderer::WorldToView(double &x, double &y, double &z)

{

  double     mat[16];

  double     view[4];


  // get the perspective transformation from the active camera

  if (!this->ActiveCamera)

    {

    vtkErrorMacro("WorldToView: no active camera, cannot compute world to view, returning 0,0,0");

    x = y = z = 0.0;

    return;

    }

  vtkMatrix4x4::DeepCopy(mat, this->ActiveCamera-> GetCompositeProjectionTransformMatrix( this->GetTiledAspectRatio(),0,1));


  view[0] = x*mat[0] + y*mat[1] + z*mat[2] + mat[3];

  view[1] = x*mat[4] + y*mat[5] + z*mat[6] + mat[7];

  view[2] = x*mat[8] + y*mat[9] + z*mat[10] + mat[11];

  view[3] = x*mat[12] + y*mat[13] + z*mat[14] + mat[15];


  if (view[3] != 0.0)

    {

    x = view[0]/view[3];

    y = view[1]/view[3];

    z = view[2]/view[3];

    }

}

我们可以看到此函数的重点在于从ActiveCamera中获得CompositeProjectionTransformMatrix,获得此矩阵与原来的世界坐标系相乘之后就可得到view坐标系下的值,下边我们看怎么获得这个矩阵呢?

//

首先呢从函数参数中可知首先得获得窗口的宽高比,通过vtkRenderer的GetTiledAspectRatio()函数

  // Description:

  // Compute the aspect ratio of this renderer for the current tile. When

  // tiled displays are used the aspect ratio of the renderer for a given

  // tile may be diferent that the aspect ratio of the renderer when rendered

  // in it entirity

double vtkRenderer::GetTiledAspectRatio()

{

  int usize, vsize;

  this->GetTiledSize(&usize,&vsize);//Get the size of the viewport in display coordinates.


  //some renderer subclasses may have more complicated computations for the

  // aspect ratio. SO take that into account by computing the difference

  // between our simple aspect ratio and what the actual renderer is

  // reporting.

  double aspect[2];

  this->ComputeAspect();

  this->GetAspect(aspect);

  double aspect2[2];

  this->vtkViewport::ComputeAspect();

  this->vtkViewport::GetAspect(aspect2);

  double aspectModification = aspect[0]*aspect2[1]/(aspect[1]*aspect2[0]);


  double finalAspect = 1.0;

  if(vsize && usize)

    {

    finalAspect = aspectModification*usize/vsize;

    }

  return finalAspect;

}

///

  // Description:

  // Return the concatenation of the ViewTransform and the ProjectionTransform.  This transform will convert world coordinates to   viewport coordinates.  

  //The 'aspect' is the width/height for the viewport, and the nearz and farz are the Z-buffer values that map to the near and far clipping planes.

  // The viewport coordinates of a point located inside the frustum are in the range ([-1,+1],[-1,+1],[nearz,farz]).

vtkMatrix4x4 *vtkCamera::GetCompositeProjectionTransformMatrix(double aspect,double nearz,double farz)//其中nearz为0,farz为1

{

  // turn off stereo, the CompositeProjectionTransformMatrix is used for

  // picking, not for rendering.

  int stereo = this->Stereo;

  this->Stereo = 0;


  this->Transform->Identity();

  this->Transform->Concatenate(this->GetProjectionTransformMatrix(aspect,nearz, farz));

  this->Transform->Concatenate(this->GetViewTransformMatrix());


  this->Stereo = stereo;


  // return the transform

  return this->Transform->GetMatrix();

}

下边讲解其中的两个重要函数:

(1)  // Description:
  // Return the projection transform matrix, which converts from camera coordinates to viewport coordinates.  The 'aspect' is the
  // width/height for the viewport, and the nearz and farz are the Z-buffer values that map to the near and far clipping planes.
  // The viewport coordinates of a point located inside the frustum are in the range ([-1,+1],[-1,+1],[nearz,farz]).
實際上 Z buffer 中能存放的數字當然會有一定的限度,所以通常會把 Z 值縮小到 0 ~ 1 的範圍。因此,在繪製 3D 場景時,就會需要把可能出現的 Z 值限制在某個範圍內。通常是用兩個和投影平面平行的平面,把所有超出這兩個平面範圍的三角面都切掉。這兩個平面通常分別稱為 Z near 和 Z far,分別表示較近的平面和較遠的平面。而在 Z near 平面的 Z 值為 0,在 Z far 的 Z 值為 1

vtkMatrix4x4 *vtkCamera::GetProjectionTransformMatrix(double aspect,double nearz,double farz) // 其中nearz为0,farz为1
{
  this->ComputeProjectionTransform(aspect, nearz, farz);

  // return the transform
  return this->ProjectionTransform->GetMatrix();
}

void vtkCamera::ComputeProjectionTransform(double aspect,  double nearz, double farz)
{
  this->ProjectionTransform->Identity();

  // apply user defined transform last if there is one
  if ( this->UserTransform )//一般为NULL
    {
    this->ProjectionTransform->Concatenate( this->UserTransform->GetMatrix() );
    }

   // adjust Z-buffer range
  this->ProjectionTransform->AdjustZBuffer( -1, +1, nearz, farz );//

  if ( this->ParallelProjection )
    {
    // set up a rectangular parallelipiped

    double width = this->ParallelScale * aspect;//ParallelScale越大最后得到的图像越小
    double height = this->ParallelScale;// ,parallelScale代表 the height of the viewport in world-coordinate distances 

    double xmin = ( this->WindowCenter[0] - 1.0 ) * width;
    double xmax = ( this->WindowCenter[0] + 1.0 ) * width;
    double ymin = ( this->WindowCenter[1] - 1.0 ) * height;
    double ymax = ( this->WindowCenter[1] + 1.0 ) * height;

    this->ProjectionTransform->Ortho( xmin, xmax, ymin, ymax,  this->ClippingRange[0], this->ClippingRange[1] );
    }
  else if(this->UseOffAxisProjection)
    {
    this->ComputeOffAxisProjectionFrustum();
    }
  else
    {
    // set up a perspective frustum

    double tmp = tan( vtkMath::RadiansFromDegrees( this->ViewAngle ) / 2. );
    double width;
    double height;
    if ( this->UseHorizontalViewAngle )
      {
      width = this->ClippingRange[0] * tmp;
      height = this->ClippingRange[0] * tmp / aspect;
      }
    else
      {
      width = this->ClippingRange[0] * tmp * aspect;
      height = this->ClippingRange[0] * tmp;
      }

    double xmin = ( this->WindowCenter[0] - 1.0 ) * width;
    double xmax = ( this->WindowCenter[0] + 1.0 ) * width;
    double ymin = ( this->WindowCenter[1] - 1.0 ) * height;
    double ymax = ( this->WindowCenter[1] + 1.0 ) * height;

    this->ProjectionTransform->Frustum( xmin, xmax, ymin, ymax,
                                        this->ClippingRange[0],
                                        this->ClippingRange[1] );
    }

  if ( this->Stereo && !this->UseOffAxisProjection)
    {
    // set up a shear for stereo views
    if ( this->LeftEye )
      {
      this->ProjectionTransform->Stereo( -this->EyeAngle/2,
                                          this->Distance );
      }
    else
      {
      this->ProjectionTransform->Stereo( +this->EyeAngle/2,
                                          this->Distance );
      }
    }

  if ( this->ViewShear[0] != 0.0 || this->ViewShear[1] != 0.0 )
    {
    this->ProjectionTransform->Shear( this->ViewShear[0],
                                      this->ViewShear[1],
                                      this->ViewShear[2] * this->Distance );
    }

}

//----------------------------------------------------------------------------
// Utility for adjusting the min/max range of the Z buffer.  Usually
// the oldZMin, oldZMax are [-1,+1] as per Ortho and Frustum, and
// you are mapping the Z buffer to a new range(0,1)
  // Description:
  // Perform an adjustment to the Z-Buffer range that the near and far
  // clipping planes map to.  By default Ortho, Frustum, and Perspective
  // map the near clipping plane to -1 and the far clipping plane to +1.
  // In PreMultiply mode, you call this method before calling Ortho, Frustum,
  // or Perspective.  In PostMultiply mode you can call it after.

void vtkPerspectiveTransform::AdjustZBuffer(double oldZMin, double oldZMax, double newZMin, double newZMax)//把z值范围(-1,1)映射到(0,1)
{
  double matrix[4][4];
  vtkMatrix4x4::Identity(*matrix);

  matrix[2][2] = (newZMax - newZMin)/(oldZMax - oldZMin);
  matrix[2][3] = (newZMin*oldZMax - newZMax*oldZMin)/(oldZMax - oldZMin);

  this->Concatenate(*matrix);
}
/
// Create an orthogonal projection matrix and concatenate it by the current transformation. 
// The orthographic perspective maps  [xmin,xmax], [ymin,ymax], [-znear,-zfar] to  [-1,+1], [-1,+1], [-1,+1].
void vtkPerspectiveTransform::Ortho(double xmin, double xmax,double ymin, double ymax, double znear, double zfar)//此时znear,zfar为相机裁剪平面的距离
{
  double matrix[4][4];
  vtkMatrix4x4::Identity(*matrix);

  matrix[0][0] = 2/(xmax - xmin);
  matrix[1][1] = 2/(ymax - ymin);
  matrix[2][2] = -2/(zfar - znear);

  matrix[0][3] = -(xmin + xmax)/(xmax - xmin);
  matrix[1][3] = -(ymin + ymax)/(ymax - ymin);
  matrix[2][3] = -(znear + zfar)/(zfar - znear);

  this->Concatenate(*matrix);
}


(2)
  // Description:
  // For backward compatibility. Use GetModelViewTransformMatrix() now.
  // Return the matrix of the view transform.
  // The ViewTransform depends on only three ivars:  the Position, the
  // FocalPoint, and the ViewUp vector.  All the other methods are there
  // simply for the sake of the users' convenience.
vtkMatrix4x4 *vtkCamera::GetViewTransformMatrix()
{
  return this->GetModelViewTransformMatrix();
}



(2)下边解剖ViewtoDisplay()函数
//----------------------------------------------------------------------------
// Convert view coordinates to display coordinates.
void vtkViewport::ViewToDisplay()
{
  if ( this->VTKWindow )
    {
    double dx,dy;
    int sizex,sizey;
    int *size = NULL;

    /* get physical window dimensions */
    size = this->VTKWindow->GetSize();
    if (!size)
      {
      return;
      }
    sizex = size[0];
    sizey = size[1];

    dx = (this->ViewPoint[0] + 1.0) *
      (sizex*(this->Viewport[2]-this->Viewport[0])) / 2.0 +
        sizex*this->Viewport[0];
    dy = (this->ViewPoint[1] + 1.0) *
      (sizey*(this->Viewport[3]-this->Viewport[1])) / 2.0 +
        sizey*this->Viewport[1];

    this->SetDisplayPoint(dx,dy,this->ViewPoint[2]);
    }
}
加黑的部分其实就是一个线性插值的过程
假设display坐标系下x坐标为dis,view坐标系下的坐标为view,则有下列关系:
(view-(-1))/(1-(-1))=(dis-sizex*viewport[0])/sizex*(viewport[2]-viewport[0]);
经过变换即可得到上述 的dx值,同理dy也可以这样得到

你可能感兴趣的:(VTK)