Introducción a VTK





VTK es una biblioteca open source para visualización. Corre en Windows, Linux, Solaris, Irix y HP entre otras plataformas.

El código fuente y la documentación pueden bajarse de:

http://www.kitware.com/vtk.html.


En su mayoría, los paquetes comerciales requieren licencia, pago, y no se distribuyen con el código abierto.

VTK en cambio es gratuita, de código abierto, y no requiere licencia (excepto algunos métodos patentados), tiene soporte on-line, documentación, newslist, etc.

Cuidado que VTK es una librería y no un paquete (uno debe desarrollar sus propias aplicaciones).



Cómo utilizar VTK.

Usando sus clases desde C++.

Por medio de scripting desde Java, Tcl, Python.

Esta forma de trabajar tiene la ventaja de poder realizar prototipos rápidos. Desde Java se pueden realizar aplicaciones portables y que funcionan en red.

Para utilizar VTK desde aplicaciones escritas en C++ se requieren los headers y las bibliotecas de VTK.

Las clases están agrupadas en Common, Graphics, Imaging, Patented y Contrib.



Primer ejemplo.

#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkConeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindowInteractor.h>

int main ()
{
 vtkActor*coneActor    = vtkActor::New();
 vtkRenderer*renderer     = vtkRenderer::New();
 vtkConeSource*coneGeometry = vtkConeSource::New();
 vtkRenderWindow*renderWindow = vtkRenderWindow::New();
 vtkPolyDataMapper*coneMapper   = vtkPolyDataMapper::New();
 vtkRenderWindowInteractor *interactor  = vtkRenderWindowInteractor::New();

 renderer->SetViewport( 0.0, 0.0, 1.0, 1.0 );
 renderer->SetBackground( 0.8, 0.4, 0.2 );
 renderWindow->SetSize( 500,500 );
 coneGeometry->SetResolution( 80 );

 coneMapper->SetInput( coneGeometry->GetOutput() );
 coneActor->SetMapper( coneMapper );
 renderer->AddActor( coneActor );
 renderWindow->AddRenderer( renderer );
 interactor->SetRenderWindow( renderWindow );
 renderWindow->Render();
 interactor->SetDesiredUpdateRate( 0.1 );
 interactor->Start();

 // No olvidar limpiar!
 coneGeometry->Delete();
 renderer->Delete();
 coneActor->Delete();
 coneMapper->Delete();
 renderWindow->Delete();
 interactor->Delete();
 return 0;

}




La dependencia funcional de cada objeto es la siguiente:



Un objeto de la clase vtkRenderWindow es el destinatario de las actividades desarrolladas por VTK. Su responsabilidad es mostrar las imágenes y refrescar los eventos, ocultando al usuario las idiosincracias de la plataforma. Una de estas ventanas puede recibir imágenes de varias fuentes al mismo tiempo (usualmente un vtkRenderer ).

El vtkRenderer es el encargado de renderizar una escena. Este objeto recorre las distintas fuentes de datos, computa los filtros y mapeos, obtiene las transformaciones de los actores, computa el modelo de iluminación, realiza el scan-line, etc.

El vtkActor es el responsable de manipular las transformaciones que mapean las fuentes de datos al renderizador.

El vtkMapper recibe los datos geométricos y de superficie de una fuente de datos y computa los colores en sus puntos. Existe una cantidad de mapeadores (mapeo de texturas, mapeo de valores funcionales, para 2D, para 3D, etc.)

El vtkRenderWindowInteractor responde a los movimientos del mouse, y a algunas teclas ("w" para wireframe, "s" para superficie, "e" para abandonar la aplicación, "r" para resetear la posición de la cámara). Se encarga de enviar los mensajes correspondientes a los demás objetos.


Otro ejemplo con dos viewports y dos actores.

#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkConeSource.h>
#include <vtkSphereSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindowInteractor.h>

int main ()
{
  int i;
  vtkActor          *coneActor      = vtkActor::New();
  vtkActor          *sphereActor    = vtkActor::New();
  vtkRenderer       *renderer1      = vtkRenderer::New();
  vtkRenderer       *renderer2      = vtkRenderer::New();
  vtkConeSource     *coneGeometry   = vtkConeSource::New();
  vtkSphereSource   *sphereGeometry = vtkSphereSource::New();
  vtkRenderWindow   *renderWindow   = vtkRenderWindow::New();
  vtkPolyDataMapper *coneMapper     = vtkPolyDataMapper::New();
  vtkPolyDataMapper *sphereMapper     = vtkPolyDataMapper::New();
  vtkRenderWindowInteractor *interactor =
                                vtkRenderWindowInteractor::New();

  renderWindow->SetSize(800,400);
  renderer1->SetViewport(0.0, 0.0, 0.5, 1.0);
  renderer1->SetBackground(0.5, 0.2, 0.0);
  renderer2->SetViewport(0.5, 0.0, 1.0, 1.0);
  renderer2->SetBackground(0.3, 0.3, 0.6);

  coneGeometry->SetResolution(35);
  coneMapper->SetInput( coneGeometry->GetOutput() );
  coneActor->SetMapper( coneMapper );

  sphereGeometry->SetThetaResolution(10);
  sphereGeometry->SetPhiResolution(10);
  sphereMapper->SetInput( sphereGeometry-> GetOutput() );
  sphereActor->SetMapper( sphereMapper );

  renderer1->AddActor( coneActor );
  renderer2->AddActor( coneActor );

  renderer1->AddActor( sphereActor );
  renderer2->AddActor( sphereActor );
  sphereActor->GetProperty()->SetColor(0.9, 0.8, 0.5);

  interactor->SetRenderWindow( renderWindow );
  renderWindow->AddRenderer( renderer1 );
  renderWindow->AddRenderer( renderer2 );

  renderer1->GetActiveCamera()->Roll(90);

  for(i=0; i<30; i++) {
   renderer2->GetActiveCamera()->Azimuth(3);
   coneActor->GetProperty()->SetColor(0.01*i,0.02*i,1-0.03*i);
   sphereActor->SetPosition(i*0.02,i*0.02,-i*0.03);
   renderWindow->Render();
   }

  interactor->SetDesiredUpdateRate( 20 );
  interactor->Start();

  coneGeometry->Delete();
  renderer1->Delete();
  renderer2->Delete();
  coneActor->Delete();
  coneMapper->Delete();
  renderWindow->Delete();
  interactor->Delete();

  return 0;
}



Ejemplo de rendering de volúmenes.



 
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkStructuredPointsReader.h"
#include "vtkPiecewiseFunction.h"
#include "vtkVolumeProperty.h"
#include "vtkVolumeRayCastCompositeFunction.h"
#include "vtkVolumeRayCastMapper.h"
#include "vtkVolume.h"


int main( int argc, char *argv[] )
{
  vtkRenderer                *renderer      = vtkRenderer::New();
  vtkRenderWindow            *renWindow     = vtkRenderWindow::New();
  vtkRenderWindowInteractor  *interactor    = vtkRenderWindowInteractor::New();
  vtkStructuredPointsReader  *reader        = vtkStructuredPointsReader::New();
  vtkPiecewiseFunction       *opacity       = vtkPiecewiseFunction::New();
  vtkColorTransferFunction   *color         = vtkColorTransferFunction::New();
  vtkVolumeProperty          *volumeProperty = vtkVolumeProperty::New();
  vtkVolumeRayCastCompositeFunction *compositeFunction = 
                   vtkVolumeRayCastCompositeFunction::New();
  vtkVolumeRayCastMapper     *volumeMapper  = vtkVolumeRayCastMapper::New();
  vtkVolume                  *volume        = vtkVolume::New();

  renWindow->SetSize(400,400);

  // leer la data de un archivo (provisto por la distribución)
  reader->SetFileName("ironProt.vtk");
  reader->Update();

  opacity->AddSegment(0, 0.0, 255, 0.5);
  color->AddRGBPoint(64, 1.0, 0.0, 0.0);
  color->AddRGBPoint(128, 1.0, 1.0, 0.0);
  color->AddRGBPoint(196, 0.0, 1.0, 1.0);

  volumeProperty->SetColor(color);
  volumeProperty->SetScalarOpacity(opacity);
  volumeProperty->SetInterpolationTypeToLinear();
  volumeProperty->ShadeOn();

  volumeMapper->SetInput(reader->GetOutput());
  volumeMapper->SetVolumeRayCastFunction(compositeFunction);

  volume->SetMapper(volumeMapper);
  volume->SetProperty(volumeProperty);

  renderer->SetBackground(0.2, 0.2, 0.8);
  renderer->AddVolume(volume);
  renderer->GetActiveCamera()->Azimuth(20.0);
  renderer->GetActiveCamera()->Dolly(1.60);
  renderer->ResetCameraClippingRange();

  interactor->SetRenderWindow(renWindow);
  renWindow->AddRenderer(renderer);
  renWindow->Render();
  interactor->Start();

  renderer->Delete();
  renWindow->Delete();
  interactor->Delete();
  reader->Delete();
  opacity->Delete();
  color->Delete();
  volumeProperty->Delete();
  compositeFunction->Delete();
  volumeMapper->Delete();
  volume->Delete();

  return 0;

}


La dependencia funcional de cada objeto es la siguiente:



En este ejemplo, se lee un archivo con datos 3D, el cual se utiliza para inicializar un objeto de la clase vtkStructuredPoints, el cual modela una grilla regular de valores escalares.

La asignación se realiza por medio de un objeto vtkStructuredPointsReader, el cual recibe el nombre del archivo y fuerza la lectura con el método update.
Los datos 3D son renderizados por medio de transparencias, por lo que es necesario definir una función de transferencia que relacione el color y absorción de cada celda en función del valor almacenado.

Un modelo para estas funciones es utilizar paletas definidas por medio de funciones lineales a trozos. La clase vtkPiecewiseFunction soporta este comportamiento, al poder ser definida por medio de segmentos.

La clase vtkColorTransferFunction soporta este mismo comportamiento para paletas.

En el ejemplo se utiliza un objeto de cada clase, para el color y la opacidad. Luego de ser definidos, son asignados como propiedades del volumen por medio de un objeto intermedio de la clase vtkVolumeProperty. Estas propiedades serán interpoladas trilinealmente durante el rendering.

La geometría del volumen de datos y el método de evaluación de la misma se ponen en conjunción con un objeto de la clase vtkVolumeMapper.

La salida de este objeto, junto con las propiedades del volumen, son asignadas a un objeto vtkVolume, el cual es finalmente asignado al renderizador.