1.7.3. Height field clipping

VolumeViz supports clipping the volume against one or more surfaces defined by a height field.The SoUniformGridClipping SoUniformGridClipping SoUniformGridClipping node specifies a clipping surface defined by a clipping axis (axis field),a bounding box (extent field) in world coordinates and a uniform scalar clipping grid (a 2D array of height values). This tool provides an efficient way for seismic applications to clip volume rendering against horizon surfaces (see also the discussion of SoUniformGridProjectionClipping SoUniformGridProjectionClipping SoUniformGridProjectionClipping below). A flat clipping surface with non-zero thickness can also be used in medical applications to implement "thick slice" rendering. The clipping grid does not need to have the same dimensions as the volume to be clipped (it will be sampled and interpolated). The clipping grid may be rotated and translated by standard Open Inventor transform nodes.

The height values specified using a 2D texture. Each texel is a normalized value that, together with the extent, specifies a height. For integer valued textures, the range of the integer type (e.g. 0..255 for byte values) is normalized to the floating point range 0..1. For float valued textures the values should already be in the range 0..1. Each normalized value specifies a height based on the range of values in the extent box along the specified clipping axis. The default clipping axis is the Y axis.

For integer valued textures this is similar to the way values are interpreted for the SoHeightFieldGeometry SoHeightFieldGeometry SoHeightFieldGeometry node. However for float valued textures it is quite different because the SoHeightFieldGeometry SoHeightFieldGeometry SoHeightFieldGeometry node interprets float values as actual height values and ignores the extent values.

It is also possible to specify an "undefined" value using the undefinedValue field. Texels with this value will clip all voxels above and below the surface. The default undefined value is Nan (Not a Number).

The X, Y and Z coordinates of the extent are in the same coordinate system as the volume extent. They specify the bounding box of the clipping surface and also the range of height values that can be specified with the normalized values in the height field texture. They are typically, but not necessariliy, set to the same values as the volume extent. The extent of the clipping surface may be larger than the volume extent. In this case the volume is only clipped where its extent overlaps with the surface. The extent of the clipping surface may also be smaller than the volume extent. In this case the volume is clipped to the "horizontal" extent of the clipping surface (the dimensions perpendicular to the clipping axis). However along the clipping axis the volume is only limited by the surface itself, not by its extent.

SoUniformGridClipping SoUniformGridClipping SoUniformGridClipping is derived from SoTexture2 SoTexture2 SoTexture2 , so the clipping grid texture can be specified by setting the filename field or by directly setting the image field. Directly setting the image field is convenient if the height field data is floating point. All the usual texturing parameters apply. Wrapping and filtering modes are particularly important. Setting wrap mode to something other than CLAMP_TO_EDGE may cause unwanted interpolation on edges. Setting filter mode to NEAREST will give a blocky result which may or may not be desired. The format of the texture used for the grid should be the LUMINANCE* type for best performance. If no red component is available in the format used (for example ALPHA texture...), the grid will be totally flat.

Each clipping surface texture must be stored in a different OpenGL texture unit. One that is not currently used by VolumeViz for multi-data composition (i.e., a texture unit number greater than the highest dataSetId plus 1). Setting the texture unit for a clipping surface is done using the SoTextureUnit SoTextureUnit SoTextureUnit node, just like with any texture node.

The figure below shows the result (right) of applying the left texture to the volume in the middle.

The following code shows one way to achieve this result:

SoTextureUnit *texUnit = new SoTextureUnit;
texUnit->unit = 2;

SoUniformGridClipping *gridClip = new SoUniformGridClipping;
griClip->filename = "horizon2D.png";

root->addChild(texUnit);
root->addChild(gridClip);
root->addChild(transferFunction);
root->addChild(volumeData);
root->addChild(volumeRender);
SoTextureUnit texUnit = new SoTextureUnit();
texUnit.unit.Value = 2;

SoUniformGridClipping gridClip = new SoUniformGridClipping();
gridClip.filename.Value = "horizon2D.png";

Root.AddChild(texUnit);
Root.AddChild(gridClip);
Root.AddChild(transferFunction);
Root.AddChild(volumeData);
Root.AddChild(volumeRender);
SoTextureUnit texUnit = new SoTextureUnit();
texUnit.unit.setValue( 2 );

SoUniformGridClipping gridClip = new SoUniformGridClipping();
gridClip.filename.setValue( "horizon2D.png" );

