Draw a Circle Using Opengl Java



An Efficient Way to Draw Approximate Circles in OpenGL


Introduction

OpenGL is well known to not posses whatever method to rasterize non-straight curves. As such, whenever i is required to draw a curve, one has to either rasterize it himself (through GL_POINTS) which is slow, or approximate it using a number of line segments by drawing a many sided regular polygon. The latter method shall be explored here for drawing circles.

Information technology is a well known fact that a regular polygon with a large number of sides looks approximately similar a circumvolve when rasterized on the screen. As such, when given a challenge to depict a circle in OpenGL, people mostly come upwards with something like this:


        void        DrawCircle(        float        cx,                  float        cy,                  bladder        r,                  int        num_segments)  {        glBegin(GL_LINE_LOOP);                  for        (        int        ii          =                  0        ;        two          <        num_segments;        ii++)  	{                  float        theta          =                  2.0f                  *                  3.1415926f                  *                  float        (ii) /                  float        (num_segments);                  float        x          =        r          *        cosf(theta);                  bladder        y          =        r          *        sinf(theta);        glVertex2f(ten          +        cx,        y          +        cy);                  }        glEnd();  }      

There are some uncomplicated optimizations that ane can do, but all the same the full general algorithm is as stated. This is very bad algorithm however, since for every point we have to perform expensive calls to the trigonometric functions. I propose a method that requires only the basic arithmetic operations for every signal to obtain the same outcome.

The Algorithm

The inspiration came to me from physics. Equally nosotros know, when an object moves in a circle it does and so entirely because of a constant centripetal acceleration interim on it. When this is the example the object moves at a constant radial speed. Note that that ways that we have two constant length terms (radial speed and centripetal dispatch) which only change direction, but not length. Since direction is easy to compute, information technology is just a vector, we are in business.

Here is the general motion of the point whose position we apply to calculate the position of the vertices of our circumvolve. During each calculation, we move the betoken tangentially to the circle for a fixed distance (segments AK and BL) and then dorsum towards the middle of the circumvolve for a stock-still distance (segments KB and LC). At this signal we store the location of our bespeak (perhaps through a call to glVertex) and and so repeat the procedure. Our problem, thus, is to calculate the magnitude of the tangential motion, the direction of the tangential motility, the magnitude of the radial motion and the direction of the radial motion.

We are helped in this task past the following vectors that we know. At the beginning of our adding we know the vector AO. We can hands turn this vector ninety degrees so it becomes tangent to the circle by flipping the coordinates, while negating ane of them. Now, we take a vector that is tangent to the circle, just still the length of the radius. To brand be the length of AK, we multiply it by a factor, which is merely the tangent of the theta. This factor is the same for every calculation, so it can be precalculated before inbound the loop.

Now, we add the tangential vector to the vector AO and go the vector KO. We need to get the vector BO from that. Once again, we tin simply multiply information technology by a cistron which turns out to be the cosine of theta. Again, this gene is abiding and can exist precalculated. At this point we are back where we started, so nosotros can only echo this process over again, and go along to practise then until we make full the entire circle. Thus, here is the code for this implementation:


        void        DrawCircle(        bladder        cx,                  bladder        cy,                  float        r,                  int        num_segments)  {                  float        theta          =                  2                  *                  three.1415926                  /                  float        (num_segments);                  bladder        tangetial_factor          =        tanf(theta);                  float        radial_factor          =        cosf(theta);                  float        x          =        r;                  float        y          =                  0        ;        glBegin(GL_LINE_LOOP);                  for        (        int        ii          =                  0        ;        two          <        num_segments;        two++)  	{        glVertex2f(10          +        cx,        y          +        cy);                  float        tx          = -y;                  float        ty          =        x;        x          +=        tx          *        tangetial_factor;        y          +=        ty          *        tangetial_factor;        x          *=        radial_factor;        y          *=        radial_factor;  	}        glEnd();  }      

