Sheet 5: Volume Rendering 1
Exercise: Basic Volume Raycasting (20 points)
In this exercise you will implement the core loop of a direct volume renderer. The provided application already creates a 3D texture from simple analytic scalar fields, renders the clipped front faces of the volume cube, and passes all required information to the fragment shader.
Your task is to complete the function tracePrimaryRay in
Shader/cubeFS.glsl. The surrounding code already computes the ray direction,
the distance to the volume boundary, the number of samples, the step size, and the
opacity correction factor.
Implement front-to-back emission-absorption raycasting:
The shader already provides the geometric setup for one ray per fragment. The
varying entryPoint is the position where the viewing ray enters the
volume in texture coordinates. The uniform cameraPosInTextureSpace
gives the camera position in the same coordinate system, so the ray direction is
computed as normalize(entryPoint - cameraPosInTextureSpace).
The helper distanceToVolumeExit computes rayLength, the
distance from the entry point to the point where the ray leaves the clipped volume
bounds. From this length and the volume resolution voxelCount, the
shader estimates how many voxels the ray crosses. Multiplying that estimate by
oversampling gives sampleCount. The values
stepLength = rayLength / sampleCount and
delta = rayDirection * stepLength then define the spacing and direction
of the ray-marching samples.
Top view of a fragment ray through the volume. The shader starts at
entryPoint, advances by delta, and stops at the volume
exit or after early ray termination.
Inside the ray-marching loop, start with currentPoint = rayStart and an
accumulated color of vec4(0.0). At each sample, read the scalar value
from volume, map it through transferFunction, correct its
opacity for the current step length, composite it front-to-back with
under, and then advance currentPoint by delta.
Implement early ray termination and stop the raycasting when the accumulated alpha exceeds 0.95.
The opacity correction keeps the rendered volume visually stable when the sampling
rate changes. Without it, increasing oversampling would add more samples
with the same opacity, making the same volume appear too opaque. The shader uses
opacityCorrection = 100.0 * stepLength and corrects the transfer-function
alpha with
correctedAlpha = 1.0 - pow(1.0 - transferAlpha, opacityCorrection).
Equivalently, the remaining transparency is raised to the step-size-dependent
exponent:
1.0 - correctedAlpha = pow(1.0 - transferAlpha, opacityCorrection).
In this exercise, the factor 100.0 means that the transfer-function
opacity is treated as being defined for a reference step length of about
0.01 in texture space.
The final program should resemble the example shown below.
Reference Solution
V toggle volume, R reset, T toggle transfer-function editor
1 clip in X, 2 clip in Y, 3 clip in Z
4 move clip in X, 5 move clip in Y, 6 move clip in Z
Q increase oversampling, W decrease oversampling
Up zoom in, Down zoom out
Left mouse drag rotate, Right mouse drag quick edit transfer function