Click or drag to resize
SoVolumeRender Class

Renders data volumes using direct volume rendering.

Inheritance Hierarchy

Namespace: OIV.VolumeViz.Nodes
Assembly: OIV.VolumeViz (in OIV.VolumeViz.dll) Version: 2023.1.3.0 (2023.1.3)
Syntax
public class SoVolumeRender : SoVolumeShape

The SoVolumeRender type exposes the following members.

Constructors
  NameDescription
Public methodSoVolumeRender

Constructor.

Top
Methods
  NameDescription
Public methodAffectsState

Overrides default method on OIV.Inventor.Nodes.SoNode.

(Inherited from SoShape.)
Public methodBeginShape(SoAction, SoShapeTriangleShapes)
Calls BeginShape(action, shapeType, (OIV.Inventor.Details.SoFaceDetail ^)nullptr).
(Inherited from SoShape.)
Public methodBeginShape(SoAction, SoShapeTriangleShapes, SoFaceDetail)

These methods can be used by subclasses to generate triangles more easily when those triangles are part of a larger structure, such as a triangle strip, triangle fan, or triangulated polygon, according to the TriangleShape enumerated type.

(Inherited from SoShape.)
Public methodCallback

Implements primitive generation for all shapes.

(Inherited from SoShape.)
Public methodComputeBBox(SoAction, SbXfBox3d, SbVec3d)

Compute object oriented bounding box (OOB) for subclass using information in the given action (which may not necessarily be an OIV.Inventor.Actions.SoGetBoundingBoxAction).

(Inherited from SoShape.)
Public methodComputeBBox(SoAction, SbBox3f, SbVec3f)
Public methodCopy
Calls Copy(false).
(Inherited from SoNode.)
Public methodCopy(Boolean)

Creates and returns an exact copy of the node.

(Inherited from SoNode.)
Public methodCopyFieldValues(SoFieldContainer)
Calls CopyFieldValues(fc, false).
(Inherited from SoFieldContainer.)
Public methodCopyFieldValues(SoFieldContainer, Boolean)

Copies the contents of fc's fields into this object's fields.

(Inherited from SoFieldContainer.)
Public methodDispose
Releases all resources used by SoDisposable.
(Inherited from SoDisposable.)
Public methodDistribute
(Inherited from SoNode.)
Public methodDoAction
(Inherited from SoNode.)
Public methodEnableNotify

Notification at this Field Container is enabled (if flag == true) or disabled (if flag == false).

(Inherited from SoFieldContainer.)
Public methodEndShape

end shape previously started with OIV.Inventor.Nodes.SoShape.BeginShape(OIV.Inventor.Actions.SoAction, OIV.Inventor.Nodes.SoShape.TriangleShapes, OIV.Inventor.Details.SoFaceDetail).

(Inherited from SoShape.)
Public methodEquals
Determines whether the specified Object is equal to the current Object.
(Inherited from Object.)
Public methodFieldsAreEqual

Returns true if this object's fields are exactly equal to fc's fields.

(Inherited from SoFieldContainer.)
Public methodGet

Returns the values of the fields of this object in the Open Inventor ASCII file format in the given string.

(Inherited from SoFieldContainer.)
Public methodGetAllFields

Returns a list of fields, including the eventIn's and eventOut's.

(Inherited from SoFieldContainer.)
Public methodGetAlternateRep

This method is called by actions to allow the node to provide an "alternate representation" when appropriate (typically depending on the action type).

(Inherited from SoNode.)
Public methodGetBoundingBox

Implements bounding box method using virtual OIV.VolumeViz.Nodes.SoVolumeShape.ComputeBBox(OIV.Inventor.Actions.SoAction, OIV.Inventor.SbBox3f@, OIV.Inventor.SbVec3f@) method.

(Inherited from SoVolumeShape.)
Public methodGetEventIn

Returns a the eventIn with the given name.

(Inherited from SoFieldContainer.)
Public methodGetEventOut

Returns the eventOut with the given name.

(Inherited from SoFieldContainer.)
Public methodGetField

Returns a the field of this object whose name is fieldName.

(Inherited from SoFieldContainer.)
Public methodGetFieldName

Returns the name of the given field in the fieldName argument.

(Inherited from SoFieldContainer.)
Public methodGetFields

Appends references to all of this object's fields to resultList, and returns the number of fields appended.

