Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics
[Home] [Tutorials] [All Classes] [Grouped Classes]

Volume Rendering with Transfer Functions and Lighting Tutorial

This tutorial demonstrates how to use how to render volume data, how to apply custom transfer functions and lighting.

pagGuideSlicedVolume.jpg
pagGuideSlicedVolume_3.jpg
pagGuideSlicedVolume_1.jpg
pagGuideSlicedVolume_2.jpg
pagGuideSlicedVolume_4.jpg
pagGuideSlicedVolume_5.jpg
pagGuideSlicedVolume_6.jpg
pagGuideSlicedVolume_7.jpg

[From App_VolumeRendering.hpp]

class App_VolumeRendering: public BaseDemo
{
public:
  virtual void initEvent()
  {
    BaseDemo::initEvent();

    // general test setup
    mUseGLSL      = GLEW_ARB_shading_language_100 != 0;
    mDynamicLight = false; // for GLSL mode only

    // transform and trackball steup
    mVolumeTr = new vl::Transform;
    vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild( mVolumeTr.get() );
    trackball()->setTransform( mVolumeTr.get() );

    // volume
    vl::AABB volume_box( vl::vec3(-10,-10,-10), vl::vec3(+10,+10,+10) );
    mSlicedVolume = new vlVolume::SlicedVolume;
    mSlicedVolume->setTransform(mVolumeTr.get());
    mSlicedVolume->setBox(volume_box);
    mSlicedVolume->setRenderRank(1);
    sceneManager()->tree()->addActor( mSlicedVolume.get() );

    // volume_box outline
    vl::ref<vl::Effect> fx_box = new vl::Effect;
    fx_box->shader()->gocPolygonMode()->set(vl::PM_LINE, vl::PM_LINE);
    fx_box->shader()->enable(vl::EN_DEPTH_TEST);
    vl::ref<vl::Geometry> box_outline = vlut::makeBox(volume_box);
    box_outline->setColorArray(vlut::red);
    sceneManager()->tree()->addActor( box_outline.get(), fx_box.get(), mVolumeTr.get() );

    // light bulb

    if (mDynamicLight)
    {
      mLight = mSlicedVolume->light();
      mLightTr = new vl::Transform;
      vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild( mLightTr.get() );
      mLight->followTransform( mLightTr.get() );

      vl::ref<vl::Effect> fx_bulb = new vl::Effect;
      fx_bulb->shader()->enable(vl::EN_DEPTH_TEST);
      vl::ref<vl::Geometry> light_bulb = vlut::makeIcosphere(vl::vec3(0,0,0),1,1);
      sceneManager()->tree()->addActor( light_bulb.get(), fx_bulb.get(), mLightTr.get() );
    }

    // bias text
    mBiasText = new vl::Text;
    mBiasText->setFont( vl::VisualizationLibrary::fontManager()->acquireFont("/font/bitstream-vera/VeraMono.ttf", 12) );
    mBiasText->setAlignment( vl::AlignHCenter | vl::AlignBottom);
    mBiasText->setViewportAlignment( vl::AlignHCenter | vl::AlignBottom );
    mBiasText->translate(0,5,0);
    mBiasText->setBackgroundEnabled(true);
    mBiasText->setBackgroundColor(vl::fvec4(0,0,0,0.75));
    mBiasText->setColor(vlut::white);
    vl::ref<vl::Effect> effect = new vl::Effect;
    effect->shader()->enable(vl::EN_BLEND);
    sceneManager()->tree()->addActor(mBiasText.get(), effect.get());

    // bias uniform
    mAlphaBias = mSlicedVolume->glslProgram()->getUniform("val_threshold");

    // update alpha bias text
    mouseWheelEvent(0);
    
    setVolume( vl::loadImage("/volume/VLTest.dat") );
  }

  void fileDroppedEvent(const std::vector<vl::String>& files)
  {
    if(files.size() == 1) // if there is one file load it directly
    {      
      if (files[0].endsWith(".dat"))
      {
        vl::ref<vl::Image> vol_img = vl::loadImage(files[0]);
        if (vol_img)
          setVolume(vol_img);
      }
    }
    else // if there is more than one file load all the files and assemble a 3D image
    {      
      // sort files by their name
      std::vector<vl::String> files_sorted = files;
      std::sort(files_sorted.begin(), files_sorted.end());
      // load the files
      std::vector< vl::ref<vl::Image> > images;
      for(unsigned int i=0; i<files_sorted.size(); ++i)
        images.push_back( vl::loadImage(files_sorted[i]) );
      // assemble the volume
      vl::ref<vl::Image> vol_img = vl::assemble3DImage(images);
      // set the volume
      if (vol_img)
        setVolume(vol_img);
    }
  }

