The rendering of 3D volumetric data is based on a custom data access API : the interface cgVolumetricData
This data interface is very generic and therefore the 3 axes of the data are named I, J, and K.
The main method to implement is the getData method : public ByteBuffer[] getData(int I, int J, int K, int sizeI, int sizeJ, int sizeK)
This method is in charge of retrieving the data from the data source (a file for example). It should return an array of buffer with the following requirements :
If the number of samples (multiplied by the sample size in byte) can fit in a single buffer, the data should return a single buffer and not an array of smaller buffer.
The samples should be orderer in IJK order.
[(0,0,0) , (1,0,0), (2,0,0), ... (sizeI,0,0), (0, 1, 0), ... (sizeI,1,0),..., ..., (sizeI, sizeJ,0), (0,0,1) , ..., .... (sizeI, sizeJ, sizeK)]
The data should also be able to compute simple statistics such as extremums, mean, ...
A sample implementation of the data interface to read SEP file can be found at the bottom of this page.
Transfer function
The volumetric rendering needs 2 pieces of information to perform a rendering : a data and a transfer function.
The volumetric rendering node accepts transfer function that implement the cgTransferFunction
The transfer function is used to convert scalar values from the scalar field of the data in color values (with alpha channel), therefore the main method of the transfer function interface is : public Color4f value(float x);
The interface also defines a method to assign a domain to the transfer function. By doing so, it is possible to change the mapping between scalar and colors without modifying the color ramp of the transfer function.
The domain definition on the transfer function allows to clamp scalar values which are outside of the domain.
Example :
If Data range = 0<->100 and Transfer function domain = 25<->75
Then value(x) =
value(25) if x < 25
a color from the color ramp
value(75) if x > 75
The last part of the interface is used to listen to the transfer function in order to update the rendering for example.
In most cases, the transfer function is editable (and can be edited by the user). JCarnac3D-OGL provides an interface which inherits from cgTransferFunction and adds edition methods : cgEditableTransferFunction.
To create a default transfer function, you can use the utility class cgVolumetricRenderingUtil :
Sample code
// create a transfer function. cgEditableTransferFunction transferFunction = cgVolumicRenderingUtil.createDefaultTransferFunction(); transferFunction.setDomain(new Point2f(data.getMinValue(), data.getMaxValue())); // the transfer function will work on the whole domain of the data transferFunction.addControlPoint(Channel.RED, 0, 0); // no red from 0 to 75 transferFunction.addControlPoint(Channel.RED, 75, 0); transferFunction.addControlPoint(Channel.RED, 75.001f, 1); // red = 1 from 75 to higher values transferFunction.addControlPoint(Channel.RED, 125.001f, 1); transferFunction.addControlPoint(Channel.GREEN, 25, 1); // green = 1 from 0 to 75 transferFunction.addControlPoint(Channel.GREEN, 75, 1); transferFunction.addControlPoint(Channel.GREEN, 75.1f, 0); // green = 0 from 76 to higher values transferFunction.addControlPoint(Channel.GREEN, 125, 0); transferFunction.addControlPoint(Channel.ALPHA, 25, 0.01f);// small alpha value from 0 to 75 transferFunction.addControlPoint(Channel.ALPHA, 75, 0.01f); transferFunction.addControlPoint(Channel.ALPHA, 75.1f, 0.5f); // high alpha for high values transferFunction.addControlPoint(Channel.ALPHA, 125f, 0.5f); // high alpha for high values
A specialized JComponent to edit the transfer function is also provided : cgCompleteTransferFunctionEditor.
After defining data and a transfer function, you are ready to initialize the rendering node and to insert it in the scene graph.
Sample code
// create node cgJOGL3DVolumicNode volumicNode = cgVolumicRenderingUtil.createDefaultVolumicNode(); // create a configuration and set it on the node cgVolumeRaycasterConfig config = cgVolumicRenderingUtil.createDefaultConfig(); config.setRaymarchingPrecision(100); config.setTransferFunctionSamplingPrecision(512); volumicNode.setConfig(config); // set the data on the node. volumicNode.setVolume(data); // create a transfer function. volumicNode.setTransferFunction(transferFunction);
// Add the node in the scenegraph
rootNode.addNode(volumicNode);
You can have a look at the SimpleVolumetricRendering demo class to see how the volumetric rendering can be used.
JCarnac3D-OGL is able to use third-party rendering engines to perform a volumetric rendering. Currently only NVIDIA IndeX and Intel OSPRay are supported.
Sample code
TODO