Click or drag to resize
SoTransferFunction Class

Describes the association between data set values and colors.

Inheritance Hierarchy

Namespace: OIV.LDM.Nodes
Assembly: OIV.LDM (in OIV.LDM.dll) Version: 2024.1.1.0 (2024.1.1)
Syntax
public class SoTransferFunction : SoNode

The SoTransferFunction type exposes the following members.

Constructors
  NameDescription
Public methodSoTransferFunction

Constructor.

Top
Methods
  NameDescription
Public methodAffectsState

Returns true if a node has an effect on the state during traversal.

(Inherited from SoNode.)
Public methodCallback
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
Public methodEnableNotify

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

(Inherited from SoFieldContainer.)
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
(Inherited from SoNode.)
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 methodGetPackedColorMap
Public methodGetPackedColorMapCustom
Public methodGetPrimitiveCount
(Inherited from SoNode.)
Public methodGetRenderEngineMode

Returns the supported Render engine mode.

(Inherited from SoNode.)
Public methodGetRenderUnitID
(Inherited from SoNode.)
Public methodGetStringName (Inherited from SoBase.)
Public methodGetType
Gets the Type of the current instance.
(Inherited from Object.)
Public methodGLRender
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 methodHasTransparency

Returns true if the current color map contains alpha values less than 1.

Public methodIsBoundingBoxIgnoring

This method is used by getBoundingBox action traversal to know if the current node must be traversed or not, ie the bounding should be ignored.

(Inherited from SoNode.)
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 methodLoadColormap

Loads a colormap from a file.

Public methodPick
Public methodRayPick
(Inherited from SoNode.)
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 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 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
Top
Properties
  NameDescription
Public propertyactualColorMap

This field contains the actual color map used.

Public propertycolorMap

Array of floats in the range [0,1] defining a color map.

Public propertycolorMapType

ColorMap type (number of color components).

Public propertyfauxShadingDarkenThreshold

Opacity threshold for darkening edges.

Public propertyfauxShadingLength

Controls how many color values will be affected by faux shading.

Public propertyfauxShadingStrength

Controls how much faux shading will darken the color values of the transfer function.

Public propertyIsDisposable
ISafeDisposable interface implementation.
(Inherited from SoDisposable.)
Public propertymaxValue
Public propertyminValue

Remaps the defined color map between minValue and maxValue indices.

Public propertyoffset

Used for rescaling the input data values.

Public propertypredefColorMap

Predefined color map to use.

Public propertyshift

Used for rescaling the input data values.

Public propertytransferFunctionId

This field allows the use of multiple transfer functions.

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

This node defines a mapping from a range of scalar values to a set of color and alpha (inverse of transparency) values.

OIV.LDM.Nodes.SoTransferFunction has no effect on rendering an RGBA volume (OIV.VolumeViz.Nodes.SoVolumeData.dataRGBA field contains true). In this case the voxel values are explicitly color values already.

# Data range

The range of scalar values is specified by an OIV.LDM.Nodes.SoDataRange node. If the application does not provide an OIV.LDM.Nodes.SoDataRange node, then the full range of the volume's data type is mapped into the color map. This can be appropriate and convenient for 8-bit (byte) data, but is usually incorrect for larger data types, especially float values. For example, DICOM data is normally stored in 16-bit voxels, which would have a default data range of 0..65535 (or -32767..32767 if signed). To get the correct image in this case the application should create an an OIV.LDM.Nodes.SoDataRange node and set the data range to the actual range of data values, for example 0..4095. If the actual range is not known, the min and max values can be queried using the OIV.VolumeViz.Nodes.SoVolumeData getMinMax methods. Note that the minValue and maxValue fields of this node have a different meaning that is described later. An example data-to-color-mapping looks like this:

# Color maps

If no transfer function has been specified, VolumeViz rendering nodes automatically use the predefined GRAY color map. To use one of the predefined color maps, set the OIV.LDM.Nodes.SoTransferFunction.predefColorMap field to a valid value other than NONE, for example:

  • GRAY In this color map all the color values are 1,1,1 (full white) and the alpha values are a linear ramp from 0 to 1. This can be a useful starting point for volume rendering.

  • INTENSITY In this color map the color values are a linear ramp from 0 to 1 and all alpha values are 1 (full opaque). This can be a useful starting point for slice rendering.

To create a custom color map, set the OIV.LDM.Nodes.SoTransferFunction.predefColorMap field to NONE, set the OIV.LDM.Nodes.SoTransferFunction.colorMapType field to the appropriate type (RGBA is the default and most commonly used), then set the color map values in the OIV.LDM.Nodes.SoTransferFunction.colorMap field. The OIV.LDM.Nodes.SoTransferFunction.colorMap field contains an array of float values in the range [0,1]. The number of float values needed depends on the colorMapType. It is equal to the number of colors in the color map multiplied by the number of components in each color. An ALPHA color map has one component per color, a LUM_ALPHA (Luminance plus Alpha) color map has two components per color and an RGBA color map has four components per color. For example, for an RGBA color map of length N, there must be 4*N float values in the field.

