Visualization Library 2.1.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [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:

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.

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

[From App_EdgeRendering.cpp]

class App_EdgeRendering: public BaseDemo
{
public:
virtual String appletInfo()
{
return BaseDemo::appletInfo() +
"- '1' = edge rendering off.\n" +
"- '2' = edge rendering on: silhouette only.\n" +
"- '3' = edge rendering on: silhouette + creases.\n" +
"- '4' = edge rendering on: silhouette + creases + hidden lines.\n" +
"- '5' = edges only: silhouette + creases.\n" +
"- '6' = edges only: silhouette + creases + hidden lines.\n" +
"\n";
}
void initEvent()
{
vl::Log::notify(appletInfo());
// retrieve the default rendering
mMainRendering = rendering()->as<Rendering>();
// retrieve the default renderer, which we'll use as the solid-renderer
mSolidRenderer = mMainRendering->renderer();
// create our EdgeRenderer
mEdgeRenderer = new EdgeRenderer;
// we set the clear flags to be 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 EdgeRenderer.
mEdgeRenderer->setClearFlags(CF_CLEAR_DEPTH);
// target the same opengl window
mEdgeRenderer->setFramebuffer(mSolidRenderer->framebuffer());
// enqueue the EdgeRenderer in the rendering, will be executed after mSolidRenderer
mMainRendering->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(black);
// fills the scene 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 edge renderer.
setupScene();
}
// populates the scene
void setupScene()
{
// setup common states
ref<Light> camera_light = new Light;
ref<EnableSet> enables = new EnableSet;
enables->enable(EN_DEPTH_TEST);
enables->enable(EN_LIGHTING);
// red material fx
ref<Effect> red_fx = new Effect;
red_fx->shader()->setEnableSet(enables.get());
red_fx->shader()->gocMaterial()->setDiffuse(red);
red_fx->shader()->setRenderState(camera_light.get(), 0);
// green material fx
ref<Effect> green_fx = new Effect;
green_fx->shader()->setEnableSet(enables.get());
green_fx->shader()->gocMaterial()->setDiffuse(green);
green_fx->shader()->setRenderState(camera_light.get(), 0);
// blue material fx
ref<Effect> yellow_fx = new Effect;
yellow_fx->shader()->setEnableSet(enables.get());
yellow_fx->shader()->gocMaterial()->setDiffuse(yellow);
yellow_fx->shader()->setRenderState(camera_light.get(), 0);
// add box, cylinder, cone actors to the scene
ref<Geometry> geom1 = makeBox (vec3(-7,0,0),5,5,5);
ref<Geometry> geom2 = makeCylinder(vec3(0,0,0), 5,5, 10,2, true, true);
ref<Geometry> geom3 = makeCone (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(), mMainRendering->transform() );
sceneManager()->tree()->addActor( geom2.get(), green_fx.get(), mMainRendering->transform() );
sceneManager()->tree()->addActor( geom3.get(), yellow_fx.get(), mMainRendering->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' = edges only: silhouette + creases.
// '6' = edges only: silhouette + creases + hidden lines.
void keyPressEvent(unsigned short ch, EKey key)
{
BaseDemo::keyPressEvent(ch, key);
if (ch == '1')
{
mSolidRenderer->setEnableMask(0xFFFFFFFF);
mEdgeRenderer->setEnableMask(0);
Log::print("Edge rendering disabled.\n");
}
else
if (ch == '2')
{
mSolidRenderer->setEnableMask(0xFFFFFFFF);
// preserve color buffer, clear depth buffer
mEdgeRenderer->setClearFlags(CF_CLEAR_DEPTH);
mEdgeRenderer->setEnableMask(0xFFFFFFFF);
mEdgeRenderer->setShowCreases(false);
mEdgeRenderer->setShowHiddenLines(false);
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(CF_CLEAR_DEPTH);
mEdgeRenderer->setEnableMask(0xFFFFFFFF);
mEdgeRenderer->setShowCreases(true);
mEdgeRenderer->setShowHiddenLines(false);
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(CF_CLEAR_DEPTH);
mEdgeRenderer->setEnableMask(0xFFFFFFFF);
mEdgeRenderer->setShowCreases(true);
mEdgeRenderer->setShowHiddenLines(true);
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(CF_CLEAR_COLOR_DEPTH);
mEdgeRenderer->setEnableMask(0xFFFFFFFF);
mEdgeRenderer->setShowCreases(true);
mEdgeRenderer->setShowHiddenLines(false);
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(CF_CLEAR_COLOR_DEPTH);
mEdgeRenderer->setEnableMask(0xFFFFFFFF);
mEdgeRenderer->setShowCreases(true);
mEdgeRenderer->setShowHiddenLines(true);
Log::print("Hidden line removal wireframe enabled. Creases = on, hidden lines = on.\n");
}
}
void resizeEvent(int w, int h)
{
Camera* camera = mMainRendering->camera();
camera->viewport()->setWidth ( w );
camera->viewport()->setHeight( h );
camera->setProjectionPerspective();
}
void loadModel(const std::vector<String>& files)
{
// resets the scene
sceneManager()->tree()->actors()->clear();
// resets the EdgeRenderer cache
mEdgeRenderer->clearCache();
for(size_t ifile=0; ifile<files.size(); ++ifile)
{
ref<ResourceDatabase> resource_db = loadResource(files[ifile],true);
if (!resource_db || resource_db->count<Actor>() == 0)
{
Log::error("No data found.\n");
continue;
}
std::vector< ref<Actor> > actors;
resource_db->get<Actor>(actors);
for(unsigned iactor=0; iactor<actors.size(); ++iactor)
{
ref<Actor> actor = actors[iactor].get();
// define a reasonable Shader
actor->effect()->shader()->setRenderState( new Light, 0 );
actor->effect()->shader()->enable(EN_DEPTH_TEST);
actor->effect()->shader()->enable(EN_LIGHTING);
actor->effect()->shader()->gocLightModel()->setTwoSide(true);
// add the actor to the scene
sceneManager()->tree()->addActor( actor.get() );
}
}
// position the camera to nicely see the objects in the scene
trackball()->adjustView( sceneManager(), vec3(0,0,1)/*direction*/, vec3(0,1,0)/*up*/, 1.0f/*bias*/ );
}
// laod the files dropped in the window
void fileDroppedEvent(const std::vector<String>& files) { loadModel(files); }
protected:
ref< Renderer > mSolidRenderer;
ref< EdgeRenderer > mEdgeRenderer;
ref<Rendering> mMainRendering;
};
// Have fun!