  void setVolume(vl::ref<vl::Image> img)
  {
    if(img->format() == vl::IF_LUMINANCE)
    {
      if (mUseGLSL) // use GLSL
      {        
        vl::Log::info("IF_LUMINANCE image and GLSL supported: lighting and the transfer function will be computed in realtime.\n");
        vl::ref<vl::Image> trfunc = vl::Image::makeColorSpectrum(128, vlut::black, vlut::blue, vlut::green, vlut::yellow, vlut::red);
        // enables GLSL usage
        mSlicedVolume->setTransferFunction(trfunc.get());
        mSlicedVolume->setVolumeImage(img.get());
        mSlicedVolume->effect()->shader()->disable(vl::EN_ALPHA_TEST);
      }
      else // precompute transfer function and illumination
      {
        vl::Log::info("IF_LUMINANCE image and GLSL not supported: transfer function and lighting will be precomputed.\n");
        vl::ref<vl::Image> trfunc = vl::Image::makeColorSpectrum(128, vlut::black, vlut::blue, vlut::green, vlut::yellow, vlut::red);
        vl::ref<vl::Image> volume = vlVolume::SlicedVolume::genRGBAVolume(img.get(), trfunc.get(), vl::fvec3(1.0f,1.0f,0.0f));
        // disable GLSL usage
        mSlicedVolume->setTransferFunction(NULL);
        mSlicedVolume->setVolumeImage(volume.get());
        mSlicedVolume->effect()->shader()->enable(vl::EN_ALPHA_TEST);
        mSlicedVolume->effect()->shader()->gocAlphaFunc()->set(vl::FU_GEQUAL, 0.3f);
      }
    }
    else
    {
      vl::Log::info("Non IF_LUMINANCE image: not using GLSL.\n");
      // disable GLSL usage
      mSlicedVolume->setTransferFunction(NULL);
      mSlicedVolume->setVolumeImage( img.get() );
      mSlicedVolume->effect()->shader()->enable(vl::EN_ALPHA_TEST);
      mSlicedVolume->effect()->shader()->gocAlphaFunc()->set(vl::FU_GEQUAL, 0.3f);
    }
    
    mAlphaBias->setUniform(0.3f);
    updateText();
    openglContext()->update();
  }

  void updateText()
  {
    float bias = 0.0f;
    mAlphaBias->getUniform(&bias);
    mBiasText->setText(vl::Say("Bias = %n") << bias);
  }

  void mouseWheelEvent(int val)
  {
    float alpha = 0.0f;
    mAlphaBias->getUniform(&alpha);
    alpha += val * 0.01f;
    alpha =  vl::clamp(alpha, 0.0f, 1.0f);
    mAlphaBias->setUniform(alpha);

    // used for non GLSL mode volumes
    mSlicedVolume->effect()->shader()->gocAlphaFunc()->set(vl::FU_GEQUAL, alpha);
    
    updateText();
    openglContext()->update();
  }

  virtual void run()
  {
    if (mDynamicLight)
    {
      vl::mat4 mat = vl::mat4::rotation( vl::Time::currentTime()*45, 0,0,1 ) * vl::mat4::translation(20,0,0);
      mLightTr->setLocalMatrix(mat);
    }
  }

  protected:
    vl::ref<vl::Transform> mVolumeTr;
    vl::ref<vl::Transform> mLightTr;
    vl::ref<vl::Uniform> mAlphaBias;
    vl::ref<vl::Text> mBiasText;
    vl::ref<vl::Light> mLight;
    bool mDynamicLight;
    bool mUseGLSL;
    vl::ref<vlVolume::SlicedVolume> mSlicedVolume;
};

// Have fun!

Visualization Library v2010.06 Reference Documentation
Copyright 2005-2009 Michele Bosi. All rights reserved.
Updated on Tue Jun 1 00:57:08 2010.
Permission is granted to use this page to write and publish articles regarding Visualization Library.