Sign in to follow this  
Krisc

Stroke / Outer Glow Shader Question

Recommended Posts

I am working on a stroke shader to try and get a thin line around the edge of a rendered image. The problem is that the stroke "falloff" isn't working. It seems to be either on or off rather than a measure of any distance (for example distance from the edge). I have been modifying a shader I found on the web for edge detection. Ideally I would like a shader that has a gradient falloff as well as the ability to do a thin stroke. I seem to be able to do neither. So far I have controlled the stroke thickness with the Offset, but it only works up to a certain point. I am also using the shader to do stencil tests.
uniform extern texture ColorTexture;
uniform extern texture StencilTexture;
uniform extern float Offset = 1.0 / 1024.0;

sampler2D ColorSampler = sampler_state
{
	Texture = (ColorTexture);
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler2D StencilSampler = sampler_state
{
	Texture = (StencilTexture);
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

float GetEdge(float2 texCoord: TEXCOORD) : COLOR {
   // Sample neighbor pixels
   float s00 = tex2D(StencilSampler, texCoord + float2(-Offset, -Offset)).r;
   float s01 = tex2D(StencilSampler, texCoord + float2( 0,		-Offset)).r;
   float s02 = tex2D(StencilSampler, texCoord + float2( Offset, -Offset)).r;

   float s10 = tex2D(StencilSampler, texCoord + float2(-Offset,  0)).r;
   float s12 = tex2D(StencilSampler, texCoord + float2( Offset,  0)).r;

   float s20 = tex2D(StencilSampler, texCoord + float2(-Offset,  Offset)).r;
   float s21 = tex2D(StencilSampler, texCoord + float2( 0,	     Offset)).r;
   float s22 = tex2D(StencilSampler, texCoord + float2( Offset,  Offset)).r;
   
   float scale = 0.125;

   // Sobel filter in X direction
   float sobelX = s00 + scale * s10 + s20 - s02 - scale * s12 - s22;
   
   // Sobel filter in Y direction
   float sobelY = s00 + scale * s01 + s02 - s20 - scale * s21 - s22;

   float edgeSqr = sqrt(sobelX * sobelX + sobelY * sobelY);
   
   //return edgeSqr;
}

float4 StencilPS(float2 TexCoords : TEXCOORD0) : COLOR0
{
	float4 FinalColor;
	
	float2 samp = TexCoords;
	
	FinalColor = tex2D(ColorSampler, samp);
	FinalColor.a = tex2D(StencilSampler, samp).r;
	
	float Edge = GetEdge(TexCoords);
	
	if(Edge > 0.0) FinalColor = float4(0, 0, 0, Edge / 4);
	
	return FinalColor;
}

technique Stencil
{
    pass P0
    {
        PixelShader = compile ps_2_0 StencilPS();
    }
}
Here are the results... FGF Aero in XNA

Share this post


Link to post
Share on other sites
I can imagine this one being non-trivial [smile]

The edge-blurs I've done before have used multiple render-targets and, whilst functional, aren't necessarily very fast.

One example was to render the edge (single pixel) to a seperate target, gaussian blur this (note, blurs inwards as well as outwards), render the "edged" object onto this same target (overwrites the inward blur) then composite this back on to the final image. A bit of clever alpha-output allows it to work quite nicely. Unfortunately it doesn't necessarily work for internal edges, only the silhouette outline.

You may be able to perform the gauss blur with a stencil mask to avoid the extra render target.

Another thing you could look into is the various kernel filters to try and figure out whether your pixel is near to the actual edge and shade accordingly. Some sort of sobel-weighted gaussian or Kawase's streak light shader (GDC'03).

hth
Jack

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this