root.addChild(texUnit);
root.addChild(gridClip);
root.addChild(transferFunction);
root.addChild(volumeData);
root.addChild(volumeRender);

The uniform grid clipping node provides both clipAbove and clipBelow boolean fields. By default only clipAbove is true. Using a pair of grid clipping nodes, one with clipAbove and one with clipBelow, allows clipping between two surfaces. This is useful in seismic applications to clip between two "horizon" surfaces. Note that if both of these fields are true and the thickness field is zero then the entire volume will be clipped.

Clipping between two horizon surfaces

Figure 1.80. Clipping between two horizon surfaces



The thickness field expands the clipping surface in both directions perpendicular to the surface.The value of this field is specified in 0..1 normalized units spanning the specified extent of the surface. A completely flat clipping surface with thickness greater than zero can be used to implement "thick slice" (aka "thin slab") rendering. A transform node, for example SoTransform SoTransform SoTransform , can be used to position and orient the clipping surface. In this case the transform node and the clipping surface node should usually be placed under an SoTransformSeparator SoTransformSeparator SoTransformSeparator node. This allows the surface to clip the volume primitives, but prevents the transform node from affecting the volume primitives. Remember that, as usual, the transform node will rotate the clipping surface around the origin (0,0,0) of the world coordinate system.

Figure: Clipping surface thickness

The maximum number of simultaneous SoUniformGridClipping SoUniformGridClipping SoUniformGridClipping nodes that can be applied to the same volume is limited by the hardware. To query the maximum, use the following query: SoUniformGridClipping SoUniformGridClipping SoUniformGridClipping ::getMaxNumberOfUniformGrids(). Typically the maximum is four or five clipping surfaces.

Automatically generating a clipping surface

SoUniformGridProjectionClipping SoUniformGridProjectionClipping SoUniformGridProjectionClipping is a helper class derived from SoUniformGridClipping SoUniformGridClipping SoUniformGridClipping . Its clipping behavior is the same as SoUniformGridClipping SoUniformGridClipping SoUniformGridClipping , but it creates the clipping surface by rendering a specified scene graph and storing the resulting depth values as the height values in the clipping texture. This is an easy way to convert a surface such as a height field (SoHeightFieldGeometry SoHeightFieldGeometry SoHeightFieldGeometry ) to a clipping surface. It also saves memory, because typically the clipping surface can be lower resolution than the actual geometry, such as a seismic horizon surface. For added convenience the clipping surface is automatically updated if the specified scene graph changes.

The SoUniformGridProjectionClipping SoUniformGridProjectionClipping SoUniformGridProjectionClipping node has one additional field, sceneGraph of type SoSFNode SoSFNode SoSFNode . Normally an SoSeparator SoSeparator SoSeparator (or other grouping node) will be assigned to this field. The specified scene graph should not contain a camera node. The projection clipping node will automatically create an orthographic (parallel projection) camera. The created camera will be oriented according to the axis specified for the clipping surface in the axis field:

  • X axis: camera is looking in -X direction

  • Y axis: camera is looking in -Y direction

  • Z axis: camera is looking in -Z direction

The created camera is automatically positioned to look at the center of the bounding box of the specified scene graph.

The clipping surface texture is generated using an SoRenderToTextureProperty SoRenderToTextureProperty SoRenderToTextureProperty node, which is automatically created and assigned to the clipping surface node's renderToTextureProperty field (inherited from its parent class SoTexture2 SoTexture2 SoTexture2 ). By default the clipping surface texture will be 2048 texels on its longest axis. The other dimension is computed using the aspect ratio of the specified scene graph's bounding box as viewed by the created camera. In other words, the texture will be 2048 by 2048 if the aspect ratio is 1, and 2048 by something less than 2048 if the aspect ratio is greater than 1.

  • Rendering the sceneGraph on top of the clipped volume might result in z-fighting artifacts due to depth precision. In order to avoid this issue it is necessary to use a SoPolygonOffset SoPolygonOffset SoPolygonOffset node before the geometry to render.

  • The sceneGraph should not contain any draggers or manipulators because they will become part of the clipping texture and might lead to unwanted results. As a workaround, you can put draggers outside of sceneGraph, put an SoTransform SoTransform SoTransform inside your sceneGraph and connect it to your dragger.

  • If the sceneGraph contains holes, you should set the minFilter and magFilter fields to NEAREST to avoid interpolation issues between holes and the true surface.