(Inherited from SoFieldContainer.)
Public methodGetHashCode
Overrides GetHashCode().
(Inherited from SoNetBase.)
Public methodGetMatrix
(Inherited from SoNode.)
Public methodGetName

Returns the name of an instance.

(Inherited from SoBase.)
Public methodGetPrimitiveCount

Counts number of primitives produced by this shape.

(Inherited from SoShape.)
Public methodGetRenderEngineMode

Returns the supported Render engine mode.

(Inherited from SoNode.)
Public methodGetRenderUnitID
(Inherited from SoVolumeShape.)
Public methodGetShapeType

Gets the current shape Full Scene Antialiasing type.

(Inherited from SoShape.)
Public methodGetStringName (Inherited from SoBase.)
Public methodGetType
Gets the Type of the current instance.
(Inherited from Object.)
Public methodGLRender
(Inherited from SoVolumeShape.)
Public methodGLRenderBelowPath
(Inherited from SoNode.)
Public methodGLRenderInPath
(Inherited from SoNode.)
Public methodGLRenderOffPath
(Inherited from SoNode.)
Public methodGrabEventsCleanup
(Inherited from SoNode.)
Public methodGrabEventsSetup
(Inherited from SoNode.)
Public methodHandleEvent
(Inherited from SoNode.)
Public methodHasDefaultValues

Returns true if all of the object's fields have their default values.

(Inherited from SoFieldContainer.)
Public methodIsBoundingBoxIgnoring

Used by BoundingBoxAction to know if bounding box computation should be ignored or not.

(Inherited from SoShape.)
Public methodIsNotifyEnabled

Notification is the process of telling interested objects that this object has changed.

(Inherited from SoFieldContainer.)
Public methodIsOverride

Returns the state of the override flag.

(Inherited from SoNode.)
Public methodIsSynchronizable

Gets the ScaleViz synchronizable state of this object.

(Inherited from SoBase.)
Public methodPick
(Inherited from SoNode.)
Public methodRayPick

Implements picking along a ray by intersecting the ray with each primitive generated by subclass.

(Overrides SoShapeRayPick(SoRayPickAction).)
Public methodSearch
(Inherited from SoNode.)
Public methodSet

Sets one or more fields in this object to the values specified in the given string, which should be a string in the Open Inventor file format.

(Inherited from SoFieldContainer.)
Public methodSetName (Inherited from SoBase.)
Public methodSetOverride

Turns the override flag on or off.

(Inherited from SoNode.)
Public methodSetRenderProgress

Set an application defined OIV.Inventor.SoProgressIndicator object which will raise an event before and after the volume rendering task, before and after each subtask (in this case: Texture creation and Geometry creation) and after each step in the subtasks which represents in most cases individual tiles of data.

Public methodSetShapeType

set the antialiasing type for this shape.

(Inherited from SoShape.)
Public methodSetSynchronizable

Sets this to be a ScaleViz synchronizable object.

(Inherited from SoBase.)
Public methodSetToDefaults

Sets all fields in this object to their default values.

(Inherited from SoFieldContainer.)
Public methodShapeVertex

add a primitive vertex to the shape prevously started with OIV.Inventor.Nodes.SoShape.BeginShape(OIV.Inventor.Actions.SoAction, OIV.Inventor.Nodes.SoShape.TriangleShapes, OIV.Inventor.Details.SoFaceDetail).

(Inherited from SoShape.)
Public methodToString
Converts this SoBase structure to a human readable string.
(Inherited from SoBase.)
Public methodTouch

Marks an instance as modified, simulating a change to it.

(Inherited from SoNode.)
Public methodWrite
(Inherited from SoNode.)
Top
Properties
  NameDescription
Public propertyAbortCallback

Sets delegateto call during texture map rendering to test for an abort condition.

Public propertyboundingBoxIgnoring

Whether to ignore this node during bounding box traversal.

(Inherited from SoShape.)
Public propertycomposition Obsolete.

Specifies color composition mode.

(Inherited from SoVolumeShape.)
Public propertydataSetIds

Specifies the list of volumes on which volume rendering is applied.

Public propertyfixedNumSlicesInRoi

When this field is set to false (the default), the number of samples set by OIV.VolumeViz.Nodes.SoVolumeRender.numSlices is the number of samples used for the region defined by the current ROI.

Public propertyinterpolation

Interpolation mode.

(Inherited from SoVolumeShape.)
Public propertyIsDisposable
ISafeDisposable interface implementation.
(Inherited from SoDisposable.)
Public propertylightDirection Obsolete.

