6
\$\begingroup\$

I'm looking for creating smooth circle. OpenGL supports point, line, and triangle. To create other primitives like circle, we utilize the preceding shapes. In my case, I've utilized points as follows:

float radius(0.5f);
for ( float angle(0); angle < glm::radians(360.0f); angle += glm::radians(0.5f) ){
    Vertex vertices[] = { Vertex(glm::vec3( radius*cos(angle), radius*sin(angle), 0)) };
    meshes.push_back(   new Mesh(vertices, sizeof(vertices)/sizeof(vertices[0]), 'P')   );
}

In rendering loop,

while( Window.isOpen() ){
    Window.PollEvents();
    Window.clear();
    //================( Rendering )=========================
    Shader.Use();

    for ( int i(0); i < meshes.size(); ++i )
        meshes[i]->draw();

    //======================================================
    Window.SwapBuffers();
}

The result is

enter image description here

Now to create smooth circle, basically I just increase the number of points. I don't like this approach since I need to create a lot of points for single shape. My question is is there an alternative yet efficient approach for this issue?

\$\endgroup\$
6
  • 2
    \$\begingroup\$ An Efficient Way to Draw Approximate Circles in OpenGL \$\endgroup\$
    – Theraot
    Commented May 18, 2017 at 11:51
  • \$\begingroup\$ I'm pretty sure no one is going to measurebthe circle you draw them. Also, use the same model for each. \$\endgroup\$
    – Bálint
    Commented May 18, 2017 at 12:03
  • \$\begingroup\$ @Theraot, they use old OpenGL glBegin(). I'm using shaders. \$\endgroup\$
    – CroCo
    Commented May 18, 2017 at 12:21
  • 1
    \$\begingroup\$ @CroCo I'm aware. Try to see the algorithm beyond the primitives. Edit: glVertex2f would be adding a point to the mesh. C'mon you can do it! \$\endgroup\$
    – Theraot
    Commented May 18, 2017 at 12:23
  • \$\begingroup\$ Render to a texture and draw that. \$\endgroup\$
    – House
    Commented May 18, 2017 at 12:31

2 Answers 2

11
\$\begingroup\$

Here's one neat trick you can use to draw nice antialiased curves.

  1. Take a mesh (eg. a quad, or to reduce unnecessary overdraw far from the line, you can use an annulus-shaped mesh that approximates the circle more closely.

  2. Ensure the mesh's texture coordinates run from -1 at one edge of the circle to +1 at the other edge, to keep our math simple. Ensure there's a little more mesh past the -1...1 range so our circle doesn't run right into the hard polygon edge.

    Example of meshes enclosing the circle we want to draw.

  3. Use a fragment shader something a bit like this:

(I'm most familiar with HLSL syntax, so please let me know if I've made any errors translating this to GLSL)

// Compute this fragment's distance from the center of the circle in uv space.
float radius = length(textureCoordinate);

// Convert to a signed distance from the outline we want to draw.
// +ve: outside the disc, -ve: inside the disc.
float signedDistance = radius - 1.0f;

// Use screenspace derivatives to compute how quickly this value changes
// as we move one pixel left or right / up or down:
vec2 gradient = vec2(dFdx(signedDistance), dFdy(signedDistance));

// Compute (approximately) how far we are from the line in pixel coordinates.
float rangeFromLine = abs(signedDistance/length(gradient));

// Combine with our desired line thickness (in pixels) to get an opacity.
float lineWeight = clamp(THICKNESS - rangeFromLine, 0.0f, 1.0f);

return vec4(COLOR.rgb, lineWeight);

While just drawing a dense polygon with straight-line segments as suggested above by Theraot is likely to be more efficient, this shading technique is convenient because it automatically adapts to the scale of the circle / zoom of the camera to show a smooth curve, without needing to add more points as the camera gets closer. It also gives partial opacity along the edge of the line for a smooth antialiased look. It won't look good at extreme glancing angles though, since at the end of the day it's a flat sheet of geometry and has no thickness when viewed edge-on - you'd want to switch to a different shape if viewing the circle this way.

\$\endgroup\$
5
\$\begingroup\$

There's always the pure shader rendering thechnique if you really want to make it faster, but it only helps if you draw hundreds of circles, and they overlap.

Basically, you draw a rectangle that covers the whole screen, then you load each circle in the fragment shader including the radiuses. You look through each circle and see if the current fragment position is inside a circle with the formula

(x - centerX)^2 + (y - centerY)^2 < radius^2

(You should load in the squared radius to the fragment shader).

If it's inside the circle, check if the current circle is the closest one to the point, if it is, then fill fragment in with the circle's color.

I'm not saying you should use it, but if you need to draw lot of circles, it could help

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .