Stencil Glow         A Stencil Glow example for creating outlines

This page explains how to add a glowing outline to your entities.

The code comes from the following forum post: https://forums.ogre3d.org/viewtopic.php?f=11&t=27477

Example

Sgsh 4

Files

You will need to add these files to your resources:

glow.material

// *** GLSL ***
vertex_program glow_vs_glsl glsl
{
	source vs_glow.glsl
}

fragment_program glow_ps_glsl glsl
{
	source ps_glow.glsl
}

material glow
{
	technique
	{
		pass
		{
			scene_blend alpha_blend
			depth_check on
			lighting off
			
			fragment_program_ref glow_ps_glsl 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.4
			}
			vertex_program_ref glow_vs_glsl 
			{
				param_named size_value float 1.1
				param_named_auto time time_0_x 100
			}
		}
	}
}

material alpha_glow
{
	technique
	{
		pass
		{
			scene_blend alpha_blend
			depth_check on
			lighting off
			
			fragment_program_ref glow_ps_glsl 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.0
			}
			vertex_program_ref glow_vs_glsl 
			{
				param_named size_value float 1.1
				param_named_auto time time_0_x 100
			}
		}

		pass
		{
			scene_blend alpha_blend
			depth_check on
			lighting off
			
			fragment_program_ref glow_ps_glsl 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.0
			}
		}
	}
}

material no_depth_check_glow
{
	technique
	{
		pass
		{
			scene_blend alpha_blend
			depth_check off
			lighting off
			
			fragment_program_ref glow_ps_glsl 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.4
			}
			vertex_program_ref glow_vs_glsl 
			{
				param_named size_value float 1.1
				param_named_auto time time_0_x 100
			}
		}
	}
}

// *** CG ***
vertex_program glow_vs_cg cg
{
	source vs_glow.cg
	entry_point main
	profiles vs_1_1 arbvp1
}

fragment_program glow_ps_cg cg
{
	source ps_glow.cg
	entry_point main
	profiles ps_2_0 arbfp1	
}

material cg/glow
{
	technique
	{
		pass
		{
			scene_blend alpha_blend
			depth_check on
			lighting off

			vertex_program_ref glow_vs_cg
			{
				param_named_auto worldViewProjMatrix worldviewproj_matrix
				param_named size_value float 1.1
				param_named_auto time time_0_x 100
			}
			
			fragment_program_ref glow_ps_cg 
			{
				param_named alpha_value float 0.4
				param_named_auto time time_0_x 100
			}
		}
	}
}

material cg/alpha_glow
{
	technique
	{
		pass
		{
			scene_blend alpha_blend
			depth_check on
			lighting off
			
			fragment_program_ref glow_ps_cg 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.0
			}
			vertex_program_ref glow_vs_cg 
			{
				param_named_auto worldViewProjMatrix worldviewproj_matrix
				param_named size_value float 1.1
				param_named_auto time time_0_x 100
			}
		}
		
	pass
		{
			scene_blend alpha_blend
			depth_check on
			lighting off
			
			fragment_program_ref glow_ps_cg 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.0
			}
		}
	}
}

material cg/no_depth_check_glow
{
	technique
	{
		pass
		{
			scene_blend alpha_blend
			depth_check off
			lighting off
			
			fragment_program_ref glow_ps_cg 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.4
			}
		}
		
		pass
		{
			scene_blend alpha_blend
			depth_check off
			lighting off
			
			fragment_program_ref glow_ps_cg 
			{
				param_named_auto time time_0_x 100
				param_named alpha_value float 0.4
			}
			vertex_program_ref glow_vs_cg 
			{
				param_named_auto worldViewProjMatrix worldviewproj_matrix
				param_named size_value float 1.1
				param_named_auto time time_0_x 100
			}
		}
	}
}

ps_glow.glsl

uniform float alpha_value;
uniform float time;

void main(void)
{ 
	vec4 color;
	color.x = 1.0;
	color.y = 1.0;
	color.z = 0.0;
	color.w =  alpha_value * ((sin(time * 5.0) + 1.0) / 2.0 );
	gl_FragColor = color;
}

vs_glow.glsl

uniform float size_value;
uniform float time;

void main(void)
{
	vec3 Pos = gl_Vertex.xyz + ( (size_value * (1.0 + (sin(time * 5.0) + 1.0) / 15.0 ) * normal );

	gl_Position = gl_ModelViewProjectionMatrix * vec4(Pos, 1.0);
}

ps_glow.cg

float4 main(	uniform float alpha_value,
				uniform float time) : COLOR
{
	float4 color;
	color.x = 1.0;
	color.y = 1.0;
	color.z = 0.0;
	color.w = alpha_value * ((sin(time * 5.0) / 3.14 + 1.0) / 2.0 );
	return color;
}

vs_glow.cg

void main(	float4 position 		: POSITION,
			float3 normal			: NORMAL,
			float2 uv				: TEXCOORD0,
			out float4 oPosition	: POSITION,
			out float2 oUv			: TEXCOORD0,
			out float4 colour		: COLOR,
			uniform float4x4 worldViewProjMatrix,
			uniform float size_value,
			uniform float time)
{
	float4 mypos = position;
	mypos.xyz += size_value * (1.0 + (sin(time * 5.0) + 1.0) / 5.0 ) * normal; 
	oPosition = mul(worldViewProjMatrix, mypos);
}

StencilOpQueueListener.h (add to your sources)

#ifndef GLOW_H
#define GLOW_H

#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <OgreRenderQueueListener.h>

// render queues
#define RENDER_QUEUE_OUTLINE_GLOW_OBJECTS	Ogre::RenderQueueGroupID::RENDER_QUEUE_MAIN + 1
#define RENDER_QUEUE_OUTLINE_GLOW_GLOWS		Ogre::RenderQueueGroupID::RENDER_QUEUE_MAIN + 2
#define RENDER_QUEUE_FULL_GLOW_ALPHA_GLOW	Ogre::RenderQueueGroupID::RENDER_QUEUE_MAIN + 3
#define RENDER_QUEUE_FULL_GLOW_GLOW			Ogre::RenderQueueGroupID::RENDER_QUEUE_MAIN + 4
#define LAST_STENCIL_OP_RENDER_QUEUE		RENDER_QUEUE_FULL_GLOW_GLOW

// stencil values
#define STENCIL_VALUE_FOR_OUTLINE_GLOW 1
#define STENCIL_VALUE_FOR_FULL_GLOW 2
#define STENCIL_FULL_MASK 0xFFFFFFFF

// a Render queue listener to change the stencil mode
class StencilOpQueueListener : public Ogre::RenderQueueListener
{
public:
	virtual void renderQueueStarted(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& skipThisInvocation)
	{
		if (queueGroupId == RENDER_QUEUE_OUTLINE_GLOW_OBJECTS) // outline glow object
		{
			Ogre::RenderSystem * rendersys = Ogre::Root::getSingleton().getRenderSystem();

			// PORTABILIDAD: OGRE 1.11 se agrego el parametro: STENCIL_FULL_MASK
			// https://www.ogre3d.org/docs/api/1.8/class_ogre_1_1_render_system.html
			// https://ogrecave.github.io/ogre/api/1.11/class_ogre_1_1_render_system.html
			rendersys->clearFrameBuffer(Ogre::FBT_STENCIL);
			rendersys->setStencilCheckEnabled(true);
			rendersys->setStencilBufferParams(Ogre::CompareFunction::CMPF_ALWAYS_PASS,
				STENCIL_VALUE_FOR_OUTLINE_GLOW, STENCIL_FULL_MASK, STENCIL_FULL_MASK,
				Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_REPLACE, false);
		}
		if (queueGroupId == RENDER_QUEUE_OUTLINE_GLOW_GLOWS)  // outline glow
		{
			Ogre::RenderSystem * rendersys = Ogre::Root::getSingleton().getRenderSystem();
			rendersys->setStencilCheckEnabled(true);
			rendersys->setStencilBufferParams(Ogre::CompareFunction::CMPF_NOT_EQUAL,
				STENCIL_VALUE_FOR_OUTLINE_GLOW, STENCIL_FULL_MASK, STENCIL_FULL_MASK,
				Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_REPLACE,false);
		}
		if (queueGroupId == RENDER_QUEUE_FULL_GLOW_ALPHA_GLOW)  // full glow - alpha glow
		{
			Ogre::RenderSystem * rendersys = Ogre::Root::getSingleton().getRenderSystem();
			rendersys->setStencilCheckEnabled(true);
			rendersys->setStencilBufferParams(Ogre::CompareFunction::CMPF_ALWAYS_PASS,
				STENCIL_VALUE_FOR_FULL_GLOW,STENCIL_FULL_MASK, STENCIL_FULL_MASK,
				Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_REPLACE,false);
		}

		if (queueGroupId == RENDER_QUEUE_FULL_GLOW_GLOW)  // full glow - glow
		{
			Ogre::RenderSystem * rendersys = Ogre::Root::getSingleton().getRenderSystem();
			rendersys->setStencilCheckEnabled(true);
			rendersys->setStencilBufferParams(Ogre::CompareFunction::CMPF_EQUAL,
				STENCIL_VALUE_FOR_FULL_GLOW,STENCIL_FULL_MASK, STENCIL_FULL_MASK,
				Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_KEEP,Ogre::StencilOperation::SOP_ZERO,false);
		}
	}

	virtual void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation)
	{
		if ( queueGroupId == LAST_STENCIL_OP_RENDER_QUEUE )
		{
			Ogre::RenderSystem * rendersys = Ogre::Root::getSingleton().getRenderSystem();
			rendersys->setStencilCheckEnabled(false);
			rendersys->setStencilBufferParams();
		}
	}
};

#endif // GLOW_H

Usage

Set the RenderQueue Listener

StencilOpQueueListener* stencilOpFrameListener;
    stencilOpFrameListener = new StencilOpQueueListener();
    mSceneMgr->addRenderQueueListener(stencilOpFrameListener);


Set the entities that will glow

// outline glow entity
    Ogre::Entity *outlineGlowEntity = mSceneMgr->createEntity("outlineGlow", "ogrehead.mesh");
    outlineGlowEntity->setRenderQueueGroup(RENDER_QUEUE_OUTLINE_GLOW_OBJECTS);
    mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(outlineGlowEntity);

    // outline glow entity actual glow
    Ogre::Entity* actualOutlineGlowEntity = outlineGlowEntity->clone(outlineGlowEntity->getName() + "_glow");
    actualOutlineGlowEntity->setRenderQueueGroup(RENDER_QUEUE_OUTLINE_GLOW_GLOWS);
    actualOutlineGlowEntity->setMaterialName("cg/glow");
    if(outlineGlowEntity->hasSkeleton())    // To make the glow animate with it's parent
        actualOutlineGlowEntity->shareSkeletonInstanceWith(outlineGlowEntity);
    Ogre::SceneNode* actualOutlineGlowNode = outlineGlowEntity->getParentSceneNode()->createChildSceneNode("outlineGlowNode");
    actualOutlineGlowNode->attachObject(actualOutlineGlowEntity);

    // normal entity
    Ogre::Entity *normalOgreEntity = mSceneMgr->createEntity("normalOgreEntity", "ogrehead.mesh");
    Ogre::SceneNode* normalOgreNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    normalOgreNode->attachObject(normalOgreEntity);
    normalOgreNode->setPosition(80, 0, 0);

    // full glow entity
    Ogre::Entity *fullGlowEntity = mSceneMgr->createEntity("fullGlowEntity", "ogrehead.mesh");
    Ogre::SceneNode* fullGlowNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    fullGlowNode->attachObject(fullGlowEntity);
    fullGlowNode->setPosition(-80, 0, 0);

    // full glow alpha glow
    Ogre::Entity* alphaFullGlowEntity = fullGlowEntity->clone(fullGlowEntity->getName() + "_alphaGlow");
    alphaFullGlowEntity->setRenderQueueGroup(RENDER_QUEUE_FULL_GLOW_ALPHA_GLOW);
    alphaFullGlowEntity->setMaterialName("cg/alpha_glow");
    if(fullGlowEntity->hasSkeleton())    // To make the glow animate with it's parent
        alphaFullGlowEntity->shareSkeletonInstanceWith(fullGlowEntity);
    Ogre::SceneNode* alphaFullGlowNode = fullGlowEntity->getParentSceneNode()->createChildSceneNode("fullGlowAlphaNode");
    alphaFullGlowNode->attachObject(alphaFullGlowEntity);

    // full glow alpha glow
    Ogre::Entity* glowFullGlowEntity = fullGlowEntity->clone(fullGlowEntity->getName() + "_glow");
    glowFullGlowEntity->setRenderQueueGroup(RENDER_QUEUE_FULL_GLOW_GLOW);
    glowFullGlowEntity->setMaterialName("cg/no_depth_check_glow");
    if(fullGlowEntity->hasSkeleton())    // To make the glow animate with it's parent
        glowFullGlowEntity->shareSkeletonInstanceWith(fullGlowEntity);
    Ogre::SceneNode* glowFullGlowNode = fullGlowEntity->getParentSceneNode()->createChildSceneNode("fullGlowGlowNode");
    glowFullGlowNode->attachObject(glowFullGlowEntity);


To disable, just remove the glow entity from the SceneNode:

actualOutlineGlowNode->detachObject(actualOutlineGlowEntity);
    mSceneMgr->destroyEntity(actualOutlineGlowEntity);


Please note that this technique does not work well with stencil shadows


Written by sercero