As usual with SoMF fields, memory is automatically allocated as needed if you set the values one at a time using the Item property.This is convenient, but very inefficient. If you must set values one at a time, use the Count propertyto pre-allocate enough memory for all the float values. There is another reason not to set values one at a time. Changing a value in the colorMap field automatically updates the OIV.LDM.Nodes.SoTransferFunction.actualColorMap field on each change. To avoid performance problems, we recommend using the setValues() or setValuesBuffer() method to create the color map.

const int numColors = 256;
float[] colors = new float[4numColors];
for (int i = 0; i < numColors; ++i)
{
   float intensity = (numColors - i) / (float)numColors;
   int index = i4;
   colors[index] = intensity;
   colors[index + 1] = intensity;
   colors[index + 2] = intensity;
   colors[index + 3] = 1;
}
SoTransferFunction tf = new SoTransferFunction();
tf.predefColorMap.Value = SoTransferFunction.PredefColorMaps.NONE;
tf.colorMap.SetValues(0, colors);

It is also possible to load a color map from a file using the OIV.LDM.Nodes.SoTransferFunction.LoadColormap(System.String) method. Currently the only supported file format is Avizo color map (.am or .col).

Note that the meaning of the min and max fields in OIV.LDM.Nodes.SoDataRange is quite different than the meaning of the OIV.LDM.Nodes.SoTransferFunction.minValue and OIV.LDM.Nodes.SoTransferFunction.maxValue fields in OIV.LDM.Nodes.SoTransferFunction. The fields in OIV.LDM.Nodes.SoDataRange specify the range of voxel values that will be mapped into the full color map. But changing the minValue and maxValue fields in OIV.LDM.Nodes.SoTransferFunction "remaps" the color map, compressing the original list of color values into the specified range of entries. Entries outside this range are set to full transparent black (all components zero). This does not affect the mapping of scalar values to the color map, it just modifies the color map. Scalar values in the current data range are still mapped to the full range of color map entries. This is mainly useful if you are using one of the predefined color maps and want to quickly remove low value voxels that represent "noise" in the data or to use the first or last entry in the color map for "undefined" values. Visually the effect can be similar to changing the data range using OIV.LDM.Nodes.SoDataRange, and it is generally faster, but note that remapping effectively reduces the resolution of the color map. In general it's better to change the range of data values mapped into the color map using OIV.LDM.Nodes.SoDataRange.

To modify an existing color map, use the setValues() method (as discussed above) or use the start/finish editing methods. For example:

SoTransferFunction tf . . .
SbNativeArray<float> data = tf.colorMap.StartEditing();
data[0] = 1;
. . .
tf.colorMap.FinishEditing();

# Rendering

For scalar volumes (non RGBA), the current data range and transfer function specify a color and an alpha value for each voxel of the volume. These colors and alpha are multiplied by the current base color on the state before applying the lighting effects. See the definition of base color here. The base color and alpha can be set for example with an OIV.Inventor.Nodes.SoMaterial or an OIV.Inventor.Nodes.SoPhysicalMaterial node in the scene graph.

The transfer function is associated with the rendering node(s), not the volume data node. So for example it is possible to use one transfer function for volume rendering (OIV.VolumeViz.Nodes.SoVolumeRender) and a different transfer function for slices (e.g. OIV.VolumeViz.Nodes.SoOrthoSlice).

When rendering a single volume, there is only one transfer function active at a time and each rendering node uses the current (i.e. last traversed) transfer function in the traversal state, similar to the way materials apply to polygonal geometry.

The application may switch from one transfer function to a different one. For example, by putting multiple transfer functions under an OIV.Inventor.Nodes.SoSwitch node. The application may also dynamically modify the contents of a transfer function, even if using a predefined color map. See the OIV.LDM.Nodes.SoTransferFunction.colorMap and OIV.LDM.Nodes.SoTransferFunction.actualColorMap fields.

When combining multiple volumes using OIV.LDM.Nodes.SoMultiDataSeparator and OIV.VolumeViz.Nodes.SoVolumeShader it is possible to specify multiple transfer functions. All the transfer functions under the OIV.LDM.Nodes.SoMultiDataSeparator will be available to the shader program based on their OIV.LDM.Nodes.SoTransferFunction.transferFunctionId. The application should assign a unique id to each transfer function. See the description of the OIV.LDM.Nodes.SoTransferFunction.transferFunctionId field for more details.

When using volume masks (OIV.VolumeViz.Nodes.SoVolumeMask and OIV.VolumeViz.Nodes.SoVolumeMaskGroup), the OIV.LDM.Nodes.SoTransferFunction.transferFunctionId is also very important. In this case multiple transfer functions, with different id values, may exist in the traversal state simultaneously. Each mask region is only visible if there exists a transfer function with the same id as that region. Traversal order of the transfer functions is not important. Each region, including the original (unmasked) volume, is colored using the transfer function (if any) that has the same id value. See OIV.VolumeViz.Nodes.SoVolumeMask for more details.

