VTK 动画:框架、流程与实现
- VTK 动画:框架、流程与实现
- vtkAnimationCue 和 vtkAnimationScene 简介
- vtkAnimationScene 类的重要方法
- vtkAnimationCue 类的重要方法
- VTK 动画的基本流程
- 实例:一个简单的 VTK 动画演示程序
VTK是一个非常强大的图形处理库,它不仅可以用于科学计算和可视化,还可以用于创建各种有趣的动画。
VTK 动画:框架、流程与实现
参考:https://www.bilibili.com/video/BV1wM411K7wV
vtkAnimationCue 和 vtkAnimationScene 简介
VTK提供了一个由vtkAnimationCue和vtkAnimationScene等类所组成的框架,支持动画场景的创建和回放。
vtkAnimationScene表示一个场景或者是动画场景的建立。动画场景通常是通过渲染一系列的帧,在渲染每一帧时改变某些可视化参数来建立的。所渲染的每一帧都关联一个动画时间,这个时间用来确定动画中每一帧的位置。基于不同的播放模式,动画时间在动画播放过程中是一个计数不断添加的简单变量。
vtkAnimationCue代表一个随时间变化/动画的实体。vtkAnimationCue实例本身并不知道与动画相关的参数是假设改变的。因此,用户必须从vtkAnimationCue中派生出子类或者使用观察者模式监听事件。来改变动画过程中须要改变的参数。
动画场景中的Cue实体都有一个开始时间(start-time)和结束时间(end-time)。动画回放时,当场景的动画时间是处于所指定的开始时间与结束时间的范围内时,Cue实体就会被激活。Cue实体一旦激活后,它本身会发出vtkCommand::StartAnimationCueEvent事件。而对于动画系列中的每一帧。则发出vtkCommand::AnimationCueTickEvent事件,当动画时间递增至Cue实体的结束时间时,会发出vtkCommand::EndAnimationCueEvent事件。
vtkAnimationScene 类的重要方法
下面列出了类vtkAnimationScene中一些比较重要的方法:
- SetStartTime()/SetEndTime():设置动画场景的开始时间和结束时间,它们就是动画回放时的时间范围。
- SetPlayMode():用于控制动画回放的模式,也就是动画时间是怎样改变的。有两种模式能够设置:
- SequenceMode(PLAYMODE_SEQUENCE):这样的模式下,每帧的动画时间添加(1/framerate)的间隔。直至达到所设置的EndTime。因此。所渲染的总帧数是固定的,与渲染每一帧时所用的时间的长短无关。
- RealTimeMode(PLAYMODE_REALTIME):这样的模式下,整个动画的执行时间大约是(EndTime-StartTime)秒,当中第n帧的执行时间是:第n-1帧的执行时间+渲染第n-1帧所用的时间。因此。所渲染的总帧数随着渲染每一帧所用时间的不同而不同。
- SetFrameRate():帧率是指单位时间内渲染的帧数。主要用于Sequence模式。
- AddCue():将一个vtkAnimationCue实例加入到场景中。
- RemoveCue():从场景中移除vtkAnimationCue实例。
- RemoveAllCue():从场景中移除所有的vtkAnimationCue实例。
- SetAnimationTime():指定某一帧的动画时间。
- GetAnimationTime():动画回放时,获取动画的时钟时间。
- SetLoop():设为true,动画循环播放。
- Play()/Stop():开始/结束动画。
vtkAnimationCue 类的重要方法
下面是vtkAnimationCue类中一些比较重要的方法:
- SetTimeMode():TimeMode定义了Cue实体的起始时间和结束时间是怎样指定的,有两种模式可选。
- Relative(TIMEMODE_RELATIVE):这样的模式下,动画场景中的Cue实体的时间是相对动画场景的开始时间来指定的。
- Normalized(TIMEMODE_NORMALIZED):这样的模式下,Cue实体的开始时间和结束时间的取值范围为[0, 1],其中0对应动画场景的开始,1对应结束。
- SetStartTime()/SetEndTime():这两个方法主要是当Cue实体被激活时,标识动画时间的范围。
- 当TimeMode取值为TIMEMODE_RELATIVE时,与动画场景的开始和结束时间有同样的单位。
- 当TimeMode取值为TIMEMODE_NORMALIZED时,Cue实体的开始时间和结束时间的取值范围为[0, 1],其中0对应动画场景的开始,1对应结束。
- GetAnimationTime():用于vtkCommand::AnimationCueTickEvent事件的处理。处理事件时。可用来确定动画场景中的当前帧。其值依赖于TimeMode。
- 当TimeMode取值为TIMEMODE_RELATIVE时,其值是Cue实体激活后的时间单位的倍数。
- 当TimeMode取值为TIMEMODE_NORMALIZED时,Cue实体的开始时间和结束时间的取值范围为[0, 1],其中0对应动画场景的开始,1对应结束。
- GetClockTime():该方法与vtkAnimationScene::GetAnimationTime()中的动画时钟时间相似,仅当处理vtkCommand::AnimationCueTickEvent事件时才有效。
- TickInternal (double currenttime, double deltatime, double clocktime):正如前面所提的。我们能够派生一个vtkAnimationCue子类来取代编写处理动画事件的函数。子类派生时,必须要重载该函数。当中该函数的参数分别相应GetAnimationTime(),GetDeltaTime()和GetClockTime()的返回值。
- StartCueInternal()/EndCueInternal():这两个方法可在子类中重载,主要做一些创建、清除等工作以及动画回放时控制Cue实体的开始和结束。用户也能够加入事件观察者,通过监听vtkCommand::StartAnimationCueEvent和vtkCommand::EndAnimationCueEvent事件来实现相似的功能。
VTK 动画的基本流程
- 公有继承 vtkAnimationCue 实现一个自己的 vtkAnimationCue 子类,实现动画功能
- 创建一个 vtkAnimationScene 渲染场景
- 创建一个或多个 vtkAnimationCue 自定义子类实例,对于类中需要操作的对象进行赋值
- 添加动画实例到动画场景中:scene->AddCue(cue)
- 开始动画:scene->Play()
- 结束动画:scene->Stop()
实例:一个简单的 VTK 动画演示程序
VTK 对象都是通过内部定义静态函数 New() 来生成,用 Delete() 方法删除,因此构造函数、析构函数都要定义为 protected 类型,而对与赋值运算符和拷贝构造函数定义为 private 类型,只做声明不用实现。
两个宏:
vtkTypeMacro 是 VTK 的一个父子继承类型宏,用来追踪父子关系。借助该宏,不需要重新实现 vtkAnimationCue 的基本函数(虚函数)。
vtkStandardNewMacro 是 VTK 的一个宏,作用是定义一个通用的 New() 函数,使用时需要将实际的类名传递给它,确保你创建的对象是实际类的实例,而不是基类的实例。
完整程序:
#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkSphereSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkProperty.h>
#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
#include <vtkAnimationScene.h>
#include <vtkAnimationCue.h>
class vtkCustomAnimationCue : public vtkAnimationCue
{
protected:
vtkCustomAnimationCue() : RenWin(nullptr), Sphere(nullptr) {}
virtual void TickInternal(double currenttime, double deltatime, double clocktime)
{
double new_st = currenttime * 180;
this->Sphere->SetStartTheta(new_st);
this->RenWin->Render();
}
public:
static vtkCustomAnimationCue* New();
vtkTypeMacro(vtkCustomAnimationCue, vtkAnimationCue);
vtkRenderWindow* RenWin;
vtkSphereSource* Sphere;
};
vtkStandardNewMacro(vtkCustomAnimationCue);
int main()
{
// 新建一个 Source 数据源对象
vtkSmartPointer<vtkSphereSource> sphere = nullptr;
sphere = vtkSmartPointer<vtkSphereSource>::New();
// 设置属性
sphere->SetPhiResolution(30); // 设置纬度方向上的点数,默认值为 8
sphere->SetThetaResolution(30); // 设置经度方向上的点数,默认值为 8
// 新建一个 Mapper 映射器对象
vtkSmartPointer<vtkPolyDataMapper> sphereMapper = nullptr;
sphereMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
// 接受 cylinder 的输出,将数据映射为几何元素
sphereMapper->SetInputConnection(sphere->GetOutputPort());
// 新建一个 Actor 演示对象
vtkSmartPointer<vtkActor> sphereActor = nullptr;
sphereActor = vtkSmartPointer<vtkActor>::New();
// vtkActor 派生自 vtkProp 类,渲染场景中数据的可视化表达是通过 vtkProp 的子类负责的
// vtkProp 子类负责确定渲染场景中对象的位置、大小和方向信息
sphereActor->SetMapper(sphereMapper);
sphereActor->GetProperty()->SetColor(0.0, 0.0, 1.0);
// 创建一个 Renderer 渲染器对象,负责管理场景的渲染过程
vtkSmartPointer<vtkRenderer> renderer = nullptr;
renderer = vtkSmartPointer<vtkRenderer>::New();
// 添加 vtkProp 类型的对象到渲染场景中
renderer->AddActor(sphereActor);
// 设置渲染场景的背景颜色
renderer->SetBackground(1.0, 1.0, 1.0); // R、G、B,全 0 为黑色,全 1 为白色
// 创建一个 Window 窗口对象,负责本地计算机系统中窗口创建和渲染过程管理
vtkSmartPointer<vtkRenderWindow> window = nullptr;
window = vtkSmartPointer<vtkRenderWindow>::New();
window->AddRenderer(renderer);
window->SetSize(640, 480); // 设置窗口大小
window->Render();
window->SetWindowName("Sphere");
/*
// 新建一个 Interactor 交互器对象,提供平台独立的响应鼠标、键盘和时钟事件的交互机制
vtkSmartPointer<vtkRenderWindowInteractor> interactor = nullptr;
interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
// 设置渲染窗口,消息是通过渲染窗口捕获到的,所以必须要给交互器对象设置渲染窗口
interactor->SetRenderWindow(window);
// 新建一个交互器样式对象,该样式下,用户通过控制相机对物体作旋转、放大、缩小等操作
vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = nullptr;
style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
// 定义交互器样式,默认的交互样式为 vtkInteractorStyleSwitch
interactor->SetInteractorStyle(style);
interactor->Initialize();
interactor->Start();
*/
vtkAnimationScene* scene = vtkAnimationScene::New(); // 创建动画场景
//scene->SetModeToRealTime(); // 设置实时播放模式
scene->SetModeToSequence(); // 设置顺序播放模式
scene->SetFrameRate(30); // 设置帧率,单位时间内渲染的帧数
scene->SetStartTime(0); // 动画开始时间
scene->SetEndTime(60); // 动画结束时间
vtkCustomAnimationCue* cue = vtkCustomAnimationCue::New(); // 创建动画实例
// 对于类中需要操作的对象进行赋值
cue->Sphere = sphere;
cue->RenWin = window;
cue->SetTimeModeToNormalized(); // 按照场景时间标准化实例的动画时间,0 对应动画场景的开始,1 对应结束
cue->SetStartTime(.0);
cue->SetEndTime(1.0);
scene->AddCue(cue); // 添加动画实例到动画场景中
//scene->SetLoop(1); // 设置循环播放模式
scene->Play(); // 动画场景开始播放
scene->Stop(); // 结束播放
// 使用了智能指针,不需要手动释放内存
//style->Delete();
//interactor->Delete();
//window->Delete();
//renderer->Delete();
//cylinderActor->Delete();
//cylinderMapper->Delete();
//cylinder->Delete();
scene->Delete();
cue->Delete();
return EXIT_SUCCESS;
}
运行结果:
球的开口从0增大到180度,动画结束。