<p>The edge between the sphere and the background is actually the one that is incorrect; you need to initialize your normal texture with a unit normal to get correct results.</p>

<p>The two pixels thickness is a limitation of the Sobel based edge detection and other 3x3 convolution filters: you can only detect edges twice as big as your pixels. The <a href="https://en.wikipedia.org/wiki/Roberts_cross" rel="nofollow">Robert operator</a> uses a 2x2 convolution filter with weights making a cross, so it yields thinner edges but they are still thicker than one pixel.</p>

<p>If you absolutely want one pixel width, you could use filtered texture sampling and sample every half pixel when applying the Sobel operator, but the detection will probably miss some features.</p>

CC BY-SA 3.0
2016-08-01T16:01:03.243Z
Would sampling half pixels give equivalent results to generating an image at double resolution, producing lines 2 pixels wide, and then scaling the image down to the intended size?
trichoplax is on Codidact now
CC BY-SA 3.0
2016-08-01T18:49:32.863Z
Couldn't I just use the sign of the dot-product (i.e. gx or gy) in any way? For example, when sampling a horizontal edge, i get -4 for gx on one side of the edge and 4 on the other side.
enne87
CC BY-SA 3.0
2016-08-02T22:46:13.750Z
Just one more question: Would morphological erosion (http://homepages.inf.ed.ac.uk/rbf/HIPR2/erode.htm) or thinnening (http://homepages.inf.ed.ac.uk/rbf/HIPR2/thin.htm) be an alternative?
enne87
CC BY-SA 3.0
2016-08-03T01:35:57.573Z
@enne87 why not skip edge detection alltogether and use edge geometry to draw the lines?
joojaa
CC BY-SA 3.0
2016-08-03T06:14:15.107Z
@enne87: yes I think erosion could be a possibility, but I didn't suggest it because I'm not familiar enough with it so I don't know how well suited it is.
Julien Guertault
CC BY-SA 3.0
2016-08-03T08:55:50.733Z
Thank you both! @joojaa: I thought about it but how would I draw the outline of a sphere with geometrical data?
enne87
CC BY-SA 3.0
2016-08-03T09:10:08.330Z
@enne87 you draw the edges on the culling treshold and hard edges slightly offset towards the camera. A sphere is in fact one of the easier shapes.
joojaa
CC BY-SA 3.0
2016-08-03T10:30:47.260Z
@joojaa: Thanks for your suggestion. Just to make clear that I understand your idea: 

1. For the silhouette I render the edges of the backfaces and move the faces in screen Z forward so that only the edges of the backfacing triangles are visible, right?

2. Sorry but I don't understand how I would draw the inner edges (creases and ridges) ?
enne87
CC BY-SA 3.0
2016-08-03T11:01:01.980Z
@enne87 edges on the front backface boundary prefeerably. You draw them as gl lines.
joojaa
CC BY-SA 3.0
2016-08-01T11:58:01.420Z
2016-08-01T12:14:06.147Z
2016-08-01T12:14:06.147Z
Julien Guertault
<p>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.</p>

<p>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:</p>

<pre><code>#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&lt;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 &gt; 0.2) {
        color = vec4(0., 0., 0.0, 1);
    } else {
        color = diffuse;
    }
  }
</code></pre>

<p>The shader produces the following result:</p>

<p><a href="https://i.sstatic.net/XjsGs.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/XjsGs.png" alt="enter image description here"></a></p>

<p>As you can see, the line thickness of the solids are not consistent: </p>

<ol>
<li>Edges visible between the turquois background and the solid are 1px wide like
intended.</li>
<li>Edges within the object are 2px wide which is too bride.</li>
</ol>

<p>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.</p>

<p>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.</p>

<p>I'm totally stuck what I can do to produce consistent wide lines.</p>

<edge-detection>
Sobel edge detection line thickness
enne87
# Sobel edge detection line thickness

(Asked by: username_0 on 2016-07-31)

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:

[![enter image description here](https://i.sstatic.net/XjsGs.png)](https://i.sstatic.net/XjsGs.png)

As you can see, the line thickness of the solids are not consistent:

1. Edges visible between the turquois background and the solid are 1px wide like
   intended.
2. 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.

**Tags:** edge-detection


---

## 1 answers

### Answer by username_1 on 2016-08-01. Score: 1

The edge between the sphere and the background is actually the one that is incorrect; you need to initialize your normal texture with a unit normal to get correct results.

The two pixels thickness is a limitation of the Sobel based edge detection and other 3x3 convolution filters: you can only detect edges twice as big as your pixels. The [Robert operator](https://en.wikipedia.org/wiki/Roberts_cross) uses a 2x2 convolution filter with weights making a cross, so it yields thinner edges but they are still thicker than one pixel.

If you absolutely want one pixel width, you could use filtered texture sampling and sample every half pixel when applying the Sobel operator, but the detection will probably miss some features.

> **Comments:**
> **username_2**: Would sampling half pixels give equivalent results to generating an image at double resolution, producing lines 2 pixels wide, and then scaling the image down to the intended size?  
> **username_0**: Couldn't I just use the sign of the dot-product (i.e. gx or gy) in any way? For example, when sampling a horizontal edge, i get -4 for gx on one side of the edge and 4 on the other side.  
> **username_0**: Just one more question: Would morphological erosion (http://homepages.inf.ed.ac.uk/rbf/HIPR2/erode.htm) or thinnening (http://homepages.inf.ed.ac.uk/rbf/HIPR2/thin.htm) be an alternative?  
> **username_3**: @username_0 why not skip edge detection alltogether and use edge geometry to draw the lines?  
> **username_1**: @username_0: yes I think erosion could be a possibility, but I didn't suggest it because I'm not familiar enough with it so I don't know how well suited it is.  
> **username_0**: Thank you both! @username_3: I thought about it but how would I draw the outline of a sphere with geometrical data?  
> **username_3**: @username_0 you draw the edges on the culling treshold and hard edges slightly offset towards the camera. A sphere is in fact one of the easier shapes.  
> **username_0**: @username_3: Thanks for your suggestion. Just to make clear that I understand your idea:   1. For the silhouette I render the edges of the backfaces and move the faces in screen Z forward so that only the edges of the backfacing triangles are visible, right?  2. Sorry but I don't understand how I would draw the inner edges (creases and ridges) ?  
> **username_3**: @username_0 edges on the front backface boundary prefeerably. You draw them as gl lines.  

---

