Applying Polygon Offset to See Edges of a Geometry

This tutorial covers two topics:

  • The edges building

  • The polygon offset option of the RED::StateShader

We will create a simple scene with a cube and then build edges from it. Finally a polygon offset will be added in order to correctly see the edges over the geometry.

../../../_images/wf_ApplyingPolygonOffsetToSeeEdges01.jpg

The final cube with edges

The creation of the cube is simply a call to the RED::IMeshShape::Box function. Its material is a generic one and is created with the RED::IMaterial::SetupGenericDiffuseMaterial function. Nothing new here…

Building Edges

The edges are in fact a RED::ILineShape built from the cube. It exists three way to create edges shapes in HOOPS Luminate:

  • The RED::IMeshShape::BuildEdges creates edges around all triangles of the shape

  • The RED::IMeshShape::BuildBorderEdges creates border edges; a border edge belongs to a single triangle in the mesh

  • The RED::IMeshShape::BuildContourEdges is like the first one but adds contour extraction data. They have to be rendered with a RED::RenderShaderEdges shader

// Create the edge shape from the cube:
RED::Object* edge;
edge = RED::Factory::CreateInstance( CID_REDLineShape );
if( edge == NULL )
    return RED_ALLOC_FAILURE;

RC_TEST( icube->BuildEdges( edge, RED::MCL_VERTEX, RED::MCL_VERTEX, state ) );

// Add the edge shape to the scene:
RC_TEST( icamera->AddShape( edge, state ) );

The RED::ILineShape created with the three methods can be rendered using a classical render shader like a RED::RenderShaderSolid. Some line options can be changed using a RED::StateShader: line width, line smoothing, line stipples.

// Create the edge material:
// The edge material is composed of a simple solid shader in the RED::MTL_POSTLIT pass.
// The blending is disabled as we want the edges to be drawn over the geometry.
RED::Object* mat;
RC_TEST( iresmgr->CreateMaterial( mat, state ) );
RED::IMaterial* imat = mat->As< RED::IMaterial >();

// State shader:
RED::StateShader ssh;
RC_TEST( ssh.SetBlendingMode( RED::StateShader::NO_BLENDING ) );

RC_TEST( imat->RegisterShader( ssh, state ) );
RC_TEST( imat->AddShaderToPass( ssh.GetID(), RED::MTL_POSTLIT, RED::LIST_LAST, RED::LayerSet::ALL_LAYERS, state ) );

// Solid shader:
RED::RenderShaderSolid solid( RED::MTL_POSTLIT,
                            RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                            RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                            resmgr, rc );
RC_TEST( rc );

RC_TEST( imat->RegisterShader( solid, state ) );
RC_TEST( imat->AddShaderToPass( solid.GetID(), RED::MTL_POSTLIT, RED::LIST_LAST, RED::LayerSet::ALL_LAYERS, state ) );

// Apply the material to the shape:
RED::IShape* ishape = edge->As< RED::IShape >();
RC_TEST( ishape->SetMaterial( mat, state ) );

The solid shader is added to the RED::MTL_POSTLIT rendering pass because we want our edges to be drawn last. The blending is also disabled in the state shader.

../../../_images/wf_ApplyingPolygonOffsetToSeeEdges02.jpg

The final cube with border edges

Adding Polygon Offset

Unfortunately, simply adding edges shape is not sufficient to have nice edges over a geometry. Indeed both the cube and the edges are drawn at the same position; it could result on z-fighting, visual artefacts or edges not drawn completely.

One way to handle this could be to add a small depth offset when rendering the lines. The API provides functions to do this in the state shader:

  • RED::StateShader::SetPolygonOffset enables the polygon offsetting for all the following render shaders in the pipeline

  • RED::StateShader::SetPolygonOffsetValue sets the value of the offset

The polygon offsetting works only on meshes (RED::IMeshShape) and therefore can not be applied on our edges because they are in a line shape (RED::ILineShape). The solution is to offset all the scene geometry instead, i.e. the cube and the grid. A state shader is added at the beginning of each pass of the materials to handle the polygon offset:

// Add the polygon offset state shader at the beginning of each pass
// of each material of the scene.
RED::StateShader ssh;
RC_TEST( ssh.SetPolygonOffset( true ) );
RC_TEST( ssh.SetPolygonOffsetValue( 2.0f, 2.0f ) );

// Add it to the grid material:
RC_TEST( igridmat->RegisterShader( ssh, state ) );
RC_TEST( igridmat->AddShaderToPass( ssh.GetID(), RED::MTL_PRELIT,  RED::LIST_FIRST, RED::LayerSet::ALL_LAYERS, state ) );
RC_TEST( igridmat->AddShaderToPass( ssh.GetID(), RED::MTL_LIT,     RED::LIST_FIRST, RED::LayerSet::ALL_LAYERS, state ) );
RC_TEST( igridmat->AddShaderToPass( ssh.GetID(), RED::MTL_POSTLIT, RED::LIST_FIRST, RED::LayerSet::ALL_LAYERS, state ) );

// Make sure the grid is rendered before edges
RC_TEST( igridmat->SetPriority( 0, state ) );

// Add it to the cube material:
RC_TEST( icubemat->RegisterShader( ssh, state ) );
RC_TEST( icubemat->AddShaderToPass( ssh.GetID(), RED::MTL_PRELIT,  RED::LIST_FIRST, RED::LayerSet::ALL_LAYERS, state ) );
RC_TEST( icubemat->AddShaderToPass( ssh.GetID(), RED::MTL_LIT,     RED::LIST_FIRST, RED::LayerSet::ALL_LAYERS, state ) );
RC_TEST( icubemat->AddShaderToPass( ssh.GetID(), RED::MTL_POSTLIT, RED::LIST_FIRST, RED::LayerSet::ALL_LAYERS, state ) );

// Make sure the cube is rendered before edges
RC_TEST( icubemat->SetPriority( 1, state ) );

As you can see, the materials priority is changed to be sure that the edges material is rendered at last in the pipeline. The edges material kept the default priority: it is rendered last.

../../../_images/wf_ApplyingPolygonOffsetToSeeEdges03.jpg

The edges without polygon offset on the left and with polygon offset on the right