这个专栏是用于记录我在学习VTK过程中的一些心得体会。参考的资料主要有以下三个:
1. 张晓东 罗火灵《VTK图形图像开发进阶》
2. https://examples.vtk.org/site/
3. 沈子恒 《VTK 三维数据渲染进阶》
遇到的一个大问题就是由于版本更新,这些资料中很多代码无法正常运行,需要进行一定的修改,所以这个专栏会记录下来我修改后的程序代码,以便于我之后温习。也希望能给和我有同样困扰的小伙伴们一些帮助。
我使用的版本:VTK9 + VS2022
在上一篇中我介绍了一个绘制棱柱的程序代码,并通过这个程序代码引出了VTK的流水线设计。在这一篇文章中,我将详细介绍VTK的流水线设计。
先复习一下上一篇的程序:
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
///
#include "vtkCylinderSource.h"
#include "vtkPolyDataMapper.h"
#include "vtkActor.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkCamera.h"
int main()
{
//Visualize Pipeline : Source->Mapper->Actor
//
vtkCylinderSource * cylinder = vtkCylinderSource::New();
cylinder->SetHeight(3.0);
cylinder->SetRadius(1.0);
cylinder->SetResolution(8);
vtkPolyDataMapper* cylinderMapper = vtkPolyDataMapper::New();
cylinderMapper->SetInputConnection(cylinder->GetOutputPort());
vtkActor* cylinderActor = vtkActor::New();
cylinderActor->SetMapper(cylinderMapper);
///Actor property
cylinderActor->GetProperty()->SetColor(1.0, 0.32, 0.27);
cylinderActor->RotateX(30.0);
cylinderActor->RotateY(-45);
//Render Engine:Renderer->RenderWindow->RenderWindowInteractor
//
vtkRenderer* cylinderRenderer = vtkRenderer::New();
vtkRenderWindow* cylinderRenderWindow = vtkRenderWindow::New();
vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::New();
cylinderRenderWindow->AddRenderer(cylinderRenderer);
iren->SetRenderWindow(cylinderRenderWindow);
//Visualize Pipeline -> Render Engine
//
cylinderRenderer->AddActor(cylinderActor);
cylinderRenderer->SetBackground(0.1, 0.2, 0.4); // render background property
cylinderRenderWindow->SetSize(400, 400); //render window size
//Camera
// The renderer renders into the
// render window. The render window interactor captures mouse events
// and will perform appropriate camera or actor manipulation
// depending on the nature of the events.
cylinderRenderer->ResetCamera();
cylinderRenderer->GetActiveCamera()->Zoom(1.5); //focus
//Window Hold On
iren->Start();
//Clear Memory
iren->Delete();
cylinderRenderWindow->Delete();
cylinderRenderer->Delete();
cylinderActor->Delete();
cylinderMapper->Delete();
cylinder->Delete();
return 0;
}
1.VTK的流水线设计
一般来说,VTK的流水线是这样子的:
Source->Filter->Mapper->Actor
而我们在VTK的绘制代码可以大概分为以下10部分:
1. create procedural geometry; (构造数据源)
2. create a mapper;
3. give the geometry to the mapper; (生成VTK数据结构)
4. create an actor;(构造演员)
5. give the mapper to the actor; (给演员化妆)
6. create a renderer;(布置舞台)
7. create a window; (装饰剧院)
8. give the renderer to the window;(搭建舞台)
9. give the actor to the renderer; (演员上台)
10. window->render();
看上去好像步骤挺多的,实际上每一部分都是固定的套路,先定义,然后设置相关参数。比如create procedural geometry这里,首先
vtkCylinderSource* cylinder = vtkCylinderSource::New();
然后修改相关参数
cylinder->SetHeight(3.0);
cylinder->SetRadius(1.0);
cylinder->SetResolution(8);
2. 三维场景的基本要素
光照和相机是三维渲染场景必备的因素,如果没有指定,vtkRenderer会自动地创建默认的光照和相机。
灯光
VTK里用类vtkLight来表示渲染场景中的光照。VTK中的vtkLight实例可以打开、关闭,设置光照的颜色,照射位置(即焦点),光照所在的位置,强度等等。vtkLight可以分为位置光照(Positional Light,即聚光灯)和方向光照(Direction Light)。位置光照是光源位置在渲染场景中的某个位置,可以指定光照的衰减值、锥角等;方向光照即光源位置在无穷远,光线可以认为是平行的,比如自然界中的太阳光。光源的位置和焦点的连线定义光线的方向,默认的vtkLight即为方向光照。
vtkLight常的方法有:
SetColor() — 设置光照的颜色,以RGB的形式指定颜色。
SetPosition() — 设置光照位置。
SetFocalPoint() — 设置光照焦点。
SetIntensity() — 设置光照的强度。
SetSwitch() / SwitchOn()/ SwitchOff()— 打开或关闭对应的光照。
下面的示例简单展示了vtkLight的使用方法。这里定义了两个vtkLight对象,一个是绿色光,,位置在(0,0,1)一个是蓝色光,位置在(0,0,-1)。两个对象的焦点都是对着相机的焦点。
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkCylinderSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkLight.h>
#include <vtkCamera.h>
#include <vtkProperty.h>
/*
( - 6, 0, 0)blue light(6, 0, 0)green light--> X Axis
(0, -6, 0)blue light(0, 6, 0)green light--> Y Axis
(0, 0, -6)blue light(0, 0, 6)green light-->Z Axis
*/
int main()
{
vtkSmartPointer<vtkCylinderSource> cylinder =
vtkSmartPointer<vtkCylinderSource>::New();
cylinder->SetHeight(3.0);
cylinder->SetRadius(1.0);
cylinder->SetResolution(10);
vtkSmartPointer<vtkPolyDataMapper> cylinderMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
cylinderMapper->SetInputConnection(cylinder->GetOutputPort());
vtkSmartPointer<vtkActor> cylinderActor =
vtkSmartPointer<vtkActor>::New();
cylinderActor->SetMapper(cylinderMapper);
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(cylinderActor);
renderer->SetBackground(1.0, 0, 0);
vtkSmartPointer<vtkRenderWindow> renWin =
vtkSmartPointer<vtkRenderWindow>::New();
renWin->AddRenderer(renderer);
renWin->SetSize(640, 480);
renWin->Render();
renWin->SetWindowName("RenderCylinder-Lights");
vtkSmartPointer<vtkRenderWindowInteractor> iren =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
iren->SetRenderWindow(renWin);
vtkSmartPointer<vtkInteractorStyleTrackballCamera> style =
vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
iren->SetInteractorStyle(style);
vtkSmartPointer<vtkLight> myLight =
vtkSmartPointer<vtkLight>::New();
myLight->SetColor(0, 1, 0);
myLight->SetPosition(0, 0, 6);
myLight->SetFocalPoint(
renderer->GetActiveCamera()->GetFocalPoint());
renderer->AddLight(myLight);
vtkSmartPointer<vtkLight> myLight2 =
vtkSmartPointer<vtkLight>::New();
myLight2->SetColor(0, 0, 1);
myLight2->SetPosition(0, 0, -6);
myLight2->SetFocalPoint(
renderer->GetActiveCamera()->GetFocalPoint());
renderer->AddLight(myLight2);
//camera setting
//vtkSmartPointer<vtkCamera> mycamera =
// vtkSmartPointer<vtkCamera>::New();
//mycamera->SetFocalPoint(0, 0, 0);
//mycamera->SetPosition(-5, 0, 0);
//mycamera->SetClippingRange(5.5, 6.5);
//mycamera->ComputeViewPlaneNormal();
//mycamera->SetViewUp(0, 1, 0);
//renderer->SetActiveCamera(mycamera);
//vtkSmartPointer<vtkCamera> mycamera =
// vtkSmartPointer<vtkCamera>::New();
//mycamera->SetFocalPoint(0, 0, 0);
//mycamera->SetPosition(-10, 0, 0);
//mycamera->SetClippingRange(10.5, 11.5);
//mycamera->ComputeViewPlaneNormal();
//mycamera->SetViewUp(0, 1, 0);
//renderer->SetActiveCamera(mycamera);
vtkSmartPointer<vtkCamera> mycamera =
vtkSmartPointer<vtkCamera>::New();
mycamera->SetFocalPoint(0, 0, 0);
mycamera->SetPosition(-10, 0, 0);
mycamera->SetClippingRange(5.0, 9.0);
mycamera->ComputeViewPlaneNormal();
mycamera->SetViewUp(0, 1, 0);
renderer->SetActiveCamera(mycamera);
iren->Initialize();
iren->Start();
return EXIT_SUCCESS;
}
运行结果:
light
相机
观众的眼睛就好比三维渲染场景中的相机,VTK则是用vtkCamera类来表示三维渲染场景中的相机。vtkCamera负责把三维场景投影到二维平面,如屏幕、图像等。
从上图可以看出,与相机投影相关的因素主要有:
相机位置:即相机所在的位置,用方法vtkCamera::SetPosition()设置。
相机焦点:用方法vtkCamera::SetFocusPoint()设置, 默认的焦点位置在世界坐标系的原点。
朝上方向:即哪个方向为相机朝上的方向。就好比我们直立看东西,方向为头朝上,看到的东西也是直立的,如果我们倒立看某个东西,这时方向为头朝下,看到的东西当然就是倒立的。相机位置、相机焦点和朝上方向三个因素确定了相机的实际方向,即确定相机的视图。
投影方向:相机位置到相机焦点的向量方向即为投影方向。
投影方法:确定Actor是如何映射到像平面的。vtkCamera定义了两种投影方法,一种是正交投影(OrthographicProjection),也叫平行投影(Parallel Projection),即进入相机的光线与投影方向是平行的。另一种是透视投影(PerspectiveProjection),即所有的光线相交于一点。
视角:透视投影时需要指定相机的视角(View Angle),默认的视角大小为30º,可以用方法vtkCamera::SetViewAngle()设置。
前后裁剪平面:裁剪平面与投影方向相交,一般与投影方向也是垂直的。裁剪平面主要用于评估Actor与相机距离的远近,只有在前后裁剪平面之间的Actor才是可见的。裁剪平面的位置可以用方法vtkCamera::SetClippingRange()设置。
在前文灯光章节的代码中,我已经加入了camera的生成与设置:
vtkSmartPointer<vtkCamera> mycamera =
vtkSmartPointer<vtkCamera>::New();
mycamera->SetFocalPoint(0, 0, 0);
mycamera->SetPosition(-10, 0, 0);
mycamera->SetClippingRange(5.0, 9.0);
mycamera->ComputeViewPlaneNormal();
mycamera->SetViewUp(0, 1, 0);
renderer->SetActiveCamera(mycamera);
大家可以根据自己的需求调整相应的参数并进行试验。
颜色
颜色是Actor的重要属性之一。VTK主要采用RGB和HSV两种颜色系统来描述颜色。
RGB 颜色系统由三个颜色分量:红色(R)、绿色(G)和蓝色(B)的组合表示,在VTK 里,这三个分量的取值范围都是 0~1,(0,0,0)表示黑色,(,1,1)表示白色。vtkProperty:SeColor(r, g,b)采用的就是 RGB 颜色系统设置颜色属性值。
HSV颜色系统同样也是由三个分量来决定颜色,它们分别是:色相(Fue),表示颜色的基本属性,就是迺常所说的颜色名称,如红色、黄色等;饱和度 (Saturation),是指颜色的纯度,其值越高则越纯;值(Value),也就是强度 Intensity 或者亮度 Bright,值为0通常表示的是黑色,值为 1表示的是最亮的颜色。这三个分量的取值范围 0~1。类vtkLookupTable提供了 HSV颜色系统设置的方法。
常见的颜色RGB和HSV值对照表:
颜色 | RGB | HSV |
---|---|---|
黑色 | 0,0,0 | *,*,0 |
白色 | 1,1,1 | *,0,1 |
红色 | 1,0,0 | 0,1,1 |
绿色 | 0,1,0 | 1/3,1,1 |
蓝色 | 0,0,1 | 2/3,1,1 |
黄色 | 1,1,0 | 1/6,1,1 |
蓝绿色 | 0,1,1 | 1/2,1,1 |
品红色 | 1,0,1 | 5/6,1,1 |
天蓝色 | 1/2,1/2,1 | 2/3,1/2,1 |