<p>The &quot;big idea&quot; around most anti aliasing algorithms is taking multiple samples in the region around the fragment in question. These can range from very simple above/below to random samples to complex heuristics.</p>
<p>Another issue is the distance that those samples are taken from the original fragment. Here again it can range from simple fixed distance to &quot;smart&quot; heuristics.</p>
<p>Last but not least is weighting the samples taken. And as you can guess this ranges from a simple average, to weights that compute the distance to other fragments and many other schemes. (weights are like CPU TLB algorithms, everyone has a better one, but we always use the generic version because its good enough)</p>
<p>There is also the issue of diminishing returns where taking more samples doesn't really help the final output.</p>
<p>I wrote a shader toy that demonstrates basic antialiasing using an ellipse (partly because I was to lazy to adjust for aspect ratio) and is very similar to the sample you submitted with your question. Also, I tried to make it easy to change the sampling and turn sampling on/off.</p>
<p>The shader toy can be found <a href="https://www.shadertoy.com/view/Ns3yzs" rel="nofollow noreferrer">here</a>.</p>
<p>Here is the code of the shader toy:</p>
<pre><code> // Comment this out to turn of sampling 
#define do_samples

// Average the samples together
float num_samples =0.0;

vec4 GetFragValAt( vec2 uv )
{
    num_samples += 1.0;
    
    // dot is same as uv.x*uv.x+uv.y*uv.y
    return vec4( dot( uv, uv) &lt;= 0.4);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
 
    // Normalized pixel coordinates (from -1 to 1)
    vec2 uv = fragCoord/iResolution.xy*2.0-1.0;
    
    // Change offset to adjust sampling distance
    float offset = 0.002;
    
    fragColor = GetFragValAt( uv );
    
    #ifdef do_samples
    // samples in +/- x direction
    fragColor += GetFragValAt(uv + vec2( offset, 0.0));
    fragColor += GetFragValAt(uv + vec2( -offset, 0.0));
    
    // samples in +/- y direction
    fragColor += GetFragValAt(uv + vec2( 0.0, offset ));
    fragColor += GetFragValAt(uv + vec2( 0.0, -offset));
    
    // samples at diagonals
    fragColor += GetFragValAt(uv + vec2(offset, offset));
    fragColor += GetFragValAt(uv + vec2(-offset, offset));
    fragColor += GetFragValAt(uv + vec2(offset, -offset));
    fragColor += GetFragValAt(uv + vec2(-offset, -offset));
    #endif
    
    // compute average
    fragColor /= num_samples;

}
</code></pre>

CC BY-SA 4.0
2022-05-30T10:35:49.333Z
2022-05-30T10:35:49.333Z
pmw1234
<p>Probably the simplest way is to use <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-smoothstep" rel="nofollow noreferrer">smoothstep</a> to make a transition from white to black:</p>
<pre><code>float R = 0.5 / ScreenSize; // you'll have to pass or hardcode it
return smoothstep(Pos.x * Pos.x + Pos.y * Pos.y, 1.0 - R, 1.0 + R);
</code></pre>

CC BY-SA 4.0
2022-05-30T11:25:33.187Z
This is a great technique. I used something almost exactly like this on a low end SOC (commercial project) at low resolution (~720p) and the results were near perfect.
PaulHK
CC BY-SA 4.0
2022-05-30T14:30:35.447Z
@PaulHK Yep, in most cases when rendering simple geometry like lines & circles one can do manual AA without heavy MSAA or similar stuff.
lisyarus
CC BY-SA 4.0
2022-05-30T21:33:40.860Z
This works well because the implicit function is essentially the distance to the object. In general, you need to use the distance function or an approximation. See https://math.stackexchange.com/questions/59034/distance-estimation-to-an-implicit-function-graph
lhf
CC BY-SA 4.0
2022-05-31T08:40:53.633Z
@lhf Naturally. However, the OP asked specifically about a circle.
lisyarus
CC BY-SA 4.0
2022-05-28T22:36:34.373Z
2022-05-28T22:36:34.373Z
lisyarus
<p>Have a fragment shader:</p>
<pre><code>// simply set all pixel white if inside unit circle else transparent.

#pragma fragment frag
float4 frag(float4 Pos : SV_POSITION) : SV_TARGET
{
    return Pos.x * Pos.x + Pos.y * Pos.y &lt;= 1;
}
</code></pre>
<p>How to apply Anti-Aliasing?
from <a href="https://i.sstatic.net/bLqSF.png" rel="nofollow noreferrer">this</a> to <a href="https://i.sstatic.net/fMt9y.png" rel="nofollow noreferrer">this</a>.</p>

