赞
踩
体渲染是一个术语,用于描述应用于3D数据的渲染过程,其中信息存在于整个3D空间中,而不是简单地存在于3D空间中定义的2D表面上。在体渲染和几何渲染技术之间没有明确的分界线。通常两种不同的方法可以产生相似的结果,在某些情况下,一种方法可以被认为是体渲染和几何渲染技术。例如,您可以使用轮廓技术提取代表图像数据集中等值面的三角形(参见第93页的“轮廓”),然后使用几何渲染技术显示这些三角形,或者您可以在图像数据集中使用体积射线投射技术并在特定等值处终止射线遍历。这两种不同的方法产生相似(尽管不一定相同)的结果。另一个例子是使用纹理映射硬件来执行复合体渲染的技术。这种方法可以被认为是一种体渲染技术,因为它处理图像数据,或者是一种几何技术,因为它使用几何原语和标准图形硬件。
在VTK中,为了自定义所呈现数据的属性,在体渲染技术和几何渲染技术之间进行了区分。正如您在迄今为止所展示的许多示例中所看到的那样,渲染数据通常涉及创建vtkActor, vtkProperty和vtkMapper的一些子类。vtkActor用于保存有关数据的位置,方向和缩放信息,以及指向属性和映射器的指针。vtkProperty对象捕获控制数据外观的各种参数,例如环境照明系数以及对象是平面、Gouraud还是Phong阴影。最后,vtkMapper子类负责实际呈现数据。对于体渲染,使用了一组功能非常相似的类。使用vtkVolume代替vtkActor来表示场景中的数据。就像vtkActor一样,vtkVolume表示场景中数据的位置、方向和缩放。但是,vtkVolume包含对vtkVolumeProperty和vtkAbstractVolumeMapper的引用。vtkVolumeProperty表示在体渲染过程中影响数据外观的那些参数,这是一组不同于几何渲染期间使用的参数。vtkAbstractVolumeMapper子类负责体渲染过程,并确保输入数据是映射器特定算法的正确类型。
在VTK中,体渲染技术已经实现了规则的直线网格(vtkImageData)和非结构化数据(vtkUnstructuredGrid)。您使用的vtkAbstractVolumeMapper的特定子类的SetInput()方法将接受一个指针,该指针仅指向该映射器的正确数据类型(vtkImageData或vtkUnstructuredGrid)。请注意,您可以将不规则数据重新采样为常规图像数据格式,以便利用本章中描述的vtkImageData渲染技术(参见第100页的“探测”)。或者,你可以将你的数据四面体化,生成一个非结构化的网格,使用本章描述的vtkUnstructuredGrid渲染技术。
对于每种受支持的数据类型,有几种不同的体呈现技术可用。我们将从使用几种不同渲染技术编写的一些简单示例开始本章。然后,我们将介绍所有这些技术共同的对象/参数。接下来,将更详细地讨论每种体呈现技术,包括特定于该呈现方法的参数信息。接下来将讨论如何实现适用于所有体渲染方法的交互渲染率。
7.1支持的数据类型历史说明
第一个纳入VTK的体渲染方法是专为vtkImageData设计的。开发超类vtkVolumeMapper是为了定义所有vtkImageData体渲染方法的API。后来,vtkUnstructuredGrid数据集的体渲染被添加到VTK中。为了保持向后兼容性,引入了一个新的抽象超类作为所有类型的体呈现的超类。因此,vtkAbstractVolumeMapper是vtkVolumeMapper(其子类只渲染vtkImageData数据集)和vtkUnstructuredGridVolumeMapper(其子类只渲染vtkUnstructuredGrid数据集)的超类。
7.2简单示例
考虑下面的简单体渲染示例,如图7-1所示(参考VTK/Examples/ volumerender /Tcl/SimpleRayCast.tcl)。这个例子是为vtkImageData的体射线投射而编写的,但是只有用粗体突出显示的Tcl脚本部分是特定于这种呈现技术的。遵循这个例子,你会发现脚本的粗体部分的替代版本,而不是用其他映射器执行体渲染任务,包括纹理映射方法来渲染vtkImageData和基于投影的方法来渲染vtkUnstructuredGrid数据集。您将注意到,切换卷呈现技术(至少在这个简单的示例中)只需要对脚本进行一些微小的更改,因为大多数功能都是在超类API中定义的,因此对所有卷映射器都是通用的。
- # Create the reader for the data
- vtkStructuredPointsReader reader
- reader SetFileName "$VTK_DATA_ROOT/Data/ironProt.vtk"
- # Create transfer mapping scalar value to opacity
- vtkPiecewiseFunction opacityTransferFunction
- opacityTransferFunction AddPoint 20 0.0
- opacityTransferFunction AddPoint 255 0.2
- # Create transfer mapping scalar value to color
- vtkColorTransferFunction colorTransferFunction
- colorTransferFunction AddRGBPoint 0.0 0.0 0.0 0.0
- colorTransferFunction AddRGBPoint 64.0 1.0 0.0 0.0
- colorTransferFunction AddRGBPoint 128.0 0.0 0.0 1.0
- colorTransferFunction AddRGBPoint 192.0 0.0 1.0 0.0
- colorTransferFunction AddRGBPoint 255.0 0.0 0.2 0.0
- # The property describes how the data will look
- vtkVolumeProperty volumeProperty
- volumeProperty SetColor colorTransferFunction
- volumeProperty SetScalarOpacity opacityTransferFunction
- # The mapper / ray cast functions know how to render the data
- vtkVolumeRayCastCompositeFunction compositeFunction
- vtkVolumeRayCastMapper volumeMapper
- volumeMapper SetVolumeRayCastFunction compositeFunction
- volumeMapper SetInputConnection [reader GetOutputPort]
- # The volume holds the mapper and the property and
- # can be used to position/orient the volume
- vtkVolume volume
- volume SetMapper volumeMapper
- volume SetProperty volumeProperty
- ren1 AddProp volume
- renWin Render
在本例中,我们从从磁盘读取数据文件开始。然后我们定义将标量值映射到vtkVolumeProperty中使用的不透明度和颜色的函数。接下来,我们创建特定于体积光线投射的对象——一个vtkVolumeRayCastCompositeFunction,它执行沿着光线的合成样本,一个vtkVolumeRayCastMapper,它执行一些基本的光线投射操作,如转换和裁剪。我们将映射器的输入设置为从磁盘读取的数据,并创建vtkVolume(类似于vtkActor的vtkProp3D的子类)来保存映射器和属性。最后,我们将体积添加到渲染器并渲染场景。
如果你决定用2D纹理映射方法来实现上面的脚本,
- # Create the objects specific to 2D texture mapping approach
- vtkVolumeTextureMapper2D volumeMapper
- volumeMapper SetInputConnection [reader GetOutputPort]
如果你的显卡支持3D纹理映射(几乎所有最近的显卡都支持),那么你可以决定用3D纹理映射方法实现上面的脚本。脚本的加粗部分应该是:
- # Create the objects specific to 3D texture mapping approach
- vtkVolumeTextureMapper3D volumeMapper
- volumeMapper SetInputConnection [reader GetOutputPort]
vtkFixedPointVolumeRayCastMapper是vtkVolumeRayCastMapper的替代方案,并且在大多数情况下是推荐的软件映射器。vtkFixedPointVolumeRayCastMapper处理所有数据类型以及多组件数据,并使用定点计算和空间跳跃来实现高性能。然而,它是不可扩展的,因为混合操作是为了性能而硬编码的,而不是通过编写新的光线投射函数来自定义。要将此示例改为使用vtkFixedPointVolumeRayCastMapper,脚本的粗体部分将改为:
- # Create the fixed point ray cast mapper
- vtkFixedPointVolumeRayCastMapper volumeMapper volumeMapper
- SetInputConnection [reader GetOutputPort]
如果您想使用非结构化网格体渲染技术,替换代码将变得稍微复杂,以便在将数据作为输入传递给映射器之前执行从vtkImageData到vtkUnstructuredGrid的转换。在这种情况下,我们将使用非结构化网格渲染方法,该方法使用图形硬件投影网格的四面体表示。替代代码将是:
- # Convert data to unstructured grid
- vtkDataSetTriangleFilter tetraFilter
- tetraFilter SetInputConnection [reader GetOutputPort]
- # Create the objects specific to the Projected Tetrahedra method
- vtkProjectedTetrahedraMapper volumeMapper
- volumeMapper SetInputConnection [tetraFilter GetOutputPort]
请注意,不建议将vtkImageData转换为vtkUnstructuredGrid进行渲染,因为直接在vtkImageData上工作的映射器通常比vtkUnstructuredGrid数据的映射器更有效,无论是在内存消耗还是渲染性能方面。
7.3为什么使用多种体渲染技术?
正如你所看到的,在这个简单的例子中,渲染策略之间的主要变化是被实例化的体映射器的类型,也许还有一些渲染方法特定的参数,比如在光线投射技术中使用的光线投射函数。这可能会导致以下问题:为什么在VTK中有不同的体积渲染策略?为什么VTK不能简单地选择“最佳”策略?首先,预测哪种策略最有效并不总是那么容易,如果图像大小减小,可用的处理器更多,或者图形硬件成为渲染率的瓶颈,光线投射可能会优于纹理映射。这些参数与7.4在不同平台上有所不同,实际上可能在运行时不断变化。其次,由于其计算复杂性,大多数体绘制技术只能产生所需渲染方程的近似值。例如,通过体积采样并将其与alpha混合函数组合的技术只能近似于通过体积的真实积分。在不同的情况下,不同的技术在质量和速度方面表现得更好或更差。此外,有些技术只在某些特殊条件下才起作用。例如,一些技术只支持具有单一标量组件的无符号char或无符号short类型的数据,而其他技术支持任何标量类型和多组件数据。“最佳”技术取决于您的具体数据、性能和图像质量需求,以及运行代码的系统的硬件配置。事实上,“最佳”技术实际上可能是多种技术的组合。本章的一节专门描述可用于以跨平台方式实现交互式体绘制的多技术细节级策略
7.4创建vtkVolume
vtkVolume是vtkProp3D的子类,用于体渲染。类似于vtkActor(用于几何渲染),vtkVolume保存转换信息,如位置,方向和比例,以及指向映射器和属性的指针。关于如何控制vtkVolume转换的其他信息涵盖在第52页的“控制3D道具”中。
vtkVolume类接受vtkAbstractVolumeMapper子类的对象作为SetMapper()的输入,并接受vtkVolumeProperty对象作为SetProperty()的输入。vtkActor和vtkVolume是两个独立的对象,以便强制执行不同类型的映射器和属性。这些不同的类型是必要的,因为几何渲染的一些参数在体渲染中没有意义,反之亦然。例如,vtkProperty的SetRepresentationToWireframe()方法在体渲染中没有意义,而vtkVolumeProperty的SetInterpolationTypeToNearest()方法在几何渲染中没有价值。
7.5使用vtkPiecewiseFunction
为了控制标量值的三维体的外观,必须定义几个映射或传递函数。一般来说,所有的体绘制技术都需要两个传递函数。第一个所需的传递函数,称为标量不透明度传递函数,将标量值映射为不透明度或单位长度不透明度值。第二个传递函数,简称为颜色传递函数,将标量值映射为颜色。在一些结构化体绘制方法中使用的可选传递函数被称为梯度不透明度传递函数,它将标量值的梯度大小映射到不透明度乘法器中。这些映射中的任何一个都可以定义为单个值到单个值的映射,它可以用vtkPiecewiseFunction表示。对于颜色映射的标量值,vtkColorTransferFunction也可以用来定义RGB而不是灰度颜色。
从用户的角度来看,vtkPiecewiseFunction有两种方法——一种是向映射中添加信息,另一种是从映射中清除信息。当信息被添加到映射中时,它被认为是映射的一个点样本,使用插值来确定指定值之间的值。例如,考虑左边脚本的以下部分,它在右边产生传递函数draw:
- vtkPiecewiseFunction tfun
- tfun AddPoint 50 0.2
- tfun AddPoint 200 1.0
标量值50和200的映射值分别为0.2和1.0,其他所有映射值都可以在这两个值之间线性插值得到。如果夹紧打开(默认),则低于50的任何值的映射将为0.2,高于200的任何值的映射将为1.0。如果关闭夹紧,则超出范围的值映射为0.0。点可以在任何时候添加到映射中。如果映射被重新定义,它将替换现有的映射。
除了添加单个点之外,还可以添加一个段,它将定义两个映射点并清除两者之间的任何现有点。作为一个例子,考虑以下两个修改步骤和传递函数的相应图形表示:
在第一步中,我们通过删除点然后再次添加点来改变标量值50的映射,并且我们添加了一个段。在第二步中,我们通过简单地添加一个新映射而不首先删除旧映射来更改标量值50的映射。我们还添加了一个新的段,它消除了100和150的映射,因为它们位于新的段内,我们关闭了箝位。
7.6使用vtkColorTransferFunction
可以使用vtkColorTransferFunction指定标量值到颜色的映射
RGB或HSV色彩空间。可用的方法类似于vtkPiecewiseFunction提供的方法,但往往有两种风格。例如,AddRGBPoint()和
AddHSVPoint()都将一个点添加到传递函数中,一个接受RGB值作为输入,另一个接受HSV值作为输入。
下面的Tcl示例展示了如何指定一个从红到绿到蓝的传递函数,并对指定值之间的值执行RGB插值:
- vtkColorTransferFunction ctfun
- ctfun SetColorSpaceToRGB
- ctfun AddRGBPoint 0 1 0 0
- ctfun AddRGBPoint 127 0 1 0
- ctfun AddRGBPoint 255 0 0 1
7.7使用vtkVolumeProperty控制颜色/不透明度
在前两节中,我们已经讨论了创建传递函数的基础知识,但是我们还没有讨论这些函数如何控制音量的外观。通常,定义传递函数是实现有效的体积可视化的最困难的部分,因为您实际上是在执行分类操作,这需要您理解底层数据值的含义。
对于将像素映射到体中的单个位置的渲染技术(例如等值面渲染或最大强度投影),ScalarOpacity传递函数将标量值映射到不透明度。当使用合成技术时,ScalarOpacity函数将标量值映射到该值的均匀区域的每单位长度累积的不透明度。然后,特定的映射器利用一种合成形式,通过体积累积不断变化的颜色和不透明度值,形成最终的颜色和不透明度,存储在相应的像素中。
ScalarOpacity和Color传递函数通常用于执行数据的简单分类。作为背景的一部分或被认为是噪声的标量值被映射为0.0的不透明度,从而消除它们对图像的贡献。剩余的标量值可以分为不同的“材料”,具有不同的不透明度和颜色。例如,从CT扫描仪获取的数据通常可以根据数据中包含的密度值分类为空气、软组织或骨骼(图7-2)。定义为空气的标量值的不透明度为0.0,软组织标量值可能被赋予浅红棕色,骨骼值可能被赋予白色。通过改变后两种材料的不透明度,你可以看到皮肤表面或骨骼表面,或者可能通过半透明的皮肤看到骨骼。确定数据中材料之间的分界线的过程可能很繁琐,并且在某些情况下不可能基于原始输入数据值。例如,肝脏和肾脏的CT密度值可能重叠。在这种情况下,可能需要将分割过滤器应用于卷,以改变数据值,以便仅根据标量值对材料进行分类,或者提取出一种特定的材料类型。这些分割操作可以基于附加信息,如位置或与参考卷的比较。
这里显示了两个仅使用vtkVolumeProperty中定义的传递函数分割CT数据的示例,一个用于躯干(图7 - 2),另一个用于头部研究(图7 - 3)。在这两个示例中,第三个传递函数映射了梯度的大小
标量值为不透明度乘法器,并用于增强体积的过渡区域的贡献。例如,在标量值从空气到软组织或软组织到骨骼的过渡处,可以发现一个较大的梯度幅度,而在软组织和骨骼区域内,梯度幅度相对较小。下面是一个代码片段,它为8位无符号数据定义了一个典型的渐变不透明度传递函数。
- vtkPiecewiseFunction gtfun
- gtfun AddPoint 0 0.0
- gtfun AddPoint 3 0.0
- gtfun AddPoint 6 1.0
- gtfun AddPoint 255 1.0
该函数通过为任何小于3的梯度大小定义一个0.0的不透明度乘数来消除几乎均匀的区域。这个乘数遵循从0.0到1.0的线性渐变,梯度值在3到6之间,不透明度值在6以上的样本上没有变化。嘈杂的数据可能需要更积极的边缘检测(因此3和6将是更高的值)。请注意,梯度幅度传递函数目前只支持在渲染vtkImageData的体映射器中。对于渲染vtkUnstructuredGrid数据集的体映射器,不计算梯度,因此梯度大小传递函数和阴影在这些映射器中都不可用。
在vtkVolumeProperty中有一些方法与颜色和不透明度传递函数相关。SetColor()方法接受vtkPiecewiseFunction(如果你的颜色函数只定义灰度值)或vtkColorTransferFunction。您可以使用GetColorChannels()查询颜色通道的数量,如果将vtkPiecewiseFunction设置为颜色,则返回1,如果使用vtkColorTransferFunction指定颜色,则返回3。一旦您知道有多少颜色通道正在使用,您可以调用GetGrayTransferFunction()或GetRGBTransferFunction()来获得相应的函数。
SetScalarOpacity()方法接受一个vtkPiecewiseFunction来定义标量不透明度传递函数,并且有一个相应的GetScalarOpacity()方法返回这个函数。类似地,梯度不透明度传递函数有两种方法:SetGradientOpacity()和GetGradientOpacity()。
到目前为止的讨论只考虑了单分量标量数据,其中一组传递函数定义了数据的外观。另外,可以用两种方式之一呈现多组件数据。如果组件是独立的,则每个组件可以定义一组传递函数。独立数据的一个例子可能是通过模拟过程产生的非结构化网格,该过程在网格上产生温度和密度值。独立成分的另一个例子是共聚焦显微镜产生的数据,其中用不同的荧光染料多次扫描标本,以突出标本内的不同结构。在呈现组件独立的多组件数据时,必须定义每个组件的外观参数。SetColor()、SetScalarOpacity()和SetGradientOpacity()方法接受一个可选的索引值作为第一个参数,用于设置特定组件的传递函数。
多组件数据也可能表示的不是独立的属性,而是定义一个属性的一组值。例如,当使用物理切片技术时,您可能有三个或四个表示RGB或RGBA的组件数据。或者你有两个分量代表亮度和alpha。支持多个组件的卷映射器支持两种形式的非独立组件。第一个是两分量数据,其中第一个分量通过颜色传递函数来确定样本的颜色,第二个分量通过标量不透明度函数来定义样本的alpha。第二类非独立多分量数据是四分量数据,其中前三个分量直接取为RGB,第四个分量通过标量不透明度传递函数来定义alpha。在这两种非独立的情况下,最后一个分量用于计算梯度,因此也控制梯度大小不透明度传递函数。
请注意,并非所有映射器都支持多组件数据,请参阅本章其余部分提供的特定于映射器的文档,以了解所支持功能的更多信息。对于支持多个组件的映射器,限制通常是四个组件。
7.8使用vtkVolumeProperty控制阴影
用体积属性控制体的底纹类似于用属性控制几何角色的底纹(参见第53页的“角色属性”和第54页的“角色颜色”)。有一个用于遮光的标志,以及四个基本参数:环境系数、漫射系数、高光系数和高光功率。通常,前三个系数的总和为1.0,但在体渲染中,为了增加渲染体的亮度,通常需要超过这个值。这些参数的确切解释将取决于所使用的特定体绘制技术所使用的照明方程。一般来说,如果环境项占主导地位,那么体积将显得无阴影,如果漫射项占主导地位,那么体积将显得粗糙(如混凝土),如果镜面项占主导地位,那么体积将显得光滑(如玻璃)。镜面功率可用于控制外观的平滑程度(例如拉丝金属与抛光金属)。
默认情况下,阴影是关闭的。你必须显式地调用ShadeOn()来影响场景的阴影系数。设置阴影标志通常与设置环境系数为1.0,漫射系数为0.0和镜面系数为0.0相同。请注意,目前渲染vtkUnstructuredGrid数据集的体映射器不支持阴影。此外,vtkImageData的一些体渲染技术,如具有最大强度射线函数的体射线投射,不考虑阴影系数,而不考虑阴影标志的值。一个体的阴影外观(当阴影标志打开时)不仅取决于vtkVolumeProperty中阴影系数的值,还取决于渲染器中包含的光源集合及其属性。渲染体的外观取决于场景中光源的数量、位置和颜色。如果可能的话,体渲染技术试图重现OpenGL定义的照明方程。考虑下面的例子。
- #Create a geometric sphere
- vtkSphereSource sphere
- sphere SetRadius 20
- sphere SetCenter 70 25 25
- sphere SetThetaResolution 50
- sphere SetPhiResolution 50
- vtkPolyDataMapper mapper
- mapper SetInput [sphere GetOutput]
- vtkActor actor
- actor SetMapper mapper
- [actor GetProperty] SetColor 1 1 1
- [actor GetProperty] SetAmbient 0.01
- [actor GetProperty] SetDiffuse 0.7
- [actor GetProperty] SetSpecular 0.5
- [actor GetProperty] SetSpecularPower 70.0
- #Read in a volumetric sphere
- vtkSLCReader reader
- reader SetFileName "$VTK_DATA_ROOT/Data/sphere.slc"
- # Use this tfun for both opacity and color
- vtkPiecewiseFunction opacityTransferFunction
- opacityTransferFunction AddSegment 0 1.0 255 1.0
- # Make the volume property match the geometric one
- vtkVolumeProperty volumeProperty
- volumeProperty SetColor opacityTransferFunction
- volumeProperty SetScalarOpacity tfun
- volumeProperty ShadeOn
- volumeProperty SetInterpolationTypeToLinear
- volumeProperty SetDiffuse 0.7
- volumeProperty SetAmbient 0.01
- volumeProperty SetSpecular 0.5
- volumeProperty SetSpecularPower 70.0
- vtkVolumeRayCastCompositeFunction compositeFunction
- vtkVolumeRayCastMapper volumeMapper
- volumeMapper SetInput [reader GetOutput]
- volumeMapper SetVolumeRayCastFunction compositeFunction
- vtkVolume volume
- volume SetMapper volumeMapper
- volume SetProperty volumeProperty
- # Add both the geometric and volumetric spheres to the renderer
- ren1 AddProp volume
- ren1 AddProp actor
- # Create a red, green, and blue light
- vtkLight redlight
- redlight SetColor 1 0 0
- redlight SetPosition 1000 25 25
- redlight SetFocalPoint 25 25 25
- redlight SetIntensity 0.5
- vtkLight greenlight
- greenlight SetColor 0 1 0
- greenlight SetPosition 25 1000 25
- greenlight SetFocalPoint 25 25 25
- greenlight SetIntensity 0.5
- vtkLight bluelight
- bluelight SetColor 0 0 1
- bluelight SetPosition 25 25 1000
- bluelight SetFocalPoint 25 25 25
- bluelight SetIntensity 0.5
- # Add the lights to the renderer
- ren1 AddLight redlight
- ren1 AddLight greenlight
- ren1 AddLight bluelight
- #Render it!
- renWin Render
在这个例子所示的图像中(图7-4),左边的球体是用体射线投射渲染的,右边的球体是用OpenGL表面渲染渲染的。由于用于vtkActor的vtkProperty和用于vtkVolume的vtkVolumeProperty设置了相同的环境、漫反射、镜面和镜面功率值,并且两个球体的颜色都是白色,因此它们具有相似的外观。
当使用多个独立组件渲染数据时,必须为每个组件设置着色参数。SetAmbient()、SetDiffuse()、SetSpecular()和SetSpecularPower()方法中的每一个都接受一个可选的第一个参数,该参数表示组件索引。虽然vtkVolumeProperty API允许每个组件独立地启用/禁用阴影,但目前VTK中没有卷映射器支持这一点。因此,所有的Shade实例变量都应该设置为On或Off。
7.9创建卷映射器
vtkAbstractVolumeMapper是一个抽象超类,从不直接创建。相反,您将创建所需特定类型的映射器子类。在VTK 5.4中,vtkImageData的选择是vtkVolumeRayCastMapper, vtkVolumeTextureMapper2D, vtkFixedPointVolumeRayCastMapper, vtkVolumeTextureMapper3D,或VTKVolumeProVP1000Mapper。对于vtkUnstructuredGrid数据集,可用的映射器是vtkUnstructuredGridVolumeRayCastMapper, vtkUnstructuredGridZSweepMapper, vtkProjectedTetrahedraMapper或VTKHAVSVolumeMapper。
所有卷映射器都支持SetInput()方法,其参数是指向vtkImageData对象或vtkUnstructuredGrid对象的指针。对于vtkImageData卷映射器,每种渲染技术只支持某些类型的vtkImageData。例如,vtkVolumeRayCastMapper和vtkVolumeTextureMapper2D都只支持VTK_UNSIGNED_CHAR和VTK_UNSIGNED_SHORT数据与单个组件。vtkVolumeTextureMapper3D支持任何标量类型,但只有一个组件,或多个非独立组件。vtkFixedPointVolumeRayCastMapper是最灵活的,支持所有数据类型和多达四个组件。
7.10裁剪卷
由于大型复杂体积的体积渲染图像可能产生难以解释的图像,因此只查看体积的一部分通常是有用的。可以用来限制呈现的数据量的两种技术被称为裁剪和剪辑。
裁剪是一种使用六个平面定义结构化体的可见区域的方法,每个主要轴上都有两个平面。裁剪仅适用于在vtkImageData上操作的卷映射器。剪辑适用于vtkImageData和vtkUnstructuredGrid体映射器。六个轴对齐的裁剪平面在数据坐标中定义,因此依赖于数据的原点和间距,但与应用于体积的任何转换无关。使用这六个平面最常见的方法是定义一个感兴趣的子体积,如图所示。
要裁剪子卷,必须打开裁剪,设置裁剪区域标志,并在卷映射器中设置裁剪区域平面,如下所示。
- set xmin 10.0
- set xmax 50.0
- set ymin 0.0
- set ymax 33.0
- set zmin 21.0
- set zmax 47.0
- vtkVolumeRayCastMapper mapper
- mapper CroppingOn
- mapper SetCroppingRegionPlanes $xmin $xmax $ymin $ymax $zmin $zmax
- mapper SetCroppingRegionFlagsToSubVolume
注意,上面的例子是为vtkVolumeRayCastMapper显示的,但是它可以使用vtkVolumeMapper的任何具体子类,因为裁剪方法都是在超类中定义的。
由xmin, xmax, ymin, ymax, zmin和zmax值定义的六个平面将卷分成27个区域(3x3网格)。CroppingRegionFlags是一个27位数字,其中一位代表每个区域,其中值1表示该区域内的数据是可见的,值0表示该区域内的数据将被裁剪。体积中小于xmin, ymin和zmin的区域由第一个位表示,区域首先沿着x轴排序,然后是y轴,最后是z轴。
SetCroppingRegionFlagsToSubVolume()方法是一种方便的方法,它将标志设置为0x0002000 -仅中心区域可见。虽然任何27位数字都可以用来定义裁剪操作,但实际上只有少数几个被使用。另外提供了四个方便的方法来设置这些标志:SetCroppingRegionFlagsToFence()、SetCroppingResgionFlagsToInvertedFence()、SetCroppingRegionFlagsToCross()和SetCroppingRegionFlagsToInvertedCross(),如图7-5所示。
7.11裁剪卷
除了vtkVolumeMapper提供的裁剪功能外,vtkAbstractMapper3D还提供了任意裁剪平面。对于使用OpenGL在硬件中执行裁剪的vtkAbstractMapper3D的子类,如vtkPolyDataMapper, vtkVolumeTextureMapper2D和vtkProjectedTetrahedraMapper,如果您试图使用超过OpenGL支持的最大裁剪平面数量(通常为6),可能会显示错误消息。软件渲染技术,如vtkVolumeRayCastMapper可以支持任意数量的裁剪平面。vtkVolumeProMapper不直接支持这些剪切平面,尽管该类确实包含使用平面和厚度值指定一个剪切盒的方法。
通过创建vtkPlane来指定裁剪平面,定义平面参数,然后使用AddClippingPlane()方法将该平面添加到映射器。这些任意裁剪平面在体绘制中的一个常见用途是指定两个彼此平行的平面,以便执行厚的重新格式化操作。图7-6为CT数据处理的一个示例。对于非结构化数据,裁剪平面基本上可以用作裁剪平面,仅查看数据的子区域,这在试图可视化复杂结构中的内部细节时通常是必要的。
7.12控制正常编码
标准照明方程依赖于表面法线来计算阴影的漫反射和镜面分量。在vtkImageData的体绘制中,体数据中某个位置的梯度被认为指向与该位置的“表面法线”相反的方向。有限差分技术通常用于估计梯度,但这往往是一个昂贵的计算,并且如果必须在每条射线的每个样本上执行,则会使阴影体渲染非常缓慢。
避免这些昂贵计算的一种方法是预先计算网格位置的法线,并在两者之间使用某种形式的插值。如果简单地这样做,每个位置需要三个浮点数,我们仍然需要取平方根来确定大小。或者,我们可以存储幅度,这样每个法线将需要四个浮点值。由于卷往往相当大,这种技术需要太多的内存,因此我们必须以某种方式将法线量化为更小的字节数。
在一些VTKImageData体映射器中,我们选择将法线方向量化为两个眼,将幅度量化为一个眼。法线的计算由vtkEncodedGradientEstimator的子类执行(目前只有vtkFiniteDifferenceGradientEstimator),方向的编码为两个字节由vtkDirectionEncoder的子类执行(目前vtkrecursivessphere - directionencoder和VTKSphericalDirectionEncoder)。对于使用普通编码的映射器(vtkVolumeRayCastMapper和vtkVolumeTextureMapper2D),这些对象是自动创建的,所以典型的用户不需要关心这些对象。在由多个映射器将一个卷数据集渲染到同一图像的情况下,创建一个梯度估计器供所有映射器使用通常是有用的。这将节省空间和计算时间,否则每个映射器将有一个正常卷的副本。下面是一个示例代码片段:
- # Create the gradient estimator
- vtkFiniteDifferenceGradientEstimator gradientEstimator
- # Create the first mapper
- vtkVolumeRayCastMapper volumeMapper1
- volumeMapper1 SetGradientEstimator gradientEstimator
- volumeMapper1 SetInput [reader GetOutput]
- # Create the second mapper
- vtkVolumeRayCastMapper volumeMapper2
- volumeMapper2 SetGradientEstimator gradientEstimator
- volumeMapper2 SetInput [reader GetOutput]
如果将梯度估计器设置为两个不同映射器中的相同对象,那么重要的是这些映射器具有相同的输入。否则,每次映射器请求法线时,梯度估计器都会过期,并且会在渲染的每一帧期间为每个体重新生成法线。在上面的例子中,没有明确地创建方向编码对象,因此每个梯度估计器都创建了自己的编码对象。由于该对象没有任何重要的存储需求,因此这通常是可以接受的情况。或者,可以创建一个vtkRecursiveSphereDirectionEncoder,并在每个估计器上使用SetDirectionEncoder()方法。
vtkFixedPointVolumeRayCastMapper类确实支持着色,并且确实使用这些相同的梯度估计器和法线编码器,但是这些类没有在API级别公开,因此编码的法线不能在映射器之间共享。vtkVolumeTextureMapper3D类也支持着色,但是通过直接在纹理内存中存储3字节的法线表示来实现。
7.13 vtkImageData的体积光线投射
vtkVolumeRayCastMapper是一个使用软件光线投射技术来执行体渲染的体映射器。它通常是最准确的映射器,也是大多数平台上最慢的映射器。光线投射器是多线程的,以便在可用时使用多个处理器。有一些特定于体射线投射的参数尚未讨论。首先,必须在映射器中设置光线投射函数。这个对象执行实际工作,考虑沿着射线的数据值并确定要返回的最终RGBA值。目前,vtkVolumeRayCastFunction有三个支持的子类:vtkVolumeRayCastIsosurfaceFunction可用于在体积数据中渲染等面,vtkVolumeRayCastMIPFunction可用于生成体积的最大强度投影,vtkVolumeRayCastCompositeFunction可用于用alpha合成技术渲染体积。使用这些不同方法生成的图像示例如图7-7所示。左上方的图像是使用最大强度投影生成的。另外两张上面的图像是使用合成生成的,而下面的两张图像是使用等值面函数生成的。请注意,区分使用合成技术生成的图像与使用等面技术生成的图像并不总是那么容易,特别是当使用了一个锐利的不透明度斜坡时。在影响渲染过程的每个光线投射函数中都可以设置一些参数。
在vtkVolumeRayCastIsosurfaceFunction中,有一个SetIsoValue()方法可以用来设置渲染的等值面的值。在vtkVolumeRayCastMIPFunction中,你可以调用图7-7通过光线投射的体渲染。SetMaximizeMethodToScalarValue()(默认)或SetMaximizeMethodToOpacity()改变最大化操作的行为。在第一种情况下,沿着射线的每个采样点考虑标量值。选择标量值最大的样本点,然后将该标量值通过颜色和不透明度传递函数来产生最终的射线值。如果调用第二种方法,则沿着光线的每一步计算样本的不透明度,并选择不透明度值最高的样本。在vtkVolumeRayCastCompositeFunction中,你可以调用SetCompositeMethodToInterpolateFirst()(默认)或SetCompositeMethodToClassifyFirst()来改变插值和分类的顺序(图7-8)。这个设置只会在使用三线性插值时产生影响。在第一种情况下,将执行插值以确定样本点处的标量值,然后将该值用于分类(应用颜色和不透明度传递函数)。在第二种情况下,在包含样本位置的单元格的8个顶点上进行分类,然后从顶点位置计算的RGBA值插入最终的RGBA值。首先插值通常会产生“更漂亮”的图像,如图所示,其中一个几何球体包含在一个体积“到点的距离”域中,传递函数定义为突出显示体积中的三个同心球壳。interpolate first方法的基本假设是,如果两个相邻的数据点的值为10和100,那么在这两个数据点之间的某个地方存在一个值50。在按标量值对材料进行分类的情况下,情况可能并非如此。例如,考虑CT数据,值低于20为空气(透明),值从20到80为软组织,值高于80为骨骼。如果首先进行插值,那么骨骼永远不能与空气相邻-骨骼和空气之间必须总是有软组织。在口腔内,牙齿与空气接触的地方就不是这样了。如果你渲染一个图像,首先执行插值和足够高的采样率,它会看起来像牙齿上面有一层皮肤。
vtkVolumeProperty中插值类型实例变量的值对光线投射很重要。有两个选项:SetInterpolationTypeToNearest()(默认),它将在沿射线采样时使用最近邻近似,以及SetInterpolationTypeToLinear(),它将在采样期间使用三线性插值。使用三线性插值产生更平滑的图像,伪影更少,但通常需要更长的时间。两种方法获得的图像质量差异如图7-9所示。球体体素化为50x50x50体素体积,并使用alpha合成进行渲染,左边是最近邻插值,右边是三线性插值。在整个球体的图像中,可能很难区分这两种插值方法,但是通过放大球体的一部分,可以很容易地看到左边图像中的单个体素。
vtkVolumeRayCastMapper的另一个影响图像的参数是SampleDistance。这是在世界坐标中采样的射线函数的采样点之间的距离。例如,alpha合成射线函数通过沿着射线采样来执行连续体渲染积分的离散逼近。近似值的准确性提高了
随着采样的数量,但不幸的是,渲染时间也是如此。最大强度射线函数也需要采样来定位最大值。等值面射线函数不采样,而是根据当前插值函数计算交点的确切位置。
默认情况下,样本在世界坐标中间隔1个单位。在实践中,你应该根据正在渲染的3D数据的样本间距,以及标量值的变化率,以及通过传递函数分配给标量值的颜色和不透明度来调整这个间距。下面是一个体素化花瓶的例子,数据集中样本之间的间距为1x1x1。标量值在数据中平滑变化,但在传递函数中引入了急剧变化,使颜色从黑色迅速变为白色。在步长为2.0的图像中,你可以清楚地看到光线投射的“欠采样”。即使步长为1.0,也会出现一些伪影,因为花瓶的颜色在1.0的世界空间距离内会发生显著变化。如果采样距离设置为0.1,则图像看起来很平滑。当然,左边这张平滑的图像的生成时间几乎是右边这张的20倍。
7.14定点光线投射
vtkFixedPointVolumeRayCastMapper是vtkImageData的卷映射器,它采用定点算法来提高性能。vtkFixedPointVolumeRayCastMapper支持从unsigned char到double的所有标量类型,并支持多达四个独立的组件,每个组件都有自己的传递函数和着色参数。此外,该映射器支持两种非独立的多组分数据。第一个变量是两个组件数据,其中第一个组件用于查找颜色,而第二个组件用于导出正常值并查找不透明度。当密度等属性存储在第二个组件中,而第一个组件可能用作指示不同材质类型的索引时,这是很有用的,每个材质类型都可以有自己的颜色、不透明度和阴影样式。第二种是四分量无符号char数据,前三个分量直接表示RGB,第四个分量通过标量不透明度传递函数得到alpha。
vtkFixedPointVolumeRayCastMapper使用一种空间跳跃的形式来避免在卷的“空”(完全透明)区域进行处理。一旦达到完全不透明,也采用早期射线终止来终止处理。因此,当呈现具有锐利“表面”外观的数据时,可以获得显著的性能改进。
7.15 2D纹理映射
作为光线投射的替代方案,vtkImageData的体渲染可以通过纹理映射体到多边形上,并与图形硬件投影这些来执行。如果你的图形板提供合理的纹理映射加速,这种方法将比光线投射明显快得多,但代价是准确性,因为部分累积结果存储在帧缓冲区的分辨率(通常每个组件8位或更少),而不是浮点数。为了使用2D纹理映射,沿着与观看方向最接近的体轴生成四边形。随着观察方向的改变,四边形之间的采样距离也会发生变化,并且在某个点四边形的集合会跳转到一个新的轴上,这可能会导致时间伪影。一般来说,这些伪影在小体积上最为明显。
目前实现的vtkVolumeTextureMapper2D只支持alpha合成。切片上的双线性插值用于纹理映射,但由于四边形仅在数据平面上创建,因此在切片之间没有插值的概念。因此,这个映射器忽略了vtkVolumeProperty中的InterpolationType实例变量的值。
在纹理映射方法的软件中支持阴影。如果在vtkVolumeProperty中关闭阴影,则不需要执行软件阴影计算,因此此映射器的性能将比打开阴影时更好。
7.16 3D纹理映射
大多数当前的显卡现在都支持3D纹理映射,其中三维缓冲区存储在图形板上,并使用3D纹理坐标访问。然后,vtkImageData体可以通过将该体存储为纹理并投影一组平行于视图平面的多边形来渲染。这消除了2D纹理映射方法中固有的“弹出”伪影,因为不再有基于主要观看方向的底层几何结构的突然变化。然而,目前在VTK中可用的3D纹理映射方法使用了合成,这通常仍然限制在8位。因此,对于相当半透明的大体积,将会出现带状伪影,并且图像中的小特征可能会丢失。
3D纹理映射器是一个单通道映射器,它需要整个卷都在内存中。因此,对传输到纹理存储器的数据的大小进行了限制。这个限制是基于数据的类型,组件的数量,图形板上可用的纹理内存,以及一些硬编码限制,这些限制用于避免在有bug的OpenGL驱动程序中报告使用比实际可用的更多纹理内存的能力的问题。这是一个无声的限制-输入数据集将被下采样以适应可用的纹理内存,而不会产生警告或错误消息。该映射器支持单组件数据或任何标量类型,以及unsigned char类型的四组件相关数据(RGBA)。对于单个组件数据,硬编码的限制是256x256x128体素,任何宽高比都提供每个维度是2的幂。对于四分量数据,限制是256x128x128体素。3D体积纹理映射器支持两个主要的图形硬件家族:nVidia和ATI。
3D纹理映射使用了两种不同的实现-一种基于GL_NV_texture_shader2和GL_NV_register_combiners2扩展(在一些旧的nVidia卡上支持),另一种基于GL_ARB_fragment_shader扩展(由大多数当前的nVidia和ATI板支持)。要在将在各种硬件配置上运行的应用程序中使用该类,您应该有一个备份卷呈现方法。你应该创建一个vtkVolumeTextureMapper3D,分配它的输入,确保你有一个当前的OpenGL上下文(你已经渲染了至少一次),然后用一个vtkVolumeProperty作为参数调用IsRenderSupported()。如果输入有多个独立组件,或者如果图形硬件不支持使用两个实现方法中的至少一个所需的扩展集,则此方法将返回0。
7.17 vtkUnstructuredGrid的体积光线投射
vtkUnstructuredGridVolumeRayCastMapper是一个体映射器,它采用软件光线投射技术在非结构化网格上执行体渲染。使用默认的光线投射函数和集成方法,这个映射器比vtkProjectedTetrahedra方法更准确,但也明显慢一些。这个映射器通常比vtkUnstructuredGridZSweepMapper快,但是以内存消耗为代价获得这个速度,因此最好用于小型非结构化网格。
光线投射器是多线程的,以便在可用时使用多个处理器。与所有渲染vtkUnstructuredGrid数据的映射器一样,该映射器要求输入数据集完全由四面体元素组成,并且可以在必要时使用过滤器将输入数据四面体化。这个光线投射映射器有两种自定义方式。首先,您可以使用SetRayCastFunction()方法指定用于在非结构化网格中遍历光线的方法。指定的函数必须是vtkUnstructuredGridVolumeRayCastFunction的子类。目前有一个这样的子类存在于VTK中:vtkUnstructuredGridBunykRayCastFunction。本课程基于Paul Bunyk, Arie Kaufman和Claudio Silva在“不规则网格的简单,快速,健壮的光线投射”中描述的方法。这种方法非常占用内存(需要额外的显式数据副本),因此不应该用于非常大的数据。
您还可以指定一种方法,使用SetRayIntegrator()方法沿前入口点和后出口点之间的射线对与四面体相交的射线长度进行积分。指定的方法必须是vtkUnstructuredGridVolumeRayIntegrator的子类。VTK中存在几个可用的子类,当未指定时,映射器将选择一个适当的子类
给你上课。vtkUnstructuredGridHomogeneousRayIntegrator类在渲染单元格标量时是适用的。vtkUnstructuredGridLinearRayIntegrator执行分段线性射线积分。考虑到VTK 5.4中的传递函数是分段线性的,该类应该在大多数情况下给出“正确”的积分。但是,执行的计算量相当大,在大多数情况下,只能用作其他更快方法的基准。vtkUnstructuredGridPartialPreIntegration也执行分段线性射线积分,并将给出与vtkUnstructuredGridLinearRayIntegration相同的结果(由于表查找量化可能存在错误),但应该明显更快。使用的算法由Moreland和Angel给出,“用于非结构化数据的快速高精度体渲染器”。vtkUnstructuredGridPreIntegration通过查找预先计算的表来执行射线积分。结果应该等同于vtkUnstructuredGridLinearRayIntegrator和vtkUnstructuredGridPartialPreIntegration计算的结果,但比其中任何一个都快。预集成算法首先由Roettger, Kraus和Ertl在“基于细胞投影的硬件加速体和等面渲染”中介绍。
与结构化光线投射映射器类似,非结构化网格光线投射映射器将自动调整光线投射的数量,以实现所需的更新速率。由于这是一种纯软件技术,因此该方法在可用时利用多个处理器来提高性能。
7.18 ZSweep
vtkUnstructuredGridVolumeZSweepMapper渲染方法是基于Ricardo Farias, Joseph S. B. Mitchell和Claudio T. Silva在“ZSWEEP:一种有效而精确的非结构化体渲染投影算法”中描述的算法。这是一种软件投影技术,可以在任何平台上工作,但通常是VTK中可用的非结构化网格体渲染方法中最慢的。它比光线投射映射器(使用Bunyk函数)占用的内存少,因此能够渲染更大的体积。与光线投射映射器类似,可以使用SetRayIntegrator()方法指定特定的光线积分器。同样,将其保留为NULL将允许映射器为您选择合适的积分器。
7.19投影四面体
vtkProjectedTetrahedraMapper渲染方法是Shirley和Tuchman在“A Polygonal Approximation to Direct Scalar Volume rendering”中提出的经典投影四面体算法的实现。该方法利用OpenGL在给定视点上将四面体转换为三角形,然后在硬件加速下渲染这些三角形,从而提高渲染性能。然而,在这个类中使用的OpenGL方法不一定被所有驱动程序实现支持,并且可能产生工件。通常,该映射器将与光线投射器或ZSweep映射器一起使用,形成一个细节级方法,在交互过程中提供快速渲染,随后采用更准确的技术来生成最终图像。
在图7-11中,你可以看到用三种技术生成的非结构化网格体绘制图像的比较。投影四面体技术是交互式的,而其他两种技术在标准桌面系统上每个图像需要几秒钟的时间。vtkHAVSVolumeMapper是S. P. Callahan, M. Ikits, J. L. D. Comba和C. T. Silva在“非结构化体渲染的硬件辅助可视性排序”中提出的算法的实现。
代码是由Steven P. Callahan编写并贡献的。硬件辅助可视性排序(HAVS)算法首先在对象空间中对四面体网格的三角形进行排序,然后在图像空间中使用GPU上实现的固定大小的a缓冲区(称为kbuffer)对它们进行排序。HAVS算法擅长快速渲染大型数据集。权衡是,由于k大小不足(目前支持2或6)或读/写竞争条件,算法可能会产生一些渲染工件。
内建的细节层次(LOD)方法使用两种启发式方法(场或区域)中的一种对几何图形进行采样。如果启用LOD,则采样和渲染的几何体数量会动态变化,以保持在目标帧速率内。字段抽样方法通常最适用于单元格大小变化不大的数据集。相反,当体积在细胞大小上有很多变化时,面积采样方法给出了更好的近似。有关详细级别方法的更多信息,请参阅s.p. Callahan, j.l.d. Comba, P. Shirley和C. T. Silva撰写的“使用动态详细级别的大型非结构化网格的交互式渲染”。
HAVS算法在图形硬件上使用了几个高级特性。k-buffer排序网络是使用具有多个呈现目标(mrt)的帧缓冲对象(fbo)实现的。因此,只有支持这些功能的显卡才能运行该算法(至少是ATI 9500或NVidia NV40(6600))。
7.20速度与精度的权衡
如果您没有VolumePro卷渲染板、许多快速cpu或高端图形硬件,您可能不会对在场景中渲染一个或多个卷时实现的渲染速率感到满意。为了有效地与数据交互,通常有必要实现一定的帧速率,并且可能有必要为了实现速度而牺牲精度。幸运的是,对于许多体绘制方法,有很多方法可以做到这一点。事实上,它们中的一些将通过确定适当的精度级别来自动为您提供此功能,以便获得vtkRenderWindow中指定的所需更新率。
VTK默认支持vtkVolumeRayCastMapper、vtkFixedPointVolumeRayCastMapper、vtkUnstructuredGridVolumeRayCastMapper和vtkUnstructuredGridVolumeZSweepMapper实现所需的帧率。中设置所需的更新速率
或者在交互器中使用StillUpdateRate和DesiredUpdateRate(如果你正在使用一个)。由于这些渲染技术所需的时间主要取决于图像的大小,映射器将自动尝试通过减少投射的光线数量或生成的图像大小来实现所需的渲染率。缺省情况下,自动调节处于开启状态。为了保持交互性,应该在呈现窗口中指定一个中止检查过程,以便用户能够中断更高分辨率的图像,以便再次与数据进行交互。
为了达到期望的更新速率,图像的块化程度是有限制的。默认情况下,调整将允许图像变得相当块状-例如,如果有必要,为每个10x10像素邻域只投射1条光线,以达到所需的更新速率。此外,默认情况下,这些映射器不会生成大于填充屏幕上窗口所需的图像。这些限制可以在映射器中通过设置MinimumImageSampleDistance和MaximumImageSampleDistance来调整。此外,autoadjustsampledistance可以关闭,指定的ImageSampleDistance将用于表示图像平面上相邻像素之间的间距。如图7-12所示。
这种减少光线数量以达到交互帧率的技术是非常有效的。以图7-12所示的全分辨率图像为例。该图像可能需要4秒来计算,这对于数据交互(如旋转或转换数据,或交互式调整传递函数)来说太慢了。如果我们通过将ImageSampleDistance设置为2.0来代替沿着每个轴每隔一条射线进行子采样,我们将在大约1秒内获得像中间所示的图像。由于这对于有效的交互来说仍然太慢了,我们可以每4条射线进行一次抽样,并实现接近4帧每秒的渲染率,如图所示。它可能是块状的,但以每秒4帧的速度旋转块状体积比以每4秒一帧的速度旋转全分辨率体积要容易得多。
在纹理映射方法中,没有内置的自动技术来权衡准确性和速度。这可以由用户通过使用vtkImageResample创建一个较低分辨率的卷,并渲染这个新卷来相当容易地完成。由于纹理映射方法的速度高度依赖于体积的大小,这将达到与在光线投射方法中减少光线数量相似的结果。另一种选择是减少通过体积采样的平面数量。默认情况下,渲染的纹理四边形的数量将等于沿着主体轴的样本数量(由观看方向决定)。你可以设置MaximumNumberOfPlanes实例变量来减少纹理四边形的数量,ImageSampleDistance = 1.0 ImageSampleDistance = 2.0 ImageSampleDistance = 4.0图7-12改变图像采样距离对图像质量的影响默认值为0,表示对平面数量没有限制。
7.21使用vtkLODProp3D提高性能
vtkLODProp3D是一个3D道具,允许收集多个细节级别,并根据分配的渲染时间决定每个帧的渲染(参见第57页的“vtkLODProp3D”)。道具分配的渲染时间取决于渲染窗口的预期更新速率、渲染窗口中的渲染器数量、渲染器中的道具数量,以及筛选器可能根据屏幕覆盖率或其他重要因素做出的任何可能的调整。
使用vtkLODProp3D,可以将几种渲染技术收集到一个道具中,并允许道具决定使用哪种技术。这些技术可能跨越几种不同的渲染类,包括利用vtkPolyDataMapper的几何方法,以及用于结构化和非结构化数据的体积方法。
考虑以下简单的例子,创建vtkLODProp3D与三种不同形式的体渲染vtkImageData:
- vtkImageResample resampler
- resampler SetAxisMagnificationFactor 0 0.5
- resampler SetAxisMagnificationFactor 1 0.5
- resampler SetAxisMagnificationFactor 2 0.5
- vtkVolumeTextureMapper2D lowresMapper
- lowresMapper SetInput [resampler GetOutput]
- vtkVolumeTextureMapper2D medresMapper
- medresMapper SetInput [reader GetOutput]
- vtkVolumeRayCastMapper hiresMapper
- hiresMapper SetInput [reader GetOutput]
- vtkLODProp3D volumeLOD
- volumeLOD AddLOD lowresMapper volumeProperty 0.0
- volumeLOD AddLOD medresMapper volumeProperty 0.0
- volumeLOD AddLOD hiresMapper volumeProperty 0.0
为了清晰起见,本示例中省略了读取数据和设置可视化参数的许多步骤。在渲染时,将根据渲染LOD所需的估计时间和为该道具分配的时间,选择该道具的三个细节级别(LOD)中的一个。在本例中,所有三个lod使用相同的属性,但如果需要,它们也可以使用不同的属性。同样,在这种情况下,所有三个映射器都是vtkVolumeMapper的子类,但是我们可以添加一个边界框表示作为另一个LOD。如果我们渲染一个大型的vtkUnstructuredGrid数据集,我们可以通过使用最低分辨率的vtkPolyDataMapper添加轮廓表示来形成LOD,我们可以将数据重新采样到vtkImageData中并添加一个细节级别,使用3D纹理映射,我们可以添加用ZSweep映射器渲染的全分辨率非结构化数据作为最佳细节级别。
AddLOD()方法的最后一个参数是用于呈现此详细级别所需的估计时间的初始时间。将此值设置为0.0需要在确定估计的呈现时间之前渲染LOD一次。当vtkLODProp3D必须决定渲染哪个LOD时,如果有的话,它将选择一个具有0.0估计渲染时间的LOD。否则,如果能找到时间最大的LOD,它将选择不超过分配给道具渲染时间的LOD。否则,它将选择估计呈现时间最短的LOD。绘制当前帧的LOD所需的时间将取代该LOD对未来帧的估计渲染时间。
本书为英文翻译而来,供学习vtk.js的人参考。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。