I have a simple prototype with 2D worms-like destructible terrain. I use a trivial shader to discard pixels based on a mask.

varying vec2 v_texCoords;
uniform sampler2D u_texture;

void main()
{
vec4 colour = texture2D(u_texture, v_texCoords);

else {
gl_FragColor = colour;
}
}


What would be a good and simple way to add few pixels thick black outlines to the terrain to make the carved parts look better?

I've researched something about edge detection algorithms, but my hunch says that such a fullblown thing would be kind of an overkill for this task?

I'm not after quality or high performance, but simplicity.

Thanks for any ideas.

• Maybe you could post an example picture that has some arrows pointing at the effect you are trying to achieve. There are tons of options for non-photorealistic shaders, it would help if you narrow this down a bit with some reference. – MarcClintDion Jun 28 '14 at 18:52
• For instance, have a look at this page. Is this what you are describing? sunandblackcat.com/tipFullView.php?l=eng&topicid=15 – MarcClintDion Jun 28 '14 at 18:58

You should generate a distance field instead of a simple mask. In each pixel in the empty area, instead of storing 0 you store the distance to the closest pixel containing terrain. So you can return a black pixel if the value is for example between 0 and 0.1.

There are multiple algorithms to compute distance fields, so I let you find one that would suit your needs. You can find some implementations from the Wikipedia article I linked.

Here is a shader that does an outline in the simplest way that I know of. It just uses the dot product of the normal and light to cutoff colors based on the normal angle. Really, I suppose the view direction should be used instead of the light position but this gives control over the direction of the edge highlighting if you want that. There are 3 color gradients but you can take out the middle one so that you only have black(or dark brown) and the color output of your shader. You would just have to replace the line of code that I added a comment to with your 'colour' variable.

If you want the transistion between light and dark to be anti-aliased then you may want to look for some examples on how to use the GLSL SmoothStep function.

Here's what it looks like using the code I posted

And the code.

//lightPosition = 6.6


//===========================================================================

uniform       vec4    light_POSITION_01;
uniform       mat4    mvpMatrix;
uniform       mat4    modelView;
uniform       mat4    lightMatrix;

attribute     vec4    position;
attribute     vec3    normal;
attribute     vec2    texture;

varying       vec3    normalPass;
varying       vec2    varTexcoord;

void main()
{

normalPass             = (modelView *  vec4(normal, 1.0)).xyz; //there is no non-uniform scaling so modelView should be fine

varTexcoord            = texture;

gl_Position            = mvpMatrix * position;

}


//===========================================================================

uniform   sampler2D  Texture1;

uniform   vec4       light_POSITION_01;

varying   vec2       varTexcoord;
varying   vec3       normalPass;

float      NdotL1;

void main()
{

NdotL1                    =  dot(normalize(normalPass), light_POSITION_01.xyz);

if(NdotL1 > 0.5)
{
gl_FragColor        =  vec4(1.0); //REPLACE THIS LINE WITH THE COLOR OUTPUT OF YOUR SHADER
}
else if(NdotL1 > 0.2)
{
gl_FragColor        =  vec4(0.43*.7, 0.4*.7, 0.31*.7, 1.0);
}
else
{
gl_FragColor        =   vec4(0.43*.3, 0.4*.3, 0.31*.3, 1.0);
}

}


I copied the code snippet from page 3 of a .pdf document that I found when I was searching for something that fits your description. Here's is the link. (The description I've added here makes it possible to find the .pdf again somewhere else using a search engine if this link goes down. And it's good to give credit where credit is due)