约束视图截锥体的平面方程:

int main(int, char* [])
{
    vtkSmartPointer<vtkNamedColors> colors =
            vtkSmartPointer<vtkNamedColors>::New();

    vtkSmartPointer<vtkPlanes> planes =
            vtkSmartPointer<vtkPlanes>::New();

    // Using frustum planess.
    vtkSmartPointer<vtkCamera> camera =
            vtkSmartPointer<vtkCamera>::New();
    double planesArray[24];
    // 得到约束视图截锥体的平面方程。
    camera->GetFrustumPlanes( 1, planesArray );
    planes->SetFrustumPlanes( planesArray );

    vtkSmartPointer<vtkTextProperty> textProperty =
            vtkSmartPointer<vtkTextProperty>::New();
    textProperty->SetFontSize(16);
    textProperty->SetJustificationToCentered();

    // Create the render window and interactor.
    vtkSmartPointer<vtkRenderWindow> renWin =
            vtkSmartPointer<vtkRenderWindow>::New();
    renWin->SetWindowName("Planes");
    vtkSmartPointer<vtkRenderWindowInteractor> iRen =
            vtkSmartPointer<vtkRenderWindowInteractor>::New();
    iRen->SetRenderWindow(renWin);

    vtkSmartPointer<vtkHull> hull =
            vtkSmartPointer<vtkHull>::New();
    vtkSmartPointer<vtkPolyData> pd =
            vtkSmartPointer<vtkPolyData>::New();
    vtkSmartPointer<vtkPolyDataMapper> mapper =
            vtkSmartPointer<vtkPolyDataMapper>::New();
    vtkSmartPointer<vtkActor> actor =
            vtkSmartPointer<vtkActor>::New();
    vtkSmartPointer<vtkTextMapper> textMapper =
            vtkSmartPointer<vtkTextMapper>::New();
    vtkSmartPointer<vtkActor2D> textActor =
            vtkSmartPointer<vtkActor2D>::New();
    vtkSmartPointer<vtkRenderer> renderer =
            vtkSmartPointer<vtkRenderer>::New();

    hull->SetPlanes( planes );
    hull->GenerateHull( pd, -300, 300, -300, 300, -300, 300 );
    mapper->SetInputData( pd );
    actor->SetMapper( mapper );
    textMapper->SetInput( actor->GetClassName() );
    textMapper->SetTextProperty( textProperty );
    textActor->SetMapper( textMapper );
    textActor->SetPosition( 120, 16 );
    renderer->AddActor( actor );
    renderer->AddViewProp( textActor );
    renWin->AddRenderer( renderer );

    iRen->Initialize();
    renWin->Render();
    iRen->Start();

    return EXIT_SUCCESS;
}


如果我们将hull->GenerateHull( pd, -200, 200, -200, 200, -200, 200 );中的200绝对值改成50,那么有:

底部的面和其他的四个面分离了。
改成300的结果:

改成150的结果:

我们从相机那里得到了FrustumPlanes的6个平面方程,但是,( x, y, z )的范围是我们设定的,当范围过小的话,平面就只有一部分了。
故容易出现那种断开的场景。
GetFrustumPlanes的源码:

//----------------------------------------------------------------------------
// Return the 6 planes (Ax + By + Cz + D = 0) that bound
// the view frustum.
void vtkCamera::GetFrustumPlanes(double aspect, double planes[24])
{
  int i;
  double f, normals[6][4], matrix[4][4];

  // set up the normals
  for (i = 0; i < 6; i++)
  {
    normals[i][0] = 0.0;
    normals[i][1] = 0.0;
    normals[i][2] = 0.0;
    normals[i][3] = 1.0;
    // if i is even set to -1, if odd set to +1
    normals[i][i/2] = 1 - (i%2)*2;
  }

  // get the composite perspective matrix
  vtkMatrix4x4::DeepCopy(
    *matrix,
    this->GetCompositeProjectionTransformMatrix(aspect,-1,+1));

  // transpose the matrix for use with normals
  vtkMatrix4x4::Transpose(*matrix,*matrix);

  // transform the normals to world coordinates
  for (i = 0; i < 6; i++)
  {
    vtkMatrix4x4::MultiplyPoint(*matrix,normals[i],normals[i]);

    f = 1.0/sqrt(normals[i][0]*normals[i][0] +
                 normals[i][1]*normals[i][1] +
                 normals[i][2]*normals[i][2]);

    planes[4*i + 0] = normals[i][0]*f;
    planes[4*i + 1] = normals[i][1]*f;
    planes[4*i + 2] = normals[i][2]*f;
    planes[4*i + 3] = normals[i][3]*f;
  }
}

一个平面表示成

Ax+By+Cz+D=0

,四个参数一旦确定,整个平面也就确定了。
查看GenerateHull的源码实现:

void vtkHull::GenerateHull(vtkPolyData *pd, double *bounds)
{
  vtkPoints      *newPoints;
  vtkCellArray   *newPolys;

  // There should be at least four planes for this to work. There will need
  // to be more planes than four if any of them are parallel.
  if ( this->NumberOfPlanes < 4 )
  {
    vtkErrorMacro( << "There must be >= 4 planes!!!" );
    return;
  }

  // Create a new set of points and polygons into which the results will
  // be stored
  newPoints = vtkPoints::New();
  newPoints->Allocate(this->NumberOfPlanes*3);
  newPolys  = vtkCellArray::New();
  newPolys->Allocate(newPolys->EstimateSize(this->NumberOfPlanes,3));

  this->ClipPolygonsFromPlanes( newPoints, newPolys, bounds );

  pd->SetPoints(newPoints);
  pd->SetPolys(newPolys);
  newPoints->Delete();
  newPolys->Delete();

  pd->Squeeze();
}

