# OpenGL GLSL - Sobel Edge Detection Filter

With respect to this topic I've successfully implemented the Sobel Edge Detection filter in GLSL. Here is the fragment shader code of the filter:

#version 330 core
in vec2 TexCoords;
out vec4 color;

uniform sampler2D screenTexture;

mat3 sx = mat3(
1.0, 2.0, 1.0,
0.0, 0.0, 0.0,
-1.0, -2.0, -1.0
);
mat3 sy = mat3(
1.0, 0.0, -1.0,
2.0, 0.0, -2.0,
1.0, 0.0, -1.0
);

void main()
{
vec3 diffuse = texture(screenTexture, TexCoords.st).rgb;
mat3 I;
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
vec3 sample  = texelFetch(screenTexture, ivec2(gl_FragCoord) + ivec2(i-1,j-1), 0 ).rgb;
I[i][j] = length(sample);
}
}

float gx = dot(sx[0], I[0]) + dot(sx[1], I[1]) + dot(sx[2], I[2]);
float gy = dot(sy[0], I[0]) + dot(sy[1], I[1]) + dot(sy[2], I[2]);

float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));
color = vec4(diffuse - vec3(g), 1.0);
}


And here is the result of a cube with Sobel edge detection:

If you enlarge the picture, you will see that there is a lot of "noise" produced by Sobel: There are gray horizontal stripes all over the scene due to the blue/white gradient. Futhermore, the light cones produce an unwanted pattern on the cube. The black edges on the left of the cube also seem to fade because of the light cone on the left half of the cube.

So I read this article which stated that one should grayscale the image first and use a Gaussian blur filter to make the edges more apparent. On the bottom of the article, there is also the canny edge detection filter which seem to produce better results.

Now I have two questions:

1. Are the following steps correct to produce the best possible edge detection results:

• Grayscale
• Gaussian Blur
• Sobel/Canny edge detection
2. If yes, how would I merge the original image with the processed image? I mean, after processing the steps stated above, I get an image that is either completely black with white edges or vice verca. How would I put the edges on my original image/texture?

Thanks for your help!

• I wonder if you might get the results you want by utilizing OpenGL's edge flag in some way. It looks like only edges between vertices with the edge flag on are drawn when in line mode. There's a description here. (I've not used it myself or I'd post an example.) – user1118321 Jun 19 '16 at 21:44

1. The best results strongly depend on your use case. They also depend on what effect you want to achieve. Sobel is just an edge detection filter: the edges will depend on the input signal, choosing that input signal is up to you.

Here you are using the color image as an input, and the filter rightfully detects faint edges in the blue gradient, while the edges of the cube get interrupted where its color is too close from the background color.

Since I assume your program is also responsible for drawing the cube, you have access to other information that you can feed to your Sobel filter. For example depth and normals are good candidates for edge detection. The albedo before lighting could be used to. Test with different inputs and decide which ones to use depending on the results you get.

2. Regarding your question about how to combine the edge information, I suggest you filter g a little before using it. Then you can use it to interpolate between the original color and the wanted edge color.

For example you could try something like this:

    float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));

// Try different values and see what happens
g = smoothstep(0.4, 0.6, g);

vec3 edgeColor = vec3(1., 0., 0.2);
color = vec4(mix(diffuse, edgeColor, g), 1.);


To use depth or normals, you would need to save them in a texture if that's not done already. When you create the frame buffer for your regular rendering pass, you can attach various textures to it (see glFramebufferTexture2D) and write other information to them than just the color of the scene.
If you attach a depth texture (with GL_DEPTH_ATTACHMENT), it will be used automatically for depth. If you attach one or several color textures (with GL_COLOR_ATTACHMENTi), you can write to them by declaring several outputs to your fragment shaders (this used to be done with gl_FragData; either way, see glDrawBuffers).