The basic idea is to generate a bounding geometry as a triangle mesh, and then render that mesh to an off-screen buffer in order to compute the start and end positions that are used for the ray marching in the second pass.
I've implemented this in C++ using OpenGL 4 and blogged about it, including source code. Here's the general outline:
- Initialization: Given a volume texture with iso values, generate a bounding geometry on a coarse grid. For example, the volume could be 256x256x200, and the grid cells could span 8³ voxels, so the grid dimensions would be 32x32x25.
- First generate a volume texture
gridTex
, where each voxel contains the maximum iso value.
- Given a threshold iso value, generate the bounding geometry. A grid voxel is considered active if it contains anything visible. For iso-surfaces that means the value is above the threshold. For each pair of neighboring voxels in
gridTex
, generate a quad if one is active and the other is inactive. Do this every time the iso value changes.
- First pass: Render the bounding geometry to an offscreen buffer, storing the minimum and maximum depth values for each fragment.
- Second pass: Render the front faces again to the main buffer.
- Discard if not at the minimum depth value. Compute the start and end positions for the ray from the minimum and maximum depth values.
- March the ray as usual.