CC BY-SA 4.0
2022-05-29T19:48:20.170Z
This might sound like it is a specific question, but it is actually extremely broad. Do you want a solution specific to this problem? Or something more general? In which case there are many choices. FSAA, Morphological, MSAA, TXAA, this list goes on and on.
pmw1234
CC BY-SA 4.0
2022-05-30T03:13:46.520Z
@pmw1234 All Anti-Alias articles I've found so far aren't beginner friendly. So I want to start from simplest technique and build upon it. I want a solution specific to this problem.
Hello Humans
<antialiasing><fragment-shader>
Anti-Aliasing simple circle fragment shader
Hello Humans
# Anti-Aliasing simple circle fragment shader

(Asked by: username_0 on 2022-05-28)

Have a fragment shader:

```
// simply set all pixel white if inside unit circle else transparent.

#pragma fragment frag
float4 frag(float4 Pos : SV_POSITION) : SV_TARGET
{
    return Pos.x * Pos.x + Pos.y * Pos.y <= 1;
}

```

How to apply Anti-Aliasing?
from [this](https://i.sstatic.net/bLqSF.png) to [this](https://i.sstatic.net/fMt9y.png).

**Tags:** antialiasing, fragment-shader

> **Comments:**
> **username_1**: This might sound like it is a specific question, but it is actually extremely broad. Do you want a solution specific to this problem? Or something more general? In which case there are many choices. FSAA, Morphological, MSAA, TXAA, this list goes on and on.  
> **username_0**: @username_1 All Anti-Alias articles I've found so far aren't beginner friendly. So I want to start from simplest technique and build upon it. I want a solution specific to this problem.  

---

## 2 answers

### Answer by username_1 on 2022-05-30. Score: 1 | Accepted answer

The "big idea" around most anti aliasing algorithms is taking multiple samples in the region around the fragment in question. These can range from very simple above/below to random samples to complex heuristics.

Another issue is the distance that those samples are taken from the original fragment. Here again it can range from simple fixed distance to "smart" heuristics.

Last but not least is weighting the samples taken. And as you can guess this ranges from a simple average, to weights that compute the distance to other fragments and many other schemes. (weights are like CPU TLB algorithms, everyone has a better one, but we always use the generic version because its good enough)

There is also the issue of diminishing returns where taking more samples doesn't really help the final output.

I wrote a shader toy that demonstrates basic antialiasing using an ellipse (partly because I was to lazy to adjust for aspect ratio) and is very similar to the sample you submitted with your question. Also, I tried to make it easy to change the sampling and turn sampling on/off.

The shader toy can be found [here](https://www.shadertoy.com/view/Ns3yzs).

Here is the code of the shader toy:

```
 // Comment this out to turn of sampling 
#define do_samples

// Average the samples together
float num_samples =0.0;

vec4 GetFragValAt( vec2 uv )
{
    num_samples += 1.0;
    
    // dot is same as uv.x*uv.x+uv.y*uv.y
    return vec4( dot( uv, uv) <= 0.4);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
 
    // Normalized pixel coordinates (from -1 to 1)
    vec2 uv = fragCoord/iResolution.xy*2.0-1.0;
    
    // Change offset to adjust sampling distance
    float offset = 0.002;
    
    fragColor = GetFragValAt( uv );
    
    #ifdef do_samples
    // samples in +/- x direction
    fragColor += GetFragValAt(uv + vec2( offset, 0.0));
    fragColor += GetFragValAt(uv + vec2( -offset, 0.0));
    
    // samples in +/- y direction
    fragColor += GetFragValAt(uv + vec2( 0.0, offset ));
    fragColor += GetFragValAt(uv + vec2( 0.0, -offset));
    
    // samples at diagonals
    fragColor += GetFragValAt(uv + vec2(offset, offset));
    fragColor += GetFragValAt(uv + vec2(-offset, offset));
    fragColor += GetFragValAt(uv + vec2(offset, -offset));
    fragColor += GetFragValAt(uv + vec2(-offset, -offset));
    #endif
    
    // compute average
    fragColor /= num_samples;

}

```


---

### Answer by username_2 on 2022-05-28. Score: 3

Probably the simplest way is to use [smoothstep](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-smoothstep) to make a transition from white to black:

```
float R = 0.5 / ScreenSize; // you'll have to pass or hardcode it
return smoothstep(Pos.x * Pos.x + Pos.y * Pos.y, 1.0 - R, 1.0 + R);

```

> **Comments:**
> **username_3**: This is a great technique. I used something almost exactly like this on a low end SOC (commercial project) at low resolution (~720p) and the results were near perfect.  
> **username_2**: @username_3 Yep, in most cases when rendering simple geometry like lines & circles one can do manual AA without heavy MSAA or similar stuff.  
> **username_4**: This works well because the implicit function is essentially the distance to the object. In general, you need to use the distance function or an approximation. See https://math.stackexchange.com/questions/59034/distance-estimation-to-an-implicit-function-graph  
> **username_2**: @username_4 Naturally. However, the OP asked specifically about a circle.  

---