It can be convenient to use the same color map for volume rendering and for slice rendering. But typically the volume rendering color map should have transparency (alpha values less than one) and the slice rendering color map should be opaque. It's not actually necessary to create two separate color maps. Set the *alphaUse* field in the slice node to ALPHA_OPAQUE and the slice will ignore the transparency values in the color map.

Applications are not limited to using an OIV.LDM.Nodes.SoTransferFunction node to specify the data to color mapping. The application can implement its own data to color mapping function in GLSL (OpenGL shader language). Use an OIV.VolumeViz.Nodes.SoVolumeShader node and replace the VVizComputeFragmentColor function. In this case OIV.LDM.Nodes.SoTransferFunction can still be used to load a color map (or maps) on the GPU, if desired, but the actual assignment of a color is done by the application's shader function.

# More about data mapping

We have discussed above the basic concept of mapping scalar values to color values. Here we present more details, which may be important in some cases, for example when rendering a label volume. The way scalar values are mapped to indices in the color map depends on:

We have discussed all of these previously except for texture precision. The texturePrecision field of OIV.VolumeViz.Nodes.SoVolumeData effectively specifies the GPU data type, in other words the size (in bits) of the data values stored on the GPU. The default is 8 (1 byte), but VolumeViz also supports 16. Effectively a volume data value is normalized in the specified data range (OIV.LDM.Nodes.SoDataRange) then converted to a GPU data value in the range of values supported by the GPU data type (default 0..255). For 8-bit (byte) volume data this is a one-to-one mapping. For larger data types it is possible that more than one volume data value will map to the same GPU data value (value aliasing). During rendering the GPU data value is normalized to the number of entries in the color map then converted to an index into the list of colors. For 8-bit (byte) data, if the color map contains 256 entries, this is also a one-to-one mapping. But note that the GPU data values are usually interpolated before this step (see the interpolation field in, for example, OIV.VolumeViz.Nodes.SoVolumeRender).

The data values may also be explicitly rescaled before mapping onto the color map, using the OIV.LDM.Nodes.SoTransferFunction.shift and OIV.LDM.Nodes.SoTransferFunction.offset fields.

# 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 that 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 with volume rendering, set field OIV.VolumeViz.Nodes.SoVolumeShape.interpolation to NEAREST and leave the field OIV.VolumeViz.Nodes.SoVolumeRenderingQuality.preIntegrated to false. If rendering isosurfaces, set the field OIV.VolumeViz.Nodes.SoVolumeRenderingQuality.segmentedInterpolation to true.

It is also important to set the data range, texture precision and color map size carefully. In particular, take care that the number of values in the data range and the number of entries in the color map are exactly equal and a "power of 2" (8, 16, 32, ...). For example, if you have 6 label values, you might (logically) set the data range to 0..5 (min = 0 and max = 5) and create a label color map with 6 color values. That will work most of the time, but in a some cases (that are difficult to predict), loss of precision in the mapping arithmetic can cause a data value to map to the wrong color. In the previous example, with 6 label values, we recommend setting the data range to 0..7 and creating a color map with 8 color values (the last two values can be anything since they will not be used).

If you need more than 256 label values (this is not common, but it is possible), follow the above recommendation and also set the texturePrecision field to 16 (the default is 8). This ensures that each label value maps to a unique GPU data value. For example, if you have 1000 labels, we recommond setting the data range to 0..1024, setting texturePrecision to 16 and creating a color map with 1024 color values.

Finally, if you're rendering a label volume and you want to "remove" one of the materials (i.e. make it invisible), you cannot do this in the usual data volume way by modifying the data range in the OIV.LDM.Nodes.SoDataRange node or changing the min/max values in the OIV.LDM.Nodes.SoTransferFunction node. Doing either of those change will cause label values to map to different colors. Instead you should directly modify the values in the color map. In other words, for the color entry corresponding to the material id, change the Alpha value to zero.

# Faux shading

This node also supports a "faux shading" technique that can produce results somewhat similar to surface shading without the performance penalty for computing lighting. Faux shading modifies the lower portion of the color map so that color and opacity ramp down to zero. Faux shading is controlled by the OIV.LDM.Nodes.SoTransferFunction.fauxShadingLength, OIV.LDM.Nodes.SoTransferFunction.fauxShadingStrength, and OIV.LDM.Nodes.SoTransferFunction.fauxShadingDarkenThreshold fields.
No Faux Shading Faux Shading

The OIV.LDM.Nodes.SoTransferFunction.actualColorMap field always contains the actual color values (after remapping, faux shading, etc have been applied) that will be used for rendering.

# LIMITATIONS

FILE FORMAT/DEFAULT

TransferFunction {
shift 0
offset 0
predefColorMap NONE
colorMapType RGBA
colorMap 0
actualColorMap 0
transferFunctionId 0
minValue 0
maxValue 255
fauxShadingStrength 1
fauxShadingLength 0
fauxShadingDarkenThreshold 1
}

ACTION BEHAVIOR

OIV.Inventor.Actions.SoCallbackAction, OIV.Inventor.Actions.SoGLRenderAction Sets transfer function parameters in the traversal state.

See Also