4

How do I draw a filled circle with openGl on iPhone ?

I've found many solutions but none of them work. Probably because there are many ways to do it. But what's the method with shortest code ?

durandal
  • 95
  • 1
  • 1
  • 5
aneuryzm
  • 63,052
  • 100
  • 273
  • 488
  • "none of them work" Oh? How so? – genpfault Jul 12 '12 at 13:36
  • It would be intersting to hear about the solutions you found and what you have tried so far. If none of them works, I suspect that you have some bugs in your implementations! – kroneml Jul 13 '12 at 08:12

4 Answers4

17

For a truly smooth circle, you're going to want a custom fragment shader. For example, the following vertex shader:

 attribute vec4 position;
 attribute vec4 inputTextureCoordinate;

 varying vec2 textureCoordinate;

 void main()
 {
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate.xy;
 }

and fragment shader:

 varying highp vec2 textureCoordinate;

 const highp vec2 center = vec2(0.5, 0.5);
 const highp float radius = 0.5;

 void main()
 {
     highp float distanceFromCenter = distance(center, textureCoordinate);
     lowp float checkForPresenceWithinCircle = step(distanceFromCenter, radius);

     gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * checkForPresenceWithinCircle;     
 }

will draw a smooth red circle within a square that you draw to the screen. You'll need to supply vertices for your square to the position attribute and coordinates that range from 0.0 to 1.0 in X and Y to the inputTextureCoordinate attribute, but this will draw a circle that's as sharp as your viewport's resolution allows and do so very quickly.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • I also think this is the best solution. Note that you can either draw a square using a (triangle) billboard or just a point with the correct point size. The second solution has the advantage that you only have to transfer the point position and the radius (i.e. one `vec4`). You also don't have to worry about your square facing the camera in case you have a 3D scene. – kroneml Jul 13 '12 at 08:10
  • But how to avoid the black part of the square being drawn? E.g, if you want to draw several such spheres on the screen, and they overlap, then the "black" part of the square will overwrite the other spheres, like in this picture: http://imgur.com/e7Z4ugb – OMH Mar 13 '14 at 20:50
  • 1
    @OMH - That portion is drawn transparent in the above code, and you'll then need to enable blending to make sure that you don't just get opaque black on the edges of the sphere. For overlapping spheres, you'll also need to do a depth rendering pre-pass to generate a depth lookup texture to determine when to reject fragments that are being blocked by other spheres merging into this one. I describe the process here: http://www.sunsetlakesoftware.com/2011/05/08/enhancing-molecules-using-opengl-es-20 and here: http://stackoverflow.com/q/6051237/19679 – Brad Larson Mar 13 '14 at 20:54
  • 2
    For a smoother circle, replace the line of: 'lowp float checkForPresenceWithinCircle = step(distanceFromCenter, radius);' with 'highp float checkForPresenceWithinCircle = 1.0 - smoothstep(radius-0.05, radius+0.05, distanceFromCenter);'. This change creates a quick and simple antialiasing effect. – Andy Shiue Nov 27 '15 at 22:26
2

One way would be to use GL_POINTS:

glPointSize(radius);
glBegin(GL_POINTS); 
glVertex2f(x,y); 
glEnd(); 

Another alternative would be to use GL_TRIANGLE_FAN:

radius = 1.0;
glBegin(GL_TRIANGLE_FAN);
glVertex2f(x, y);
for(int angle = 1; angle <= 360; angle = angle + 1)
glVertex2f(x + sind(angle) * radius, y + cosd(angle) * radius);
glEnd();
StuGrey
  • 1,479
  • 9
  • 20
  • Thanks, I get "implicit declaration of function glBegin is invalid in c99" warning. What does it mean ? – aneuryzm Jul 14 '12 at 09:17
  • We would really need to see your code to see where you are going wrong. – StuGrey Jul 14 '12 at 14:59
  • Ios does not seem to support the glBegin call. Remember this is ES2, and they have eliminated some of the functions. There does not seem to be a glEnd function either. –  Dec 03 '12 at 20:45
1

To get the verices of a circle:

     float[] verts=MakeCircle2d(1,100,0,0)

     public static float[] MakeCircle2d(float rad,int points,float x,float y)//x,y  ofsets
     {
            float[] verts=new float[points*2+2];
            boolean first=true;
            float fx=0;
            float fy=0;
            int c=0;
            for (int i = 0; i < points; i++)
            {
                    float fi = 2*Trig.PI*i/points;
                    float xa = rad*Trig.sin(fi + Trig.PI)+x ;
                    float ya = rad*Trig.cos(fi + Trig.PI)+y ;
                    if(first)
                    {
                        first=false;
                        fx=xa;
                        fy=ya;
                    }
                    verts[c]=xa;
                    verts[c+1]=ya;
                    c+=2;
            }
            verts[c]=fx;
            verts[c+1]=fy;
            return verts;
      }

Draw it as GL10.GL_LINES if you want a empty circle

 gl.glDrawArrays(GL10.GL_LINES, 0, verts.length / 2);

Or draw it as GL10.GL_TRIANGLE_FAN if you want a filled one

 gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, verts.length / 2);

Its java but it is really easy to convert to c++/objc

SteveL
  • 3,331
  • 4
  • 32
  • 57
0

Here is a super fast way using shaders... Just make a Quad with a vertex buffer and set the UV's from -1 to 1 for each corner of the quad.

The vertex buffer in floats should look like: NOTE: this needs a index buffer too.

var verts = new float[20]
{
    -1, -1, 0,   -1, -1,
    -1, 1, 0,   -1, 1,
    1, 1, 0,   1, 1,
    1, -1, 0,   1, -1,
};


#VS
attribute vec3 Position0;
attribute vec2 Texcoord0;

varying vec4 Position_VSPS;
varying vec2 Texcoord_VSPS;

uniform vec2 Location;

void main()
{
    vec3 loc = vec3(Position0.xy + Location, Position0.z);
    gl_Position = Position_VSPS = vec4(loc, 1);
    Texcoord_VSPS = loc.xy;
}
#END

#PS
varying vec4 Position_VSPS;
varying vec2 Texcoord_VSPS;

uniform vec2 Location;

void main()
{
    float dis = distance(Location, Texcoord_VSPS);
    if (.1 - dis < 0.0) discard;

    gl_FragData[0] = vec4(0, 0, 1, 1);
}
#END
zezba9000
  • 3,247
  • 1
  • 29
  • 51