VTK 体绘制之vtkVolume

发布时间 2023-06-25 13:22:34作者: 一杯清酒邀明月

基本概念
  vtkVolume类似于几何渲染中的vtkActor,用于表示渲染场景中的对象。除了存储基本的变换信息(平移、旋转、放缩等),其内部还存储了两个重要对象。这两个对象分别是vtkAbstactVolumeMapper对象和    vtkVolumeProperty对象。相应的函数如下。
1)void SetMapper(vtkAbstractVolumeMapper* mapper)。该函数用于连接vtkAbstractMapper对象,并根据不同的体绘制算法获取其内部生产的图元数据。
2)void SetProperty(vtkVolumeProperty* property)。该函数用于设置vtkVolumeProperty对象。其中vtkVolumeProperty用来设置体绘制的颜色和不透明函数以及阴影等信息。

在体绘制中,颜色以及不透明度的设置至关重要,决定了最终的显示效果。这里简单介绍在vtkVolumeProperty中设置不透明度以及阴影。

不透明度传输函数
  不透明度传输函数是一个分段线性标量映射函数,利用该函数可将光线投影过程中的采样点灰度值映射为不同的不透明度值,已决定最终颜色值。一个标准的不透明度设置代码如下:

1 //添加灰度不透明度属性
2 vtkSmartPointer<vtkPiecewiseFunction> compositeOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
3 compositeOpacity->AddPoint(70, 0.0);
4 compositeOpacity->AddPoint(90, 0.4);
5 compositeOpacity->AddPoint(180, 0.6);
6 volumeProperty->SetScalarOpacity(compositeOpacity);

  上面代码中设置了三个不透明度断点(70,0.00)、(90,0.40)、(180,0.60)。其意义是,当灰度值小于70时,不透明度值映射为0;当灰度值介于70~90时,通过线性映射到0.0~0.40之间的一个值;当灰度值介于90~180时,现行映射至0.40~0.60;当灰度值大于180度时,不透明度映射到0.60~1.00的一个值。如果图像的灰度范围为0~255,那么上述代码利用三个断点将整个灰度范围分为四段处理。vtkVolumeProperty类中通过如下函数设置和获取不透明度函数:

1 void SetScalarOpacity(vtkPiecewiseFuntion* function);
2 vtkPiecewiseFunction*  GetScalarOpacity(int Index)

梯度不透明度函数
  梯度不透明度函数是将梯度模值映射为一个不透明度乘子,从而增强过渡区域的显示效果。该函数也是使用vtkPiecewiseFunction类。例如,在不同材料的临界区域,如空气到软组织,或者软组织到骨头的临界区,梯度值会比较大,而材料的内部梯度值则会相对比较小。一个标准的梯度不透明度函数代码如下:

1 vtkSmartPointer<vtkPiecewiseFunction> gradientOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
2 gradientOpacity->AddPoint(10, 0.0);
3 gradientOpacity->AddPoint(90, 0.5);
4 gradientOpacity->AddPoint(100, 1.0);

  上述代码中将梯度小于10的点的不透明度乘子设为0,即完全透明。当梯度大小为10时,不透明度为0;梯度大小为90时,不透明度乘子为0.5;梯度大小在10~90时,不透明度乘子通过线性映射至0~0.5之间的数值;同理,当梯度大小在90~100之间时,不透明度乘子通过线性映射至0.5~1.0之间;梯度值大于100时,其不透明度乘子为1.0。vtkVolumeProperty中通过如下函数设置和获取梯度不透明度函数:

void SetGradientOpacity(vtkPiecewiseFunction* function);
vtkPiecewiseFunction* GetGradientOpacity();

颜色传输函数
  颜色传输函数与不透明度传输函数的使用类似,二者的不同之处在于颜色传输函数是将一个标量值映射为颜色值。这个颜色值可以是RGB值,也可以是HSV值。VTK中颜色传输函数采用vtkColorTransferFunction类实现。一个标准的颜色传输函数代码如下:

1 vtkSmartPointer<vtkColorTransferFunction> color = vtkSmartPointer<vtkColorTransferFunction>::New();
2 color->AddRGBPoint(0, 0, 0, 0);
3 color->AddRGBPoint(64, 1.0, 0.52, 0.3);
4 color->AddRGBPoint(190.0, 1.00, 1.00, 1.00);
5 color->AddRGBPoint(220.0, 0.20, 0.20, 0.20);

        由于vtkVolumeProperty接受两种形式的颜色传输函数(灰度或者RGB),一次在获取相应的颜色传输函数时,需要首先判断VTKVolumeProperty已经设置的颜色传输类型,其函数为:

1 int GetColorChannels();
2 如果返回为1,则说明设置的是灰度传输函数;
3 如果返回为3,则说明设置的是彩色RGB传输函数。

  根据不同的类型既可通过如下函数获取有效的传输函数:

1 VTKColorTransferFunction* GetRGBTransferFunction();
2 vtkPiecewiseFunction* GetGrayTransferFunction();

光照与阴影
  通过VTKVolumeProperty可以设置体绘制阴影效果(Shading)。阴影效果主要受环境光系数、散射光系数、反射光系数和高光强度四个参数影响。使用vtkVolumeProperty::SetAmbient()设置环境光系数;使用vtkVolumeProperty::SetDiffuse()设置散射光系数;使用vtkVolumeProperty::SetSpecular()设置反射光系数。一般情况下,三个系数的和应该为1。但是有时候,在体绘制过程中为了提高亮度,三值之和会大于1。另外,还有一个参数是高光强度(Specular Power),用于控制体绘制的外观平滑程度。可用于VTKVolumeProperty::SetSpecularPower()设置该参数。vtkVolumeProperty中默认是是关闭阴影效果的,因此需要显式调用ShadeOn()函数来打开阴影效果。一个标准的光照与阴影设置代码如下。

