# 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
Cartoonish models with silhouette enhancement
Hidden line removal wireframe

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:

Key | Mode
--- | ---
'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.

- Silhouette edges are those edges that are shared by a backfacing and front facing quad or triangle.

- Crease edges are those edges that are shared by two triangles or quads and that form an angle >= creaseAngle(), ie: 0 means that the two triangles or quads are coplanar; the edges of a cube define 90 degrees angles; the lateral edges of a 10 faces cylinder define 36 (360/10) degrees angles and so on.

- Boundary edges are those edges that belong to a single triangle or quad.

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

[From `App_EdgeRendering.hpp`]

    class App_EdgeRendering: public BaseDemo
    {
    public:
      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
        mSceneManager->tree()->addActor( geom1.get(), red_fx.get(),    mSolidRendering->transform() );
        mSceneManager->tree()->addActor( geom2.get(), green_fx.get(),  mSolidRendering->transform() );
        mSceneManager->tree()->addActor( geom3.get(), yellow_fx.get(), mSolidRendering->transform() );
      }

      void initEvent()
      {
        BaseDemo::initEvent();

        // initialize our renderings to use the camera, the scene-manager, rendering target etc. from the default renderingTree()
        mSolidRendering = new vl::Rendering;
        *mSolidRendering = *(vl::VisualizationLibrary::rendering()->as<vl::Rendering>());
        mEdgeRendering  = new vl::Rendering;
        *mEdgeRendering  = *(vl::VisualizationLibrary::rendering()->as<vl::Rendering>());

        /*
        Outline of our rendering tree:

                    renderingTree() <- empty rendering
                      /   \
                     /     \
        mSolidRendering   mEdgeRendering  <- first calls the solid rendering, then calls the edge rendering
        */

        // install the new rendering tree
        vl::ref<vl::RenderingTree> render_tree = new vl::RenderingTree;
        vl::VisualizationLibrary::setRendering(render_tree.get());
        render_tree->subRenderings()->push_back(mSolidRendering.get());
        render_tree->subRenderings()->push_back(mEdgeRendering.get());

        // bind the scene manager containing the actors to be rendered to both the solid and the edge rendering
        mSceneManager = new vl::SceneManagerActorTree;
        mSolidRendering->sceneManagers()->push_back(mSceneManager.get());
        mEdgeRendering ->sceneManagers()->push_back(mSceneManager.get());

        // bind an vl::EdgeRenderer to the mEdgeRendering in order to perform automatic edge extraction and rendering.
        // 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 = new vl::EdgeRenderer;
        mEdgeRendering->setRenderer( mEdgeRenderer.get() );
        mEdgeRendering->setClearFlags(vl::CF_CLEAR_DEPTH);

        // 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();
      }

      // 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')
        {
          mSolidRendering->setEnableMask(0xFFFFFFFF);
          mEdgeRendering->setEnableMask(0);
          vl::Log::print("Edge rendering disabled.\n");
        }
        else
        if (ch == '2')
        {
          mSolidRendering->setEnableMask(0xFFFFFFFF);
          // preserve color buffer, clear depth buffer
          mEdgeRendering->setClearFlags(vl::CF_CLEAR_DEPTH);
          mEdgeRendering->setEnableMask(0xFFFFFFFF);
          mEdgeRenderer->setShowCreases(false);
          mEdgeRenderer->setShowHiddenLines(false);
          vl::Log::print("Edge rendering enabled. Creases = off, hidden lines = off.\n");
        }
        else
        if (ch == '3')
        {
          mSolidRendering->setEnableMask(0xFFFFFFFF);
          // preserve color buffer, clear depth buffer
          mEdgeRendering->setClearFlags(vl::CF_CLEAR_DEPTH);
          mEdgeRendering->setEnableMask(0xFFFFFFFF);
          mEdgeRenderer->setShowCreases(true);
          mEdgeRenderer->setShowHiddenLines(false);
          vl::Log::print("Edge rendering enabled. Creases = on, hidden lines = off.\n");
        }
        else
        if (ch == '4')
        {
          mSolidRendering->setEnableMask(0xFFFFFFFF);
          // preserve color buffer, clear depth buffer
          mEdgeRendering->setClearFlags(vl::CF_CLEAR_DEPTH);
          mEdgeRendering->setEnableMask(0xFFFFFFFF);
          mEdgeRenderer->setShowCreases(true);
          mEdgeRenderer->setShowHiddenLines(true);
          vl::Log::print("Edge rendering enabled. Creases = on, hidden lines = on.\n");
        }
        else
        if (ch == '5')
        {
          mSolidRendering->setEnableMask(0);
          // clear color and depth buffer
          mEdgeRendering->setClearFlags(vl::CF_CLEAR_COLOR_DEPTH);
          mEdgeRendering->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')
        {
          mSolidRendering->setEnableMask(0);
          // clear color and depth buffer
          mEdgeRendering->setClearFlags(vl::CF_CLEAR_COLOR_DEPTH);
          mEdgeRendering->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*/)
      {
        // solid rendering: update viewport and projection matrix
        mSolidRendering->camera()->viewport()->setWidth(mSolidRendering->renderTarget()->width());
        mSolidRendering->camera()->viewport()->setHeight(mSolidRendering->renderTarget()->height());
        mSolidRendering->camera()->setProjectionAsPerspective();

        // edge rendering: update viewport and projection matrix
        mEdgeRendering->camera()->viewport()->setWidth(mEdgeRendering->renderTarget()->width());
        mEdgeRendering->camera()->viewport()->setHeight(mEdgeRendering->renderTarget()->height());
        mEdgeRendering->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::EdgeRenderer > mEdgeRenderer;
      vl::ref<vl::Rendering> mSolidRendering;
      vl::ref<vl::Rendering> mEdgeRendering;
      vl::ref<vl::SceneManagerActorTree> mSceneManager;
    };

    // Have fun!

Visualization Library v2009.08 Reference Documentation
Copyright 2005-2009 Michele Bosi. All rights reserved.
Updated on Sun Nov 29 11:11:40 2009.
Permission is granted to use this page to write and publish articles regarding Visualization Library.