Visualization Library

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

Edge Enhancement and Wireframe Rendering Tutorial

This tutorial demonstrates how to improve the perception of the objects in a scene and how to perform hidden line removal wireframe rendering using the edge extraction and enhnacement capabilities of Visualization Library.

Edge Rendering Off Silhouettes Silhouettes + Creases Silhouettes + Creases + Hidden Lines
pagGuideWireframe3a.jpg
pagGuideWireframe3b.jpg
pagGuideWireframe3c.jpg
pagGuideWireframe3d.jpg
pagGuideWireframe1a.jpg
pagGuideWireframe1b.jpg
pagGuideWireframe1c.jpg
pagGuideWireframe1d.jpg
pagGuideWireframe6a.jpg
pagGuideWireframe6b.jpg
pagGuideWireframe6c.jpg
pagGuideWireframe6d.jpg
Cartoonish models with silhouette enhancement
pagGuideWireframe4b.jpg
pagGuideWireframe4a.jpg
pagGuideWireframe5b.jpg
pagGuideWireframe5a.jpg
Hidden line removal wireframe
pagGuideWireframe7a.jpg
pagGuideWireframe7b.jpg
pagGuideWireframe7c.jpg
pagGuideWireframe7d.jpg

In this tutorial we will implement a simple application capable of loading models by drag&drop and applying edge enhancement and hidden line removal wireframe rendering to the whole scene. The user will also be able to interactively switch on and off various edge rendering features:

KeyMode
'1'edge rendering off
'2'edge rendering on: silhouette only
'3'edge rendering on: silhouette + creases
'4'edge rendering on: silhouette + creases + hidden lines
'5'hidden line removal wireframe: silhouette + creases
'6'hidden line removal wireframe: silhouette + creases + hidden lines

The edges are always extracted from the triangles or quads that are part of a vl::Geometry and can be of three types: silhouette edges, crease edges and boundary edges.

pagGuideWireframe_edges.jpg

For more information see also vl::EdgeExtractor and vl::EdgeRenderer.

[From App_EdgeRendering.hpp]

class App_EdgeRendering: public BaseDemo
{
public:
  void initEvent()
  {
    BaseDemo::initEvent();

    // retrieve the default rendering
    mRendering = (vl::VisualizationLibrary::rendering()->as<vl::Rendering>());
    // retrieve the default renderer, which we'll use as the solid-renderer
    mSolidRenderer = mRendering->renderer();

    // create our EdgeRenderer
    mEdgeRenderer = new vl::EdgeRenderer;
    // we set the clear flags to be vl::CF_CLEAR_DEPTH (by default is set to CF_CLEAR_COLOR_DEPTH) because 
    // when the wireframe rendering starts we want to preserve the color-buffer as generated by the solid 
    // rendering but we want to clear the Z-buffer as it is needed by the hidden-line-removal algorithm 
    // implemented by vl::EdgeRenderer.
    mEdgeRenderer->setClearFlags(vl::CF_CLEAR_DEPTH);
    // target the same opengl window
    mEdgeRenderer->setRenderTarget(mSolidRenderer->renderTarget());
    // enqueue the EdgeRenderer in the rendering, will be executed after mSolidRenderer
    mRendering->renderers().push_back( mEdgeRenderer.get() );

    // hidden line and crease options
    mEdgeRenderer->setShowHiddenLines(true);
    mEdgeRenderer->setShowCreases(true);
    mEdgeRenderer->setCreaseAngle(35.0f);

    // style options
    mEdgeRenderer->setLineWidth(2.0f);
    mEdgeRenderer->setSmoothLines(true);
    mEdgeRenderer->setDefaultLineColor(vlut::black);

    // fills mSceneManager with a few actors.
    // the beauty of this system is that you setup your actors ony once in a single scene managers and
    // they will be rendered twice, first using a normal renderer and then using the wireframe renderer.
    setupScene();
  }

