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.
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 shapeThe
RED::IMeshShape::BuildBorderEdges
creates border edges; a border edge belongs to a single triangle in the meshThe
RED::IMeshShape::BuildContourEdges
is like the first one but adds contour extraction data. They have to be rendered with aRED::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.
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.