My goal is to implement an edge detection algorithmus that is capable to find edges of arbitrary 3d meshes. I want to find the edges by detecting normal discontinuities. Furthermore, I want the edges to be one pixel wide.
I render the color values of my scene and the corresponding normal vectors to two different render buffer targets, i.e. to two different textures (screenTexture and normalTexture). I use the sobel edge detection algorithmus in my fragment shader:
#version 330 core
out vec4 color;
uniform sampler2D screenTexture;
uniform sampler2D normalTexture;
uniform sampler2D depthTexture;
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()
{
vec4 diffuse = texelFetch(screenTexture, ivec2(gl_FragCoord), 0);
vec3 normal = texelFetch(normalTexture, ivec2(gl_FragCoord), 0 ).xyz;
vec3 I[3];
for (int i=0; i<3; i++) {
float sampleValLeft = dot(normal, texelFetch(normalTexture, ivec2(gl_FragCoord) + ivec2(i-1,-1), 0 ).rgb);
float sampleValMiddle = dot(normal, texelFetch(normalTexture, ivec2(gl_FragCoord) + ivec2(i-1,0), 0 ).rgb);
float sampleValRight = dot(normal, texelFetch(normalTexture, ivec2(gl_FragCoord) + ivec2(i-1,1), 0 ).rgb);
I[i] = vec3(sampleValLeft, sampleValMiddle, sampleValRight);
}
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));
g = smoothstep(0.4,0.8, g);
if(g > 0.2) {
color = vec4(0., 0., 0.0, 1);
} else {
color = diffuse;
}
}
The shader produces the following result:
As you can see, the line thickness of the solids are not consistent:
- Edges visible between the turquois background and the solid are 1px wide like intended.
- Edges within the object are 2px wide which is too bride.
The reason why 1. works is because I initalized normalTexture with vec3(0,0,0) normal vectors which produces a dot-product of 0 as soon as the current pixel is a scene pixel and the adjacent right or left pixel is an edge pixel of the solid.
But edges within the object are detected two times by sobel because of a gx or gy which is one time positive and one time negative. In other words, gx or gy is both times not 0 and therefore g is greater than 0.2 both of the time.
I'm totally stuck what I can do to produce consistent wide lines.