Texture Based Volume Visualization
Apart from using the volume representation as a
sophisticated surface representation, I am also
interested in techniques for visualization of
volumes. For instance, texture based volume
visualization is fast becoming a very powerful
technique. This page is a small gallery of images
created using diverse texture based methods.
AcknowledgmentsAll software used to create the images below was written by me, but the techniques were found in literature. See for instance the following publications:
Volume Visualization using 2D Textures
The simplest form of volume visualization employs only 2D texture mapping which is supported by even the cheapest graphics cards available.
To render a volume we need to store the volume as a stack of slices. We keep three stacks of slices for the same volume. Each stack is perpendicular to one the three major axes, and we always choose the stack that is most perpendicular to the viewing direction.
Then each slice is rendered back to front. We have to map the densities in the volume to opacity and colour. The simplest approach is to simply use the volume density as both colour and opacity. The slices are then rendered using the over operator:
pxl_colour=opacity*colour - pxl_colour*(1-opacity)
where pxl_colour is the colour stored in the framebuffer. A final, important idea is to use the alpha test to reject new fragments (incoming pixel values) if the opacity is below some threshold.
This method requires only basic OpenGL functionality (no extensions).
We can identify a number of weaknesses with this simple approach. First, we need to store three sets of slices. Secondly, the quality of the image depends on viewing angle. Finally, we get a slight visual pop whenever the viewing direction changes.
In the images below, the viewpoint is turned
slightly. Just enough that the most perpendicular view
stack changes. This illustrates popping:
Volume editing is very simple. All we have to do is change voxel values and then rebind the textures. This allows us to interactively change a volume.
This method was used to edit the Lars Pedersen data in order to show the brain tissue.
Volume Visualization using 3D Textures
A more attractive approach is to use 3D textures. The same rendering technique can be used, but the slices are now always completely perpendicular to the viewing direction. This is possible because a 3D texture is like solid material and when we apply the 3D texture to an arbitrary plane, it is like cutting out a thin sheet of this solid material.
Isosurface Visualization using 3D Textures and Cubemaps
Cube mapping is a powerful technique for simulating specular reflections on graphics hardware. To do specular reflections, however, we must have a surface normal, but we can precompute normals and store them in the colour channels of the volume. With this in place, it is possible to render Lars as if his head were a bust made of shiny metal.
Isosurface Visualization using 3D Textures
If normals are stored in the volume we can also render isosurfaces in a simpler way. In the example below register combiners (a fragment shading facility available on the NVIDIA NV2x graphics cards) were used to compute an ambient, diffuse, and specular illumination for each pixel.
Slab-based Volume Visualization using 3D Textures
A great recent idea due to Klaus Engel et al. is to look at two slices at a time. If, for a given pixel, the value of the first slice is below the isovalue and the value of the next slice is above, we can linearly interpolate the gradients stored in either slice to the isovalue.
This results in very smooth shading. Using this method removes many artifacts from the Lars Pedersen visualization.
Ray Casting in a Fragment Program
Texture based volume rendering along the lines discussed above is very different from software based ray casting. However, it is now possible to write fragment shaders that perform complicated operations. This means that ray casting algorithms very similar to software ray casting has now become feasible in a fragment program [Kruger et al.]
I have implemented a simple ray casting algorithm using the OpenGL Shading Language. In one call to a GLSL fragment program, a single ray is cast through the volume. Of course, the program is simplistic: There is a fixed number of iterations and hence no early termination. The program doesn't even detect if it is outside the volume. However, this was as far as I was able to get on a Geforce FX5900 with the self imposed constraint that one iteration of the fragment program should equal one entire ray traversal.
Of course, to compute the ray directions it was necessary to first render the front and then the back side of the cube containing the volume. To get the desired accuracy, I use floating point textures to collect the results. For this purpose, Mark Harris's RenderTexture class was used.
The image below shows the well-known teddy bear model. The quality leaves something to be desired, but that is, of course, due to the fact that only 100 samples were used. More than that and the program would not run on an FX5900. With several passes, far better results could have been obtained. To give an indication of what is possible, the Lars Pedersen data set was rendered with a very small step size. This gives better quality, but only the face can be rendered in 100 iterations.
ConclusionsI find that ray casting was surprisingly simple to implement while preintegration and view aligned slices required jumping through some hoops that were a bit tricky. On the other hand, my implementation was simplistic - lacking early termination for instance. However, that should be simple to address on a modern GPU. The texture based volume visualization methods in general are cool, but they do have some limitations. The lack of high numerical precision in the fragment pipelines of modern graphics cards has been addressed, and now we can use floating point arithmetic. A remaining problem is volume sizes. Large volumes typically need to be split up into smaller pieces that are rendered separately.