GLSL is the standard shader language for OpenGL so you may already be familiar with it. If not, you will find its keywords and syntax are similar to the C language, plus some features from C++ like function overloading (multiple versions of a function that have the same name). There are two important differences that you will see in almost every GLSL function. First, GLSL supports generic vector types like “vec3” and “vec4”, which can contain integer or floating point (etc) values. Second, these vector types allow one or several of their components to be addressed using the suffix notation “xyzw” or “rgba”. In other words a GLSL function can refer to the first component of a vec4 variable as “color.r”, but it can also refer to the first three components simultaneously as “position.xyz”.
Example 1.9. GLSL vector syntax
vec4 color; color.rgb = ComputeRGBValue( value ); // returns a vec3 color.a = 1; // Alpha value
The suffix notation also allows “swizzling” the order of components in an assignment statement. There are many resources available on the web for learning more about GLSL.
Similar to a C or C++ programming environment, VolumeViz provides header files for the shader API. These files provide declarations for the shader API functions and for predefined uniform and varying parameters. For example, to use the function VVizGetData() in an application shader file, add the following include directive at the top of the file:
Be sure there are no spaces (blank characters) in the string “//!oiv_include”. The parser currently does not recognize white space in include directives. |
VolumeViz provides a virtual address space (also called virtual texture) for volume data on the GPU. This allows shader functions to access any voxel in the volume. For example it is possible to get the values of neighboring voxels for filtering or the values of other samples in a trace for computing seismic attributes. Voxel positions are specified in normalized volume coordinates. These are floating point values ranging from 0..1 along each axis of the volume. Since voxel positions are floating point, it is possible to request the value at any position inside the volume. Interpolation is used, if necessary, to compute the value at the requested position. Application shader code must use the VVizGetData() or VVizGetRawData() function to get voxel values.
Unlike using the Data Access API on the CPU, it is not possible to guarantee that voxel values are taken from full resolution data. However it is possible to query the resolution level for the tile containing a specific voxel. Use the GLSL methods VVizGetTileInfo() and VVizGetTileResolution().
Application shader code cannot make any assumptions about how or where data is stored on the GPU (this may change between releases or even as tiles of different resolutions are managed on the GPU). |
All application shader functions should follow these guidelines:
Must be written in the GLSL language
Must use VVizGetData() or VVizGetRawData() to get voxel values.
(The application cannot make any assumptions about how/where data is stored on the GPU.)
Must be loaded separately for slice primitives and for volume rendering.
(See the forVolumeOnly field in SoVolumeShader( C++ | Java | .NET ))
Must not replace the geometry, vertex or fragment main functions in ray casting mode.
(Turn ray casting mode off if necessary using the raycasting field in SoVolumeShader( C++ | Java | .NET ))
Volume rendering fragment shaders must not use the GLSL keyword discard in raycasting mode.
The public shader API functions are described in the Reference Manual. Some of the most commonly used functions are listed here. As previously discussed, VVIZ_DATATYPE is a macro defined as "float" for scalar volumes and "vec4" for RGBA volumes.
VVizGetData
Reimplement this function for the GET_DATA_FUNCTION slot.
VVIZ_DATATYPE VVizGetData(in VVizDataSetId dataset, in vec3 dataCoord);
This function is called with a data set id and a voxel coordinate and returns a voxel value. This value could be computed from the actual value in the specified data set, for example by applying a filter or a seismic attribute algorithm. To get the actual value call VVizGetRawData(). The default implementation just returns the value from VVizGetRawData().
VVizGetRawData
Used in application written VVizGetData() functions.
VVIZ_DATATYPE VVizGetRawData(in VVizDataSetId dataset, in vec3 dataCoord)
This function is called with a data set id and a voxel coordinate and returns the interpolated, but otherwise unmodified, voxel value from the data set.
VVizCombineData
Reimplement this function for the DATA_COMBINE_FUNCTION slot.
VVIZ_DATATYPE VVizCombineData(in vec3 dataCoord)
This function is called with a voxel coordinate and returns a voxel value. This value may be a combination of values from multiple volumes. The default implementation just calls VVizGetData() on the primary volume.
VVizComputeFragmentColor
Reimplement this function for the FRAGMENT_COMPUTE_COLOR slot
There are two versions of this function, one for slice primitives and one for volume rendering:
vec4 VVizComputeFragmentColor(in VVIZ_DATATYPE vox, in vec3 dataCoord) vec4 VVizComputeFragmentColor(in VVizDataSetId dataset, in vec3 rayDir, inout VVizVoxelInfo voxelInfoFront, in VVizVoxelInfo voxelInfoBack, in int mask)
The application must re-implement both if the shader needs to apply to both slice primitives and volume rendering. The source code for both versions can be in the same source file, however the file needs to loaded in two different volume shader nodes (one with forVolumeOnly set to true). Lighting and other rendering effects are applied to the color and opacity returned from this function. Basically this function is called with a voxel value and returns a color and opacity (RGBA value). This function is often used to blend colors from multiple data sets and can also be used to implement a custom transfer function. The default implementation just calls VVizTransferFunction(). The slice version is called with the voxel value from the primary data set, but also with the voxel coordinate, so it can fetch values from additional data sets. The volume rendering version is called with voxel info for the “front” and “back” voxels of the preintegrated rendering slab. These values can simply be forwarded to the volume rendering version of VVizTransferFunction() if you just need to do a color lookup. However you can also get the voxel value and coordinate as, for example, voxelInfoFront.value and voxelInfoFront.texCoord.
Note: In the volume rendering version of this function, all the parameters are declared "in" except the voxelInfoFront parameter, which is declared "inout". "In" is the default and can be omitted. The "inout" declaration cannot be omitted or the shader will not link.
VVizTransferFunction
Used in application written VVizComputeFragmentColor() functions.
There are two versions of this function, one for slice primitives and one for volume rendering:
vec4 VVizTransferFunction(in VVIZ_DATATYPE voxelValue, in int Id) vec4 VVizTransferFunction(in VVIZ_DATATYPE frontVoxelValue, in VVIZ_DATATYPE backVoxelValue, in int Id)
This function takes a voxel value and a transfer function id and returns the result of the color lookup. The transfer function id is a value assigned by the application in the transferFunctionId field of the SoTransferFunction( C++ | Java | .NET ) node. Each transfer function should have a unique id.
VolumeViz automatically defines the following preprocessor macros when compiling shader functions:
VVIZ_DATARGBA:
Is true if VVizData (VolumeViz input data) contains RGBA values. In this case each value in VVizData is a vec4. Otherwise each value is a float.
VVIZ_DATATYPE:
A string defining the GLSL data type in the volume (VVizData). If VVIZ_DATARGBA is true, then VVIZ_DATATYPE is defined as "vec4", in all other cases it is defined as "float". You will see this macro frequently in the declaration of VViz functions.
VVIZ_NUM_CLIPPING_PLANES:
Defines the current numbers of GL clipping planes to take in account.
VVIZ_SHAPE_TYPE:
Defines the type of shape that is currently being rendered. It might be useful in some cases to specialize the shader behavior depending on the shape type. This value can be tested in the shader source against the following macro values: VVIZ_VRENDER (volume rendering), VVIZ_SLICE, VVIZ_OBLSLICE, VVIZ_VGEOM, VVIZ_VSKIN or VVIZ_HEIGHTFIELD.
VolumeViz automatically creates some uniform parameters when loading the shader. These contain useful information about the volume. With the exception of VVizDataSetId (previously discussed), the application should not attempt to access these variables directly. Use the following query functions:
vec3 VVizGetVolumeDimensions (in VVizDataSetId dataset)
Returns the volume dimensions in voxels (integer)
vec3 VVizGetTileDimensions (in VVizDataSetId dataset)
Returns the tile dimensions in voxels (integer)
vec3 VVizGetVoxelDimensions (in VVizDataSetId dataset)
Returns the size of one voxel in normalized volume coordinates (float 0..1)
int VVizGetTileResolution (in VVizTileInfo tileInfo)
Returns the resolution level of the specified tile (0 is full resolution).
See VVizGetTileInfo to get the parameter for calling this function.
VVizTileInfo VVizGetTileInfo (in VVizDataSetId dataset, in vec3 dataCoord)
Given a voxel coordinate in normalized volume coordinates (float 0..1), returns information about the tile that contains the voxel. Specifically information about the version of the tile currently resident on the GPU. This is an opaque struct but can be used with other queries, e.g. VVizGetTileResolution().
void VVizGetTileIJKBox (in VVizDataSetId dataset, in vec3 dataCoord, out vec3 tileIJKMin, out vec3 tileIJKMax)
Given a dataSetId and voxel coordinate in normalized volume coordinates (float 0..1), returns the corners of the tile that contains the voxel (in voxel IJK coordinates).