For questions or clarifications, please contact me via meine@kogs.informatik.uni-hamburg.de. Also, note that we now have a mailing list, too.
Recently, I was looking for other volume rendering software. An overview of what I found follows; I did not yet test most of these, so additional information (on features / speed / quality / usefulness) is very welcome:
- VolPack (by Philippe Lacroute of ShearWarp fame)
- software library (Unix, but Windows & OsX port available) and Tcl/Tk frontend for fast, high-quality, software volume rendering (1994/95); supports transfer functions and a shading model with directional light sources, multiple material types with different reflective properties, depth cueing, and shadows
- Space (by Mark Dow and Greg Scott)
- actively developed as of 2006, Windows, free as in lunch, no source available.
- preint (Uni Stuttgart)
- pre-integrated OpenGL volume rendering, binaries for Windows (old/new NVidia and ATI cards) and Linux (new NVidia only); last update Nov 2001
- VolumeJ (part of BIJ - Bio-medical Imaging in Java)
- freeware, open-source, renders any (colored / anisotropic) volume that can be loaded by ImageJ, LUT classification, "not very fast"
- VoxTrace/XDend (NCMIR software)
- Linux binaries available for non-commercial use after filling out an application form, segmentation/analysis/visualization software for tomographic data
What follows are sketches of what has to be done in our VigraCaster codebase:
Refactoring
DONE: Maintain current state (zooming/orientation/...) in a VolumeRendererBase instance (as a member of RenderWidget). This would
- make coordinate transformations in the widget's code more easy
- simplify the rendering code, which currently copies all attributes manually
DONE: Then RenderJob and ShearWarpMIPJob should be merged; it is stupid to have two nearly identical classes (it's a matter of making it possible to specify the job as constructor argument in RenderJob).
DONE: There should not be string-enum mappings hard-coded in the .cxx files. setRenderMode(QString) should be removed again, and the older one would become a slot.
Fixes
The RenderImageTraits should be parametrized with the projection functor's result_type, not with the volume's data type.
DONE: The software renderers currently use a different coordinate system than the OpenGL one: If you switch between "OpenGL" and "Alpha Blending", you'll notice that their Z axes are antiparallel. Actually, the OpenGL way seems right to me: By default, the slices are displayed from front to back (of course, this depends on the original data, but we can already change this via VolumeRendererBase::setAlignment()). Please note that I described the coordinated systems on a separate page.
The Raycaster should use the same depth attenuation formula as the ShearWarp class already does.
The ShearWarp should allow separate toggling of interpolation and depth attenuation.
DONE: The Raycaster did not render all border voxels (ray bounds calculation inaccurate?!) - use /home/meine/volrend/VigraCaster/Volumes/small* and the "Draw Wireframe" option (with MIP rendermode) for testing..
Check why the AlphaProjector currently gives ugly artifacts if used with NearestNeighbor3D (maybe the preintegration method has to be applied)?
DONE: Ensure that ShearWarp supports all coordinate transformations correctly.
The anisotropic screen resolution should be ignored while rendering the anim.
Cleanups / New functionality
PARTIALLY DONE: RenderMode should be refactored into renderer + parameters; e.g. "Raycaster with MIP/sum projection, ShearWarp or OpenGL", [x] depth shading (plus depth-only slider), [x] interpolation, ...
DONE: Another slider is needed: An "isolevel slider" for the Depth/Gradient/Bubble Model renderers.
Depth cueing of the wireframe (easy at least in OpenGL)
Make it possible to switch off the slice-wise sampling in the raycaster? Actually, make it possible to choose between:
- the current way, which was intended to make 2D filtering possible (and should do so) for efficiency and
- the old way, which simply stepped with whatever step size resulted from the transformations (this would fix the current incontinuity around 45° rotations).
AFAICS, the code would consist of a cascade of template functions which successively transform the dynamic state variables into static template parameters for the renderers, analoguous to the render() -> render<Volume>() step.
Rendering animations should pop up a dialog allowing to configure the frame size (defaulting to the current widget size) and the number of frames.
It should be possible to render color volumes (again); this is not possible with all renderers, so some of them would have to be disabled if a color volume has been loaded. (Maybe allow the user to "force" calculation of a gray-valued version to circumvent this.)
The rendered image should be a 16/32bit image (display scaling should be a 2D operation), and the output scaling slider should do only this and nothing more/less.
DONE: When zooming, the last rendered image could be zoomed in 2D as a quick preview.
Transfer functions should be implemented, using the existing colormap.hxx and the corresponding editor widget. Additionally, an alpha transfer function should be enabled if the renderer supports it. (Transfer functions should be separate from the projection functor.)
Camera mode: It should be possible to render images to files. That would require to switch to a fixed output image size (i.e. renderBounds) and to temporarily reset the outputResolution to some isotropic default.
We need a "dynamically typed volume" class which also carries meta-information like name, description etc. The latter could be displayed in the GUI (so that the user can choose which one to render), and be associated with color or gray volumes, with or without alpha, and 8/16/32 bit or even float data.
Transfer Functors for ShearWarp
There exists a number of projection functors for the RayCaster class. These are not optimized for being usable by the ShearWarp, where the problem is that each ray is projected in parallel.
In order to derive projection functors usable for the ShearWarp, let us observe the the following steps performed during the projection:
- functor state initialization (direction- and method-dependent, but position independent)
- accumulator initialization (for each position, including depth inc.), possibly early ray termination
- accumulator updates, possibly early ray termination
- finishing/returning result of projection
For the ShearWarp algorithm, it is important to separate the ray state from the rest of the functor. Then, operator() would be called with not only the voxel value, but also with a reference to the corresponding state in the intermediate image. State could be a typedef inside the functor, and become the pixel type of the intermediate image.
The functor would have to store a reference to the global depth counter managed by the algorithm itself. This would happen in step 1 (the constructor), whereas step 2 would become a separate function called to prepare the intermediate image.