1 volumeProperty->ShadeOn();
2 volumeProperty->SetAmbient(0.4);
3 volumeProperty->SetDiffuse(0.6);
4 volumeProperty->SetSpecular(0.2);

示例演示

        我们对比加与不加梯度不透明度函数两种情况。代码如下。

 1 /**********************************************************************
 2 
 3 Copyright (c) Mr.Bin. All rights reserved.
 4 For more information visit: http://blog.csdn.net/webzhuce
 5 
 6 **********************************************************************/
 7 #include <vtkSmartPointer.h>
 8 #include <vtkImageData.h>
 9 #include <vtkStructuredPoints.h>
10 #include <vtkStructuredPointsReader.h>
11 #include <vtkColorTransferFunction.h>
12 #include <vtkPiecewiseFunction.h>
13 #include <vtkRenderer.h>
14 #include <vtkRenderWindow.h>
15 #include <vtkRenderWindowInteractor.h>
16 #include <vtkVolumeProperty.h>
17 #include <vtkFixedPointVolumeRayCastMapper.h>
18 
19 int main(int argc, char *argv[])
20 {
21     vtkNew<vtkStructuredPointsReader> reader;
22     reader->SetFileName("E:\\TestData\\mummy.128.vtk");
23     reader->Update();
24     vtkNew<vtkFixedPointVolumeRayCastMapper> volumeMapper0;
25     volumeMapper0->SetInputData(reader->GetOutput());
26     volumeMapper0->SetBlendModeToComposite();
27 
28     vtkNew<vtkFixedPointVolumeRayCastMapper> volumeMapper1;
29     volumeMapper1->SetInputData(reader->GetOutput());
30     volumeMapper1->SetBlendModeToComposite();
31 
32     vtkNew<vtkVolumeProperty> volumeProperty0;
33     volumeProperty0->SetInterpolationTypeToLinear();
34     volumeProperty0->SetAmbient(0.4);
35     volumeProperty0->SetDiffuse(0.6);
36     volumeProperty0->SetSpecular(0.2);
37 
38     vtkNew<vtkVolumeProperty> volumeProperty1;
39     volumeProperty1->SetInterpolationTypeToLinear();
40     volumeProperty1->SetAmbient(0.4);
41     volumeProperty1->SetDiffuse(0.6);
42     volumeProperty1->SetSpecular(0.2);
43 
44     vtkNew<vtkPiecewiseFunction> compositeOpacity;
45     compositeOpacity->AddPoint(70,   0.00);
46     compositeOpacity->AddPoint(90,   0.40);
47     compositeOpacity->AddPoint(180,  0.60);
48     volumeProperty0->SetScalarOpacity(compositeOpacity); //设置不透明度传输函数
49     volumeProperty1->SetScalarOpacity(compositeOpacity); //设置不透明度传输函数
50 
51     vtkNew<vtkPiecewiseFunction> volumeGradientOpacity;
52     volumeGradientOpacity->AddPoint(10,  0.0);
53     volumeGradientOpacity->AddPoint(90,  0.5);
54     volumeGradientOpacity->AddPoint(100, 1.0);
55     //volumeProperty0->SetGradientOpacity(volumeGradientOpacity);//设置梯度不透明度效果对比
56     volumeProperty1->SetGradientOpacity(volumeGradientOpacity);//设置梯度不透明度效果对比
57 
58     vtkNew<vtkColorTransferFunction> color;
59     color->AddRGBPoint(0.000,  0.00, 0.00, 0.00);
60     color->AddRGBPoint(64.00,  1.00, 0.52, 0.30);
61     color->AddRGBPoint(190.0,  1.00, 1.00, 1.00);
62     color->AddRGBPoint(220.0,  0.20, 0.20, 0.20);
63     volumeProperty0->SetColor(color);
64     volumeProperty1->SetColor(color);
65 
66     vtkNew<vtkVolume> volume0;
67     volume0->SetMapper(volumeMapper0);
68     volume0->SetProperty(volumeProperty0);
69 
70     vtkNew<vtkVolume> volume1;
71     volume1->SetMapper(volumeMapper1);
72     volume1->SetProperty(volumeProperty1);
73 
74     double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
75     double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
76     vtkNew<vtkRenderer> renderer0;
77     renderer0->SetBackground(1.0, 1.0, 1.0);
78     renderer0->SetViewport(leftViewport);
79     renderer0->AddVolume(volume0);
80 
81     vtkNew<vtkRenderer> renderer1;
82     renderer1->SetBackground(1.0, 1.0, 1.0);
83     renderer1->SetViewport(rightViewport);
84     renderer1->AddVolume(volume1);
85 
86     vtkNew<vtkRenderWindow> renWin;
87     renWin->AddRenderer(renderer0);
88     renWin->AddRenderer(renderer1);
89     renWin->SetSize(640, 480);
90     renWin->SetWindowName("VolumeMapper");
91 
92     vtkNew<vtkRenderWindowInteractor> iren;
93     iren->SetRenderWindow(renWin);
94     renWin->Render();
95     iren->Start();
96 
97     return EXIT_SUCCESS;
98 }

运行结果