  // populates the scene
  void setupScene()
  {
    // setup common states
    vl::ref<vl::Light> camera_light = new vl::Light(0);
    vl::ref<vl::EnableSet> enables = new vl::EnableSet;
    enables->enable(vl::EN_DEPTH_TEST);
    enables->enable(vl::EN_LIGHTING);

    // red material fx
    vl::ref<vl::Effect> red_fx = new vl::Effect;
    red_fx->shader()->setEnableSet(enables.get());
    red_fx->shader()->gocMaterial()->setDiffuse(vlut::red);
    red_fx->shader()->setRenderState(camera_light.get());

    // green material fx
    vl::ref<vl::Effect> green_fx = new vl::Effect;
    green_fx->shader()->setEnableSet(enables.get());
    green_fx->shader()->gocMaterial()->setDiffuse(vlut::green);
    green_fx->shader()->setRenderState(camera_light.get());

    // blue material fx
    vl::ref<vl::Effect> yellow_fx = new vl::Effect;
    yellow_fx->shader()->setEnableSet(enables.get());
    yellow_fx->shader()->gocMaterial()->setDiffuse(vlut::yellow);
    yellow_fx->shader()->setRenderState(camera_light.get());

    // add box, cylinder, cone actors to the scene
    vl::ref<vl::Geometry> geom1 = vlut::makeBox     (vl::vec3(-7,0,0),5,5,5);
    vl::ref<vl::Geometry> geom2 = vlut::makeCylinder(vl::vec3(0,0,0), 5,5, 10,2, true, true);
    vl::ref<vl::Geometry> geom3 = vlut::makeCone    (vl::vec3(+7,0,0),5,5, 20, true);

    // needed since we enabled the lighting
    geom1->computeNormals();
    geom2->computeNormals();
    geom3->computeNormals();

    // add the actors to the scene
    sceneManager()->tree()->addActor( geom1.get(), red_fx.get(),    mRendering->transform() );
    sceneManager()->tree()->addActor( geom2.get(), green_fx.get(),  mRendering->transform() );
    sceneManager()->tree()->addActor( geom3.get(), yellow_fx.get(), mRendering->transform() );
  }

  // user controls:
  // '1' = edge rendering off.
  // '2' = edge rendering on: silhouette only.
  // '3' = edge rendering on: silhouette + creases.
  // '4' = edge rendering on: silhouette + creases + hidden lines.
  // '5' = hidden line removal wireframe: silhouette + creases.
  // '6' = hidden line removal wireframe: silhouette + creases + hidden lines.
  void keyPressEvent(unsigned short ch, vl::EKey key)
  {
    BaseDemo::keyPressEvent(ch, key);

    if (ch == '1')
    {
      mSolidRenderer->setEnableMask(0xFFFFFFFF);
      mEdgeRenderer->setEnableMask(0);
      vl::Log::print("Edge rendering disabled.\n");
    }
    else
    if (ch == '2')
    {
      mSolidRenderer->setEnableMask(0xFFFFFFFF);
      // preserve color buffer, clear depth buffer
      mEdgeRenderer->setClearFlags(vl::CF_CLEAR_DEPTH);
      mEdgeRenderer->setEnableMask(0xFFFFFFFF);
      mEdgeRenderer->setShowCreases(false);
      mEdgeRenderer->setShowHiddenLines(false);
      vl::Log::print("Edge rendering enabled. Creases = off, hidden lines = off.\n");
    }
    else
    if (ch == '3')
    {
      mSolidRenderer->setEnableMask(0xFFFFFFFF);
      // preserve color buffer, clear depth buffer
      mEdgeRenderer->setClearFlags(vl::CF_CLEAR_DEPTH);
      mEdgeRenderer->setEnableMask(0xFFFFFFFF);
      mEdgeRenderer->setShowCreases(true);
      mEdgeRenderer->setShowHiddenLines(false);
      vl::Log::print("Edge rendering enabled. Creases = on, hidden lines = off.\n");
    }
    else
    if (ch == '4')
    {
      mSolidRenderer->setEnableMask(0xFFFFFFFF);
      // preserve color buffer, clear depth buffer
      mEdgeRenderer->setClearFlags(vl::CF_CLEAR_DEPTH);
      mEdgeRenderer->setEnableMask(0xFFFFFFFF);
      mEdgeRenderer->setShowCreases(true);
      mEdgeRenderer->setShowHiddenLines(true);
      vl::Log::print("Edge rendering enabled. Creases = on, hidden lines = on.\n");
    }
    else
    if (ch == '5')
    {
      mSolidRenderer->setEnableMask(0);
      // clear color and depth buffer
      mEdgeRenderer->setClearFlags(vl::CF_CLEAR_COLOR_DEPTH);
      mEdgeRenderer->setEnableMask(0xFFFFFFFF);
      mEdgeRenderer->setShowCreases(true);
      mEdgeRenderer->setShowHiddenLines(false);
      vl::Log::print("Hidden line removal wireframe enabled. Creases = on, hidden lines = off.\n");
    }
    if (ch == '6')
    {
      mSolidRenderer->setEnableMask(0);
      // clear color and depth buffer
      mEdgeRenderer->setClearFlags(vl::CF_CLEAR_COLOR_DEPTH);
      mEdgeRenderer->setEnableMask(0xFFFFFFFF);
      mEdgeRenderer->setShowCreases(true);
      mEdgeRenderer->setShowHiddenLines(true);
      vl::Log::print("Hidden line removal wireframe enabled. Creases = on, hidden lines = on.\n");
    }
  }