Light direction (relative to the volume).

Public propertylighting Obsolete.

Indicates if lighting is required.

Public propertylightIntensity Obsolete.

Light intensity in the range [0-1].

Public propertylowResMode

Sets the method to use when moving in low resolution.

Public propertylowScreenResolutionScale

If OIV.VolumeViz.Nodes.SoVolumeRender.lowResMode is DECREASE_SCREEN_RESOLUTION, render the volume at a lower screen resolution.

Public propertynumSlices

Specifies the number of samples along each ray.

Public propertynumSlicesControl

Controls how the number of samples along each ray is determined.

Public propertyopacityCorrection

Controls whether opacity correction is done.

Public propertyopacityThreshold

Specifies a threshold opacity (alpha) value that defines voxels considered to be "solid" (non-transparent).

Public propertyprojectedTileSubdivision

When doing volume projection (see OIV.Inventor.Nodes.SoProjection), only the geometry (corner vertices) of the LDM tiles are projected, not the individual voxels.

Public propertyrenderMode

Specifies how the voxels along each sampling ray are combined to form the final image.

Public propertysamplingAlignment

Specifies which technique to use to align rayCast samples.

Public propertysubdivideTile

If true, LDM tiles will be subdivided for rendering.

Public propertyUserData
Gets or sets the user data to be contained by the field container.
(Inherited from SoFieldContainer.)
Public propertyviewAlignedSlices Obsolete.
Top
Remarks

This node renders volume data using "direct volume rendering". The volume process involves sampling, interpolation, classification and composition. By default the rendering algorithm is a GPU-based technique called raycasting. Raycasting has similarities to the well known algorithm called raytracing, but is specialized for volume data (renders voxels not triangles) and does not currently implement reflection or refraction. One or more "rays" are cast through the volume from each relevant pixel on the screen. Samples are taken at intervals along each ray. The sample interval is controlled by the OIV.VolumeViz.Nodes.SoVolumeRender.numSlices and OIV.VolumeViz.Nodes.SoVolumeRender.numSlicesControl fields At each sample point a value is interpolated from the closest voxels. The interpolation technique is controlled by the interpolation field. Classification means that color and opacity are computed based on the current OIV.LDM.Nodes.SoDataRange and OIV.LDM.Nodes.SoTransferFunction (and possibly values from other volumes - see OIV.VolumeViz.Nodes.SoVolumeShader). Optional rendering effects may modify the base color and/or opacity. These effects are controlled by an OIV.VolumeViz.Nodes.SoVolumeRenderingQuality node and include lighting, shadows, edge coloring, boundary opacity and more. The sample is then composited with other samples along the ray. Composition is controlled by the OIV.VolumeViz.Nodes.SoVolumeRender.renderMode field. By default colors are combined based on the opacity of the sample (alpha blending). The ray is terminated if it reaches full opacity or reaches the current depth in the depth buffer.

For a volume containing scalar data values, each voxel's basic RGBA value is determined by the current OIV.LDM.Nodes.SoDataRange and OIV.LDM.Nodes.SoTransferFunction nodes. This value is combined with current diffuse color and transparency (set, for example, with an OIV.Inventor.Nodes.SoMaterial node). This means that, for example, the current transparency can be used as a global alpha scale factor to decrease the opacity of all voxels. By default, scalar values are loaded on the GPU and the GPU interpolates between the data values before applying the color map. See the interpolation field for options. To force RGBA values to be loaded (implies color map is applied on the CPU and GPU interpolates between color values), see the usePalettedTexture field in the OIV.VolumeViz.Nodes.SoVolumeData node.

For an RGBA volume, each voxel's base RGBA value comes directly from the volume data (OIV.LDM.Nodes.SoDataRange and OIV.LDM.Nodes.SoTransferFunction are ignored). However if lighting is enabled, the final voxel color is also affected by the emissiveColor, specularColor and shininess fields of OIV.Inventor.Nodes.SoMaterial.

The OIV.VolumeViz.Nodes.SoVolumeRender.samplingAlignment field controls whether the samples are axis aligned (perpendicular to one axis of the volume), view aligned (perpendicular to the view direction) or boundary aligned (each ray starts at the first intersected voxel with alpha value > 0). Generally boundary aligned slices should be used (better image quality). Using OIV.VolumeViz.Nodes.SoVolumeGroup, SoVolumeIsoSurface or OIV.Inventor.Nodes.SoProjection nodes will automatically switch to view-aligned samples.