vtkPolyData是由newPoints和newPolys生成的。
假设有平面

3x+2y+4Z+D=0

( A=3, B=2,C=4 ),那么我们可以得到平面的“样子”,但还确定不了平面的“位置”:

研究研究camera得到的截锥体平面,增加打印:

double planesArray[24];
// 得到约束视图截锥体的平面方程。
camera->GetFrustumPlanes( 1, planesArray );
planes->SetFrustumPlanes( planesArray );
std::cout << "planes->GetNumberOfPlanes(): " << planes->GetNumberOfPlanes() << std::endl;
printf( "(%lf, %lf, %lf, %lf)\n", planesArray[0], planesArray[1], planesArray[2], planesArray[3] );
printf( "(%lf, %lf, %lf, %lf)\n", planesArray[4], planesArray[5], planesArray[6], planesArray[7] );
printf( "(%lf, %lf, %lf, %lf)\n", planesArray[8], planesArray[9], planesArray[10], planesArray[11] );
printf( "(%lf, %lf, %lf, %lf)\n", planesArray[12], planesArray[13], planesArray[14], planesArray[15] );
printf( "(%lf, %lf, %lf, %lf)\n", planesArray[16], planesArray[17], planesArray[18], planesArray[19] );
printf( "(%lf, %lf, %lf, %lf)\n", planesArray[20], planesArray[21], planesArray[22], planesArray[23] );

double *origin = NULL;
origin = planes->GetPlane( 0 )->GetOrigin();
printf( "origin: (%lf, %lf, %lf)\n", origin[0], origin[1], origin[2] );
origin = planes->GetPlane( 1 )->GetOrigin();
printf( "origin: (%lf, %lf, %lf)\n", origin[0], origin[1], origin[2] );
origin = planes->GetPlane( 2 )->GetOrigin();
printf( "origin: (%lf, %lf, %lf)\n", origin[0], origin[1], origin[2] );
origin = planes->GetPlane( 3 )->GetOrigin();
printf( "origin: (%lf, %lf, %lf)\n", origin[0], origin[1], origin[2] );
origin = planes->GetPlane( 4 )->GetOrigin();
printf( "origin: (%lf, %lf, %lf)\n", origin[0], origin[1], origin[2] );
origin = planes->GetPlane( 5 )->GetOrigin();
printf( "origin: (%lf, %lf, %lf)\n", origin[0], origin[1], origin[2] );

得到:

planes->GetNumberOfPlanes(): 6
(0.965926, 0.000000, -0.258819, 0.258819)
(-0.965926, 0.000000, -0.258819, 0.258819)
(0.000000, 0.965926, -0.258819, 0.258819)
(0.000000, -0.965926, -0.258819, 0.258819)
(0.000000, 0.000000, -1.000000, 0.990000)
(0.000000, 0.000000, 1.000000, 999.010000)
origin: (-0.267949, 0.000000, 0.000000)
origin: (0.267949, 0.000000, 0.000000)
origin: (0.000000, -0.267949, 0.000000)
origin: (0.000000, 0.267949, 0.000000)
origin: (0.000000, 0.000000, 0.990000)
origin: (0.000000, 0.000000, -999.010010)

接着,我们增加自己的InteractorStyle,检测鼠标点击处对应的world坐标。
MyStyle.h

#include <vtkInteractorStyleTrackballCamera.h>
class MyStyle : public vtkInteractorStyleTrackballCamera
{
public:
    static MyStyle* New();
    virtual void OnLeftButtonDown();
};

MyStyle.cpp

#include "MyStyle.h"
#include <vtkInteractorObserver.h>
#include <vtkCellPicker.h>
#include <vtkSmartPointer.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindowInteractor.h>

vtkStandardNewMacro(MyStyle);

void MyStyle::OnLeftButtonDown()
{
    // Get the location of the click (in window coordinates)
    int* pos = this->GetInteractor()->GetEventPosition();

    vtkSmartPointer<vtkCellPicker> picker =
      vtkSmartPointer<vtkCellPicker>::New();
    picker->SetTolerance(0.0005);

    // Pick from this location.
    picker->Pick(pos[0], pos[1], 0, this->GetDefaultRenderer());
    double* worldPosition = picker->GetPickPosition();

    printf( "click: %lf %lf %lf\n",  worldPosition[0], worldPosition[1], worldPosition[2] );
    vtkInteractorStyleTrackballCamera::OnLeftButtonDown();
}

为了避免输出缓存,在main.cpp中加入:

setbuf(stdout, NULL);

通过打印输出,可以发现底部的平面就是 origin: (0.000000, 0.000000, -999.010010) 的第六个平面。
那么剩下的5个平面等式怎么就只对应4个平面呢?
测试:

planesArray[0] = planesArray[1] = planesArray[2] = planesArray[3] = 0;

planesArray[4] = planesArray[5] = planesArray[6] = planesArray[7] = 0;

planesArray[8] = planesArray[9] = planesArray[10] = planesArray[11] = 0;

planesArray[12] = planesArray[13] = planesArray[14] = planesArray[15] = 0;

planesArray[16] = planesArray[17] = planesArray[18] = planesArray[19] = 0;

planesArray[20] = planesArray[21] = planesArray[22] = planesArray[23] = 0;


貌似,第五个的平面方程丢失的话影响不大。

不过,系数全都赋值0的话,他们都会得到这样的错误输出:

ERROR: In /Users/weiyang/Downloads/VTK-8.1.1/Filters/Core/vtkHull.cxx, line 67
vtkHull (0x7fc2e362a6e0): Zero length vector not allowed for plane normal!

工程代码地址:
https://github.com/theArcticOcean/CLib/tree/master/VTKLearn/FrustumPlane

分类: C plus plus

发表评论

电子邮件地址不会被公开。 必填项已用*标注