The algorithm tin exist modified to describe arcs instead of circles. We get-go calculate the starting indicate from the starting bending. Then we summate the theta parameter based not on the full circle as earlier, but rather the angular span of the arc.
Hither is one fashion to do it:
        void        DrawArc(        float        cx,                  float        cy,                  float        r,                  float        start_angle,                  float        arc_angle,                  int        num_segments)  {                  float        theta          =        arc_angle          /                  float        (num_segments          -                  1        );                  bladder        tangetial_factor          =        tanf(theta);                  float        radial_factor          =        cosf(theta);                  float        ten          =        r          *        cosf(start_angle);                  float        y          =        r          *        sinf(start_angle);        glBegin(GL_LINE_STRIP);                  for        (        int        2          =                  0        ;        ii          <        num_segments;        ii++) 	{        glVertex2f(x          +        cx,        y          +        cy);                  float        tx          = -y;                  float        ty          =        ten;        x          +=        tx          *        tangetial_factor;        y          +=        ty          *        tangetial_factor;        ten          *=        radial_factor;        y          *=        radial_factor;  	}        glEnd();  }      

Other Considerations

Both this and the other algorithms can be greately sped upwards past using a vertex array, that is then passed as an argument to the glDrawArrays, I get out information technology upward to the reader to implement this piddling optimisation.

You lot will note that to draw the circle you need to pass the number of segments to draw. Is it possible to create a function to give a good estimate of that number so that you get a reasonably smooth circle? Yep there is. One manner to practice this is to limit to the magnitude of the KB segment in the diagram above. The length of that segment is easily derived to be:


        (        1                  -        cos(theta)) *        r

Nosotros set up that to some small value and solve for theta. I discover that setting it to 0.25 (i.due east. a quarter of a pixel) generally produces adequate results. You lot can vary that number as advisable. The actual formula that you become involves inverse trigonometric functions, but I found that the following approximation matches their prediction quite well:


        int        GetNumCircleSegments(        float        r)  {                  return                  ten                  *        sqrtf(r);        }      

Lastly, this code can be adapted to draw ellipses as well. Simply scale the x and y variables by appropriate factors, either using a matrix, or perhaps by modifying the algorithm itself. This extension is trivial.

Decision

This algorithm is significantly faster than any other circle approximation algorithm I take personally encountered on the web. I believe that if 1 chooses to draw a circle using OpenGL and using a polygonal approximation and then my method is optimal. In theory, with a large number of segments the true circle, i.eastward. compatible with the diamond-leave specification, can be drawn, although at that indicate it may be preferable to rasterize the circle using GL_POINTS manually.

Recasting the Algorithm every bit a Repeated Rotation

Someone suggested to me that this algorithm tin be recast equally a simple repeated rotation. That is indeed correct. All you accept to practise is multiply the whole algorithm by a few trig functions, and obtain something that just looks similar the repeated awarding of the rotation matrix. I leave information technology as the excersize to the reader to meet how yous can transform the to a higher place algorithm into this one:

        void        DrawCircle(        float        cx,                  bladder        cy,                  float        r,                  int        num_segments)  {                  float        theta          =                  2                  *                  3.1415926                  /                  float        (num_segments);                  bladder        c          =        cosf(theta);                  float        s          =        sinf(theta);                  float        t;                  float        x          =        r;                  float        y          =                  0        ;        glBegin(GL_LINE_LOOP);                  for        (        int        ii          =                  0        ;        2          <        num_segments;        ii++)  	{        glVertex2f(ten          +        cx,        y          +        cy);        t          =        x;        x          =        c          *        10          -        s          *        y;        y          =        s          *        t          +        c          *        y; 	}        glEnd();  }      

The advantages of this version of the algorithm is that it is easier to understand (repeated rotation instead of the physics inspired mumbo jumbo) and at a glance it seems to be a petty more numerically stable. I use this version in my current projects, but in principle its your selection which ane you lot like more.

This lawmaking is released as public domain, just I volition appreciate an email if you do utilise it somewhere. Experience gratuitous to electronic mail me for clarifications of the algorithm and to report bugs and whatnot.




Original content Copyright SiegeLord'southward Abode 2006-2017. All rights reserved.   ♫

web analytics

crosstheighty.blogspot.com

Source: http://slabode.exofire.net/circle_draw.shtml

0 Response to "Draw a Circle Using Opengl Java"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel