How do I get this circle to maintain its thickness when viewed from different angles?

I have drawn a circle in a fragment shader. I have been able to keep the thickness when viewed at different distances with: border *= -mvPos.z;
But if viewed from an angle it gets thinner.

Is this a good way to draw a circle? (with glsl) or is their a better way? I plan on having multiple circles inside each other that change with distance.

here is the fragment shader code:

varying vec3 vPosition;
varying vec4 mvPos;

void main() {
    float circle_radius = 5.0;
    float border = 0.0025;
    vec2 uv = vPosition.xz;
    float dist = sqrt(dot(uv, uv));

    border *= -mvPos.z;
    float t = 1.0 + smoothstep(circle_radius, circle_radius + border, dist) 
                  - smoothstep(circle_radius - border, circle_radius, dist);
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0 - t);

There’s nothing that can be done in your situation, because you’re trying to maintain the circle thickness on a plane. So when that plane inevitably gets infinitely thin when you rotate it by 90-degrees, then there’s nothing you can do to make the plane (and therefore, circle) thicker.

Think of it as using a big fat brush on a piece of paper, then looking at the paper from the edge. It doesn’t matter how fat the brush is, the stroke is still going to be flat.

You’ll need to use actual torus 3D geometry to maintain the thickness when viewed from the edge.

1 Like

In dependence of your goal, you can use a torus, or a line, or a fat line.

I might go with a line, but I would rather the perfect circle look of a shader generated circle.
There has been some progress, a suggestion from someone has led me to this:

Thank you. If I do it this way, the plane will be very large and OrbitControls will have a maxPolarAngle to keep the view of the plane wide enough. If this route is unsuccessful then I will use a line, which is about the thickness i am looking for.

Just out of curiousity, what’s the goal?

The circles are to show a distance from a center point. when the camera is close the circle would have a smaller radius and when the camera is far the radius would be larger. the different radius would be somehitng like 1metre, 5 meter 10 meters etc. I can do this with a BufferGeometry in a circle and LineMaterial, but I want to try it with a shader, to get a better circle and maybe better performance.
What do you think?

Fwidth is what I would use for this to get pixel perfect lines, as well, but I pass it in as a threshold to smoothstep, which gives a pretty nice anti aliased edge but you’ll start to notice artifacts at really skewed angles particularly with thicker lines. And of course it’ll disappear if you look at the plane from the side anyway:

    // calculate the distance from center
    float dist = length(uv);    
    float distToEdge = abs(dist - circle_radius);

    float pixelWidth = fwidth(dist);
    float t = smoothstep(pixelWidth * (border - 1.0), pixelWidth * (border + 0.25), distToEdge) ;

Here’s a fiddle with the change:

You can adjust the border - 1.0 and border + 0.25 thresholds to taste depending on how much of a feathered / AA’d edge you want. The artifacts at skewed angles are gonna bug me, though…


Nice. This works really well.
Thank you!