The example below demonstrates how to use an arbitrary GLSL program and transfer function to render a volume. The fragment shader used in this example is capable of computing the volume gradient and per-pixel lighting in real-time with up to 4 lights at the same time. It's also capable of taking advantage of a precomputed normal-texture to speedup the lighting computations. You can drag and drop in the window any supported volume data to visualize it at different threshold level. The threshold level can be modified using the mouse wheel.
#include "BaseDemo.hpp"
static bool USE_GLSL = true;
static bool DYNAMIC_LIGHTS = true;
static bool COLORED_LIGHTS = true;
static bool PRECOMPUTE_GRADIENT = false;
static const int SLICE_COUNT = 1000;
class App_VolumeSliced: public BaseDemo
{
public:
{
return BaseDemo::appletInfo() +
"- Use the mouse wheel to change the bias used to render the volume.\n" +
"- Drop inside the window a set of 2D files or a DDS or DAT volume to display it.\n" +
"\n";
}
virtual void initEvent()
{
{
Log::error(
"This test requires 3D texture or GLSL support!\n");
exit(1);
}
DYNAMIC_LIGHTS &= USE_GLSL;
COLORED_LIGHTS &= DYNAMIC_LIGHTS;
PRECOMPUTE_GRADIENT &= USE_GLSL;
if (DYNAMIC_LIGHTS && COLORED_LIGHTS)
{
mLight1->setAmbient(
fvec4(0.0f, 0.0f, 0.0f, 1.0f));
mLight2->setAmbient(
fvec4(0.0f, 0.0f, 0.0f, 1.0f));
mLight0->setDiffuse(vl::gold);
mLight1->setDiffuse(vl::green);
mLight2->setDiffuse(vl::royalblue);
}
if ( DYNAMIC_LIGHTS )
{
rendering()->
as<
Rendering>()->transform()->addChild( mLight0Tr.get() );
rendering()->
as<
Rendering>()->transform()->addChild( mLight1Tr.get() );
rendering()->
as<
Rendering>()->transform()->addChild( mLight2Tr.get() );
mLight0->bindTransform( mLight0Tr.get() );
mLight1->bindTransform( mLight1Tr.get() );
mLight2->bindTransform( mLight2Tr.get() );
sceneManager()->tree()->addActor( light_bulb.
get(), fx_bulb.
get(), mLight0Tr.get() );
sceneManager()->tree()->addActor( light_bulb.
get(), fx_bulb.
get(), mLight1Tr.get() );
sceneManager()->tree()->addActor( light_bulb.
get(), fx_bulb.
get(), mLight2Tr.get() );
}
if ( DYNAMIC_LIGHTS )
{
}
if (USE_GLSL)
{
}
mVolumeAct->setTransform( mVolumeTr.get() );
sceneManager()->tree()->addActor( mVolumeAct.get() );
mSlicedVolume->
bindActor( mVolumeAct.get() );
mSlicedVolume->setSliceCount( SLICE_COUNT );
mSlicedVolume->setBox( volume_box );
sceneManager()->tree()->addActor( box_outline.
get(), fx_box.
get(), mVolumeTr.get() );
mBiasText->translate(0,5,0);
mBiasText->setBackgroundEnabled(true);
mBiasText->setBackgroundColor(
fvec4(0,0,0,0.75));
mBiasText->setColor(vl::white);
sceneManager()->tree()->addActor(mBiasText.get(), effect.
get());
mVolumeAct->gocUniform("val_threshold")->setUniformF(0.5f);
mAlphaBias = mVolumeAct->getUniform("val_threshold");
mouseWheelEvent(0);
setupVolume(
loadImage(
"/volume/VLTest.dat") );
}
void fileDroppedEvent(const std::vector<String>& files)
{
if(files.size() == 1)
{
if (files[0].endsWith(".dat"))
{
if (vol_img)
setupVolume(vol_img);
}
}
else
{
std::vector<String> files_sorted = files;
std::sort(files_sorted.begin(), files_sorted.end());
std::vector< ref<Image> > images;
for(unsigned int i=0; i<files_sorted.size(); ++i)
{
images.push_back(
loadImage(files_sorted[i]) );
if (files_sorted[i].endsWith(".dcm"))
images.back()->contrastHounsfieldAuto();
}
if (vol_img)
setupVolume(vol_img);
}
}
{
Effect* vol_fx = mVolumeAct->effect();
{
if ( USE_GLSL )
{
Log::notify(
"IF_LUMINANCE image and GLSL supported: lighting and the transfer function will be computed in realtime.\n");
if (COLORED_LIGHTS) {
}
else {
}
if ( PRECOMPUTE_GRADIENT )
{
}
}
else
{
Log::notify(
"IF_LUMINANCE image and GLSL not supported: transfer function and lighting will be precomputed.\n");
}
}
else
{
Log::notify(
"Non IF_LUMINANCE image: not using GLSL.\n");
}
mAlphaBias->setUniformF(0.3f);
updateText();
openglContext()->update();
}
void updateText()
{
float bias = 0.0f;
mAlphaBias->getUniform(&bias);
mBiasText->setText(
Say(
"Bias = %n") << bias);
}
void mouseWheelEvent(int val)
{
float alpha = 0.0f;
mAlphaBias->getUniform(&alpha);
alpha += val * 0.002f;
alpha =
clamp(alpha, 0.0f, 1.0f);
mAlphaBias->setUniformF(alpha);
mVolumeAct->effect()->shader()->gocAlphaFunc()->set(
FU_GEQUAL, alpha);
updateText();
openglContext()->update();
}
virtual void updateScene()
{
if (DYNAMIC_LIGHTS)
{
mLight0Tr->setLocalMatrix(mat);
mLight1Tr->setLocalMatrix(mat);
mLight2Tr->setLocalMatrix(mat);
}
}
virtual void destroyEvent() {
BaseDemo::destroyEvent();
mLight0 = mLight1 = mLight2 =
NULL;
}
protected:
};
#version 150 compatibility
#define LIGHTING_ALPHA_THRESHOLD 0.02
uniform sampler3D volume_texunit;
uniform sampler3D gradient_texunit;
uniform sampler1D trfunc_texunit;
uniform float trfunc_delta;
uniform
vec3 light_position[4];
uniform bool light_enable[4];
uniform
vec3 eye_position;
uniform float val_threshold;
uniform
vec3 gradient_delta;
uniform bool precomputed_gradient;
{
float shininess = 50.0;
float diff_coeff =
max(
dot(L,N),0.0);
float spec_coeff =
pow(
max(
dot(H,N), 0.0), shininess);
if (diff_coeff <= 0.0)
spec_coeff = 0.0;
return Ka * gl_LightSource[light].ambient.rgb +
Kd * gl_LightSource[light].diffuse.rgb * diff_coeff +
Ks * gl_LightSource[light].specular.rgb * spec_coeff ;
}
{
float val = texture3D( volume_texunit, gl_TexCoord[0].xyz ).r;
if (val < val_threshold)
discard;
float clamped_val = trfunc_delta + ( 1.0 - 2.0 * trfunc_delta ) * val;
vec4 color = texture1D( trfunc_texunit, clamped_val );
if (color.a > LIGHTING_ALPHA_THRESHOLD)
{
if (precomputed_gradient)
{
N =
normalize( (texture3D(gradient_texunit, gl_TexCoord[0].xyz).xyz -
vec3(0.5,0.5,0.5))*2.0 );
}
else
{
sample1.
x = texture3D(volume_texunit, gl_TexCoord[0].xyz-
vec3(gradient_delta.x,0.0,0.0) ).r;
sample2.x = texture3D(volume_texunit, gl_TexCoord[0].xyz+
vec3(gradient_delta.x,0.0,0.0) ).r;
sample1.y = texture3D(volume_texunit, gl_TexCoord[0].xyz-
vec3(0.0,gradient_delta.y,0.0) ).r;
sample2.y = texture3D(volume_texunit, gl_TexCoord[0].xyz+
vec3(0.0,gradient_delta.y,0.0) ).r;
sample1.z = texture3D(volume_texunit, gl_TexCoord[0].xyz-
vec3(0.0,0.0,gradient_delta.z) ).r;
sample2.z = texture3D(volume_texunit, gl_TexCoord[0].xyz+
vec3(0.0,0.0,gradient_delta.z) ).r;
}
for(int i=0; i<4; ++i)
{
if (light_enable[i])
{
color_tmp.rgb += color.rgb * blinn_phong(N,V,L,i);
}
}
}
else {
color_tmp = color.rgb;
}
gl_FragColor =
vec4(color_tmp,color.a);
}