  void resizeEvent(int w, int h)
  {
    vl::Camera* camera = mRendering->camera();
    camera->viewport()->setWidth ( w );
    camera->viewport()->setHeight( h );
    camera->setProjectionAsPerspective();
  }

  void loadModel(const std::vector<vl::String>& files)
  {
    // resets the scene
    mSceneManager->tree()->actors()->clear();
    // resets the EdgeRenderer cache
    mEdgeRenderer->clearCache();

    for(unsigned int i=0; i<files.size(); ++i)
    {
      vl::ref<vl::ResourceDatabase> resource_db = vl::loadResource(files[i],true);

      if (!resource_db || resource_db->count<vl::Actor>() == 0)
      {
        vl::Log::error("No data found.\n");
        continue;
      }

      std::vector< vl::ref<vl::Actor> > actors;
      resource_db->get<vl::Actor>(actors);
      for(unsigned i=0; i<actors.size(); ++i)
      {
        vl::ref<vl::Actor> actor = actors[i].get();
        // define a reasonable Shader
        actor->effect()->shader()->setRenderState( new vl::Light(0) );
        actor->effect()->shader()->enable(vl::EN_DEPTH_TEST);
        actor->effect()->shader()->enable(vl::EN_LIGHTING);
        actor->effect()->shader()->gocLightModel()->setTwoSide(true);
        // add the actor to the scene
        mSceneManager->tree()->addActor( actor.get() );
      }
    }

    // position the camera to nicely see the objects in the scene
    trackball()->adjustView( mSceneManager.get(), vl::vec3(0,0,1)/*direction*/, vl::vec3(0,1,0)/*up*/, 1.0f/*bias*/ );
  }

  // laod the files dropped in the window
  void fileDroppedEvent(const std::vector<vl::String>& files) { loadModel(files); }

protected:
  vl::ref< vl::Renderer > mSolidRenderer;
  vl::ref< vl::EdgeRenderer > mEdgeRenderer;
  vl::ref<vl::Rendering> mRendering;
  vl::ref<vl::SceneManagerActorTree> mSceneManager;
};

// Have fun!


Visualization Library v2010.11.1123 Reference Documentation
Copyright 2005-2009 Michele Bosi. All rights reserved.
Updated on Thu Nov 18 2010 02:07:55.
Permission is granted to use this page to write and publish articles regarding Visualization Library.