In volume ray casting, the default depth buffer only records the front face of the bounding cube, not the actual rendered surface. To enable occlusion (e.g., for crosshairs) and mouse picking, you must explicitly set gl_FragDepth in your fragment shader when the first non-transparent voxel is encountered.
To implement this, use an isHit flag within your ray marching loop. When isHit is 0 and you find a voxel with sufficient opacity, set isHit = 1 and calculate gl_FragDepth using the Model-View-Projection matrix to map the hit position to the depth buffer.
int isHit = 0; //used to detect first non-transparent voxel
for (float t = t_hit.x; t < t_hit.y; t += dt) {
float val = texture(volume, p).r;
vec4 val_color = vec4(texture(transfer_fcn, vec2(val, 0.5)).rgb, val);
p += ray_dir * dt;
if (val_color.a < 0.01) continue; //skip transparent samples
if (!isHit) { //our first non-transparent voxel
isHit = 1;
gl_FragDepth = ((ModelViewProjectionMatrix * vec4(pos, 1.0)).z + 1.0) * 0.5;
}
color.rgb += (1.0 - color.a) * val_color.a * val_color.rgb;
color.a += (1.0 - color.a) * val_color.a;
if (color.a >= 0.95) {
break;
}
}