The property nodes SoVolumeIsoSurface and OIV.VolumeViz.Nodes.SoVolumeDataDrawStyle add additional rendering styles and can be used, for example, to force OIV.VolumeViz.Nodes.SoVolumeRender to draw a GPU computed isosurface instead of volume rendering.

Example rendering:

lighting = false lighting = true
composition = ALPHA_BLENDING composition = MAX_INTENSITY

Multiple volumes:

VolumeViz provides several mechanisms for combining or rendering multiple volumes, depending on the application's specific requirements. There are several cases:

  • CPU data combining The data values from multiple volumes can be combined on the CPU during data loading, using the OIV.LDM.Nodes.SoDataCompositor node. For example computing the "difference" between two volumes on the fly. OIV.LDM.Nodes.SoLDMDataTransform and OIV.VolumeViz.Nodes.SoVolumeTransform can be used to modify data for a single volume, for example scaling data values or computing a derived data set. OIV.LDM.Nodes.SoLDMDataTransform is applied when data is loaded and OIV.VolumeViz.Nodes.SoVolumeTransform is applied before data is transferred to the GPU.

  • Multiple data sets These are volumes that are really multiple data sets on the same "grid", in other words volumes having exactly the same dimensions and extent. For example, seismic attribute volumes. These volumes can be combined on the GPU using a simple fragment shader function. For example, replacing the VVizComputeFragmentColor (GLSL) function allows "co-blending" the colors from multiple volumes. See OIV.VolumeViz.Nodes.SoVolumeShader for more details. Use an OIV.LDM.Nodes.SoMultiDataSeparator node to group the volume data nodes that should be combined.

  • Multiple independent volumes These are volumes in the same scene that are completely independent (but might overlap in space) or have different dimensions or different extents. For example, medical datasets from different modalities. In order to ensure that the rendering of overlapping transparent volumes is properly interleaved, there are 2 options:

Custom shaders:

The OIV.VolumeViz.Nodes.SoVolumeShader node allows a variety of custom shader functions to be defined for special computation or rendering effects on single volumes or multiple volumes. All of these features require programmable shader support on the GPU. Be sure to use an OIV.LDM.Nodes.SoMultiDataSeparator (instead of OIV.Inventor.Nodes.SoSeparator) when combining multiple volumes.

Composition of multiple independent volumes:

It is possible to compose datasets that have different dimensions, tile sizes and transformations. In those cases, the volume rendering will be applied on the union of the extents of all the OIV.VolumeViz.Nodes.SoVolumeData nodes on state. In addition, texture coordinates conversion functions are provided in the VolumeViz/vvizStructure.h shader include in order to help fetch the correct data values in custom shaders. For instance,

vec3 VVizTextureToTextureVec(in VVizDataSetId datasetSrc, in VVizDataSetId datasetDst, in vec3 texCoord);
can be used to convert texture coordinates related to one dataset to texture coordinates related to another dataset. The conversion is based solely on the transformations applied to each dataset, which are defined by their model matrix and their extent. Please note that the model matrix of a dataset is defined by to the OIV.Inventor.Nodes.SoTransformation nodes that are placed before the OIV.LDM.Nodes.SoDataSet node in the order of the traversal.

See OIV.VolumeViz.Nodes.SoVolumeShader.ShaderPositions.FRAGMENT_COMPUTE_COLOR for an example of a shader implementing the VVizComputeFragmentColor() function in that case.

Lighting:

The OIV.VolumeViz.Nodes.SoVolumeRenderingQuality property node allows you to to enable GPU computed lighting based on the first OIV.Inventor.Nodes.SoLight node in the scene graph. (Note that this is typically the viewer's "headlight".) VolumeViz supports two lighting algorithms. They are both computed on the GPU and are independent (but normally only one should be enabled).

  • Gradient lighting Computes the effect of lighting for every sample along the ray, similar to the computation for polygonal geometry, but using a gradient vector computed from the voxel data values instead of a normal vector. Gradient lighting is enabled by the OIV.VolumeViz.Nodes.SoVolumeRenderingQuality lighting field. Gradient lighting is the classic solution but can have problems in relatively homogeneous regions where the gradient magnitude is small and gradient direction somewhat random. Multiple fields affect this computation including interpolation, gradientThreshold, gradientQuality and surfaceScalarExponent.

  • Deferred lighting Computes the effect of lighting for every visible voxel, as a post-processing step, using a gradient vector computed from the (visible) voxel depth values. Deferred lighting is enabled by the OIV.VolumeViz.Nodes.SoVolumeRenderingQuality deferredLighting field. Deferred lighting generally provides better performance because fewer lighting computations are needed. Deferred lighting generally provides better image quality for volumes that inherently contain surfaces (sharp gradients) like medical and industrial scans. Deferred lighting is most effective when the opacity values in the transfer function are approximately binary (mostly 0 and 1 values). See also the OIV.VolumeViz.Nodes.SoVolumeRender.opacityThreshold field.

Warning: CPU computed lighting can be enabled using the OIV.VolumeViz.Nodes.SoVolumeRender.lighting field in this node. In this case the OIV.VolumeViz.Nodes.SoVolumeRender.lightDirection and OIV.VolumeViz.Nodes.SoVolumeRender.lightIntensity fields control the light. Note that when lighting is computed on the CPU, RGBA textures are loaded on the GPU, so color map changes generally require re-loading all the data textures. NOTE: This feature is obsolete. Use OIV.VolumeViz.Nodes.SoVolumeRenderingQuality.lighting instead.

Gradient lighting Deferred lighting

Shadows:

Open Inventor shadow rendering works for volume rendering similar to any other geometry. When shadow rendering is enabled (see OIV.Inventor.Nodes.SoShadowGroup), non-transparent voxels can cast and receive shadows (see OIV.Inventor.Nodes.SoShadowStyle). Shadow rendering is independent of whether lighting is enabled for the volume.

OIV.VolumeViz.Nodes.SoVolumeRender also supports "ambient occlusion" rendering (see ambientOcclusion field in OIV.VolumeViz.Nodes.SoVolumeRenderingQuality). This rendering mode is visually a kind of self-shadowing and represents an approximation of the effect of ambient global lighting in the scene. Ambient occlusion can be combined with gradient or deferred lighting and with shadow casting.

Shadow casting Ambient occlusion

Clipping:

VolumeViz provides multiple tools for clipping volume rendering. Any or all of these tools may be combined for more complex clipping situations. Note that these tools clip all volume shapes including slices.

Picking:

OIV.Inventor.Actions.SoRayPickAction handles picking of VolumeViz shapes similar to other geometry in the scene, but with additional features. Picking on an OIV.VolumeViz.Nodes.SoVolumeRender node can return the first non-transparent voxel "hit" or the entire set of intersected voxels along the pick ray, depending on the pickAll flag of the OIV.Inventor.Actions.SoRayPickAction. Similar to other geometry, OIV.Inventor.SoPickedPoint can return a "detail" class specific to OIV.VolumeViz.Nodes.SoVolumeRender. OIV.VolumeViz.Details.SoVolumeRenderDetail returns the IJK (voxel coordinate) position of the pick and the data value at that point.

Since Open Inventor 8.6, the OIV.VolumeViz.Nodes.SoVolumeRender node (by default) uses the GPU to compute the picked voxel during an OIV.Inventor.Actions.SoRayPickAction. For this to work, the OIV.Inventor.Actions.SoRayPickAction must have its scene manager initialised using the method OIV.Inventor.Actions.SoAction.SetSceneManager(OIV.Inventor.SoSceneManager). OIV.Inventor.Actions.SoHandleEventAction does this automatically, so it is not necessary for the application to take any action when using (for example) an OIV.Inventor.Nodes.SoEventCallback node and calling the getPickedPoint() method. However if the application creates its own OIV.Inventor.Actions.SoRayPickAction then it should set the scene manager. If no scene manager is specified, a warning message is issued and software picking is done. If necessary, using the GPU for volume picking may be disabled by setting the environment variable IVVR_GPU_PICKING to 0 (see OIV.Inventor.SoPreferences).

Projection:

The OIV.VolumeViz.Nodes.SoVolumeRender node supports projected volume rendering, for example rendering a volume defined on a grid of latitude / longitude coordinates. Projection is enabled by adding an OIV.Inventor.Nodes.SoProjection node before the OIV.VolumeViz.Nodes.SoVolumeRender node (see OIV.Inventor.Nodes.SoProjection for more information about supported coordinate systems, ellipsoids and map projections). The projection quality versus speed ratio can be controlled using the new OIV.VolumeViz.Nodes.SoVolumeRender.projectedTileSubdivision field that defines how often each tile's geometry will be subdivided when projected. This is important because only the corner points of the tiles are projected, not the individual voxels. So subdividing the tiles provides a better approximation of the actual shape of the grid. Volume projection works with both regular (uniform voxel spacing) and rectilinear (non-uniform voxel spacing) grids. OIV.Inventor.Nodes.SoProjection automatically selects view-aligned sampling.

Warning:

  • Volume projection is incompatible with some options enabled by the VolumeRenderingQuality node. Do not enable the preIntegrated, jittering or edgeDetect2D fields.

  • Volume projection requires all culling to be disabled. The following options in class OIV.LDM.SoLDMGlobalResourceParameters should be disabled: setScreenResolutionCulling (default is false), setViewpointRefinement (default is true) and setViewCulling (default is true).

Performance:

Volume rendering performance is affected by many factors including the size of the volume and the rendering options selected. Image quality is also affected by many rendering options and, in general, higher quality implies lower performance. Some of the factors affecting volume rendering performance are:

  • Number of voxels: This mainly depends on the size of the volume, but can be reduced using an OIV.LDM.Nodes.SoROI (region of interest) node.

  • Number of pixels: A larger number of pixels means a larger number of rays must be cast through the volume and therefore the shader execution time on the GPU will be longer. This effect is most noticeable when high quality rendering options are enabled. The number of pixels rendered can be temporarily reduced by setting the OIV.VolumeViz.Nodes.SoVolumeRender.lowResMode field to DECREASE_SCREEN_RESOLUTION. This reduces the number of times the shader programs running on the GPU must be executed.

  • Number of samples (slices): This is controlled by the OIV.VolumeViz.Nodes.SoVolumeRender.numSlices and OIV.VolumeViz.Nodes.SoVolumeRender.numSlicesControl fields. Note that better image quality can obtained with the same number of samples by enabling options like preintegrated rendering (see OIV.VolumeViz.Nodes.SoVolumeRenderingQuality) and/or the BOUNDARY_ALIGNED setting for OIV.VolumeViz.Nodes.SoVolumeRender.samplingAlignment. The number of samples can be automatically decreased when interacting using an OIV.Inventor.Nodes.SoInteractiveComplexity node. Since Open Inventor 9.2, we recommend to set the OIV.VolumeViz.Nodes.SoVolumeRender.numSlicesControl field to AUTOMATIC and the OIV.VolumeViz.Nodes.SoVolumeRender.numSlices field to -1. The number of samples will be computed based on the dimensions of the volume (number of voxels on each axis), the OIV.Inventor.Nodes.SoComplexity.value setting and the viewing direction. If the viewing direction changes, the number of samples will be automatically adjusted.

  • Opacity: Increasing the number of opaque, or nearly opaque, voxels in the volume (using OIV.LDM.Nodes.SoTransferFunction) will generally improve performance because the sampling rays can terminate sooner. See also IVVR_ALPHA_THRESHOLD_INTERACTIVE in OIV.Inventor.SoPreferences. If you are using a completely opaque transfer function, for example with a "volume probe", OIV.VolumeViz.Nodes.SoVolumeSkin will generate the same image much faster.

  • Rendering options: Many of the advanced rendering options and rendering effects enabled by OIV.VolumeViz.Nodes.SoVolumeRenderingQuality have an additional performance cost. These include lighting, edge coloring, boundary opacity, cubic interpolation and gradient quality. These settings can be automatically changed while interacting using an OIV.Inventor.Nodes.SoInteractiveComplexity node.

  • Tile size: For backward compatibility, the default tile size is still only 64. This is quite small for modern CPU/GPU hardware. The smaller the tile size, the larger the total number of tiles that must be managed by VolumeViz. This overhead can be significant, especially for operations that require reloading the data textures on the GPU, for example, changing the data range (OIV.LDM.Nodes.SoDataRange). For smaller volumes, like 512^3, it can be efficient to set the tile size large enough to contain the entire volume. For very large volumes, larger tile sizes are efficient for OIV.VolumeViz.Nodes.SoVolumeRender but somewhat inefficient for slice rendering because complete tiles must be loaded even though the slice only uses part of the data (see also OIV.VolumeViz.Nodes.SoSlice.largeSliceSupport). Applications should experiment. For volumes stored in LDM file format, the tile size must be specified when the volume is converted to LDM (see OIV.LDM.Converters.SoConverter and the "-t" option). For other data data formats the tile size can be specified using the OIV.VolumeViz.Nodes.SoVolumeData node's ldmResourceParameters field, but only after setting the filename field or calling the SetReader()method.

  • LDM_USE_IN_MEM_COMPRESSION This environment variable (see OIV.Inventor.SoPreferences) affects much more than its name implies. VolumeViz always manages data as "tiles", regardless of the data format. In many cases VolumeViz must create (or uncompress) the tiles at run time. These cases include in-memory volumes, any volume reader that does not implement the readTile() method (all built-in formats except LDM) and compressed LDM format files. If this variable is true ( the default value), then VolumeViz only keeps a small cache of tiles in memory. See the OIV.Inventor.Devices.SoBufferObject method getBufferObjectCache() for the current default and note that this setting is separate from the max main memory parameter. If a tile's data is needed and that tile is not in the cache, the tile must be recreated. This overhead can be significant, especially for operations that require recreating data textures on the GPU, for example, changing the data range (OIV.LDM.Nodes.SoDataRange). We recommend setting this variable to false unless the memory conserving feature for compressed tiles is critical.

    VolumeViz keeps tiles from a compressed LDM format file in CPU memory as compressed data. This option allows more tiles to be kept in the same amount of CPU memory, but incurs a performance penalty because a tile must be uncompressed when its data is needed, unless it is found in a small cache of uncompressed tiles (see OIV.Inventor.Devices.SoBufferObject.GetBufferObjectCache()). This penalty is particularly noticeable for operations that require recreating data textures on the GPU, for example changing the data range (OIV.LDM.Nodes.SoDataRange).

If rendering performance is too slow, it may be necessary to render with high quality settings when the user is not interacting with the scene, but temporarily switch to high performance (lower quality) settings when the user is interacting. Open Inventor automatically sets "interactive mode" when the user is moving the camera or moving a dragger. The application can explicitly set interactive mode, for example while the user is moving a slider in the user interface. Some important tools are:

Limitations:

  • Multi-thread rendering: Unlike most Open Inventor nodes, VolumeViz nodes do not support simultaneous rendering in multiple threads (even when Open Inventor is initialized using one of the initThread() methods).

  • Geometric transforms: The volume size (extent in 3D space) and orientation can be modified by transformation nodes in the scene graph just like any geometry. For a volume this in turn modifies the appearance of volume rendering nodes like OIV.VolumeViz.Nodes.SoVolumeRender. However please note: The same transformation must be applied to the volume data node and all volume rendering nodes associated with that volume. So effectively any transformation nodes that affect the volume must be placed before the volume data node.

  • Multiple dataset : The field OIV.VolumeViz.Nodes.SoVolumeRender.dataSetIds has limitations described here

  • Render modes: When using one of the projection render mode (i.e. MIN_INTENSITY_PROJECTION, MAX_INTENSITY_PROJECTION, SUM_INTENSITY_PROJECTION or AVERAGE_INTENSITY_PROJECTION):

EXAMPLE

For simple data sets, a basic VolumeViz rendering could be achieved with only a few nodes: minimally an OIV.VolumeViz.Nodes.SoVolumeData node to identify the data set and one rendering node. However most data sets need at least some of the additional nodes shown here in order to get a correct and useful rendering. Most applications will need additional nodes to take advantage of region of interest, interaction, clipping and other VolumeViz features. Please consider the code shown here as simply a guideline and a starting point for exploring the many powerful features available in Open Inventor.

Note that some of the property nodes (data, material, color map, etc) will typically be shared by multiple rendering nodes. For example the volume data usually only needs to be loaded once, using a single OIV.VolumeViz.Nodes.SoVolumeData node. Multiple slices and/or regions can be rendered using that data node and they may use the same transfer function or each have their own.

Also note that this example is for a data volume, not a label volume. Please see the notes about label volumes following the code block.

SoPreferences.SetValue( "LDM_USE_IN_MEM_COMPRESSION", "0" );

SoSeparator volSep = new SoSeparator();
 root.AddChild( volSep );

SoInteractiveComplexity interact = new SoInteractiveComplexity();
 // Decrease "number of samples"
 interact.fieldSettings[0] = "SoComplexity value 0.2 0.5";
 // Decrease interpolation quality. 
 interact.fieldSettings[1] = "SoVolumeRender interpolation LINEAR CUBIC";
 // Don't wait before returning to full quality rendering.
 interact.refinementDelay.Value = 0;
 volSep.AddChild( interact );

SoComplexity volComp = new SoComplexity();
 volSep.AddChild( volComp );

SoVolumeData volData = new SoVolumeData();
 volData.fileName.Value = "$OIVNETHOME/src/demos/data/VolumeViz/3DHead.ldm";
 volSep.AddChild( volData );

SoDataRange volRange = new SoDataRange();
if (volData.GetDatumSize() > 1)
{
   double minVal, maxVal;
   volData.GetMinMax( out minVal, out maxVal);
   volRange.min.Value = minVal;
   volRange.max.Value = maxVal;
}
volSep.AddChild( volRange );

SoTransferFunction volTF = new SoTransferFunction();
 volTF.predefColorMap.Value = SoTransferFunction.PredefColorMaps.GRAY;
 volTF.minValue.Value = 10; // Make "noise" voxels transparent
 volSep.AddChild( volTF );

SoMaterial volMat = new SoMaterial();
 volMat.diffuseColor.SetValue(1, 1, 1);
 volSep.AddChild( volMat );

SoVolumeRenderingQuality volQual = new SoVolumeRenderingQuality();
 // Remove border artifacts while moving. 
 volQual.interpolateOnMove.Value = true;
 // Higher quality rendering
 volQual.preIntegrated.Value = true;
 // Optional: Enable screen space lighting
 volQual.deferredLighting.Value = true;
 // Optional: If using gradient lighting, increase quality
 volQual.surfaceScalarExponent.Value = 5;
 volSep.AddChild( volQual );

SoVolumeRender volRend = new SoVolumeRender();
 // Let Inventor compute best number of slices
 volRend.numSlicesControl.Value = SoVolumeRender.NumSlicesControls.AUTOMATIC;
 // Optional: Use lower screen resolution while moving.
 volRend.lowResMode.Value = SoVolumeRender.LowResModes.DECREASE_SCREEN_RESOLUTION;
 volRend.lowScreenResolutionScale.Value = 2;
 // Remove "slicing" artifacts
 volRend.samplingAlignment.Value = SoVolumeRender.SamplingAlignments.BOUNDARY_ALIGNED;
 volSep.AddChild( volRend );

Label volumes

A label volume, also known as a label field, is usually the result of doing some sort of segmentation on a data volume. Each voxel value is an integer label (id) identifying which material, object, etc that the voxel belongs to. There could be 100's or 1000's of labels, but there might be as few as 8 label values. For example, a simple label volume might have 7 opaque materials plus plus an "exterior" material which is completely transparent. Conceptually, there is one big difference between a (typical) data volume and a label volume. A data volume is conceptually a set of discrete samples taken from a continuous scalar field. So we know the exact value at the center of each voxel and interpolate between those values to get the value at any position in between voxels. In a label volume we normally consider each voxel to belong completely to one material, so the value is constant until we cross the boundary into the next voxel. Therefore we do not want to interpolate the label values.

When rendering a label volume, make the following changes to the above example:

If rendering isosurfaces (OIV.VolumeViz.Nodes.SoVolumeIsosurface), set the OIV.VolumeViz.Nodes.SoVolumeRenderingQuality segmentedInterpolation field to true.

It is also important to set the data range, texture precision and color map size carefully. Please see the label volume discussion in OIV.LDM.Nodes.SoTransferFunction.

FILE FORMAT/DEFAULT

VolumeRender {
interpolation LINEAR
lighting false
lightDirection -1, -1, -1
lightIntensity 1
numSlices 0
numSlicesControl AUTOMATIC
samplingAlignment BOUNDARY_ALIGNED
lowResMode DECREASE_NONE
lowScreenResolutionScale 1
subdivideTile false
projectedTileSubdivision 1
fixedNumSlicesInRoi false
opacityCorrection true
renderMode VOLUME_RENDERING
dataSetIds []
}

ACTION BEHAVIOR

OIV.Inventor.Actions.SoGLRenderAction Draws a volume-rendered image based on current OIV.VolumeViz.Nodes.SoVolumeData.

OIV.Inventor.Actions.SoGetBoundingBoxAction Computes the bounding box that encloses the volume.

OIV.Inventor.Actions.SoRayPickAction Since Open Inventor version 8.5, picking always returns the first non-transparent voxel intersected by the pick ray. The old behavior can be restored by using an OIV.Inventor.Nodes.SoPickStyle node set to BOUNDING_BOX.

See Also