How to draw smoothed edge ellipse in shader?

I’m trying to draw an ellipse with smoothed edges using smoothstep function.
If I draw a circle and squash it, the edge transition is different horizontally and vertically:

  void main() {

    vec2 spread = vec2(0.1, 0.4);
    
    float trans = 0.1;
    float color = smoothstep(1.0 - trans, 1.0 + trans, length((vuv - vec2(0.5)) / spread));

    gl_FragColor = vec4(0.25 * color, 0.0, 0.0, 1.0);
  }

I can draw concentric ellipses with equal gap between them, but I’m not sure how to feed this to smoothstep:

  void main() {

    vec2 uv = vuv - vec2(0.5);

    vec2 spreadVar = vec2(0.05);
    vec2 spread = vec2(0.2, 0.4);
    vec2 spreadIn = spread - spreadVar;
    
    float dist = length(uv / spread);
    float dist2 = length(uv / spreadIn);

    float circle = step(1.0, dist) + step(1.0, dist2); 

    gl_FragColor = vec4(0.25 * circle, 0.0, 0.0, 1.0);
  }

Hi!
Not sure, if this is what you’re looking for, but here is an option with fwidth() :thinking: :

<script type='x-shader/x-fragment' id='fs_smooth'>

  varying vec2 vuv;

  void main() {

    vec2 spread = vec2(0.1, 0.4);
    
    vec2 fw = fwidth(vuv); // added
    float trans = min(fw.x, fw.y); // changed
    float color = smoothstep(1.0 - trans, 1.0 + trans, length((vuv - vec2(0.5)) / spread));

    gl_FragColor = vec4(0.25 * color, 0.0, 0.0, 1.0);
  }
</script>

Result:

3 Likes

My problem is the different size of the transition zone horizontally and vertically (left image), I’d like it to be the same (right image).
I faked the right image by blurring sharp oval in PS.
I don’t want to blur, I’d like to use something fast and producing visually similar result (like smoothstep).

fwidth() doesn’t help me in this case, I’m working with a quad without perspective, so dFdxy is constant across the canvas, but so is trans already, if you set vec2 fw = 100.0 * fwidth(vuv); you’ll get the same image as with trans = 0.1.

Why not search shadertoy for SDF of ellipse, there are some implemetations.
For example: Shader - Shadertoy BETA

3 Likes

Tried another option:

<script type='x-shader/x-fragment' id='fs_sharp'>

  varying vec2 vuv;

  void main() {

    vec2 uv = vuv - vec2(0.5);
		
    float spreadVal = 0.05;
    vec2 spreadVar = vec2(spreadVal);
    vec2 spread = vec2(0.2, 0.3);
    vec2 spreadIn = spread - spreadVar;
    
    float dist = length(uv / spread);
    float dist2 = length(uv / spreadIn);
    float circle = smoothstep(dist2, dist, dist2/dist);

    gl_FragColor = vec4(0.5 * circle, 0.0, 0.0, 1.0);
  }
  
  </script>

2 Likes
float circle = smoothstep(dist2, dist, 1.); 
2 Likes

I found SDF solution, but I believe this one is slightly faster, nice!

Added all versions of code to the original fiddle.

Thanks everyone!

2 Likes