views:

157

answers:

3

Dear reader,

I'm working on an application where I draw a circular progress indicator which slowly gets "filled", here you can see a couple of images. I'm using intermediate mode and I'm only drawing this circular shape by drawing the vertices at each frame update (see code below). When the circle fills up the border line between the white and gray is a bit flickery and I'm wondering how I can make it more smooth.

This is a concrete example, but in general I'm wondering how to get the smoothest motion even when using very simple lineair interpolation? When I look at the motion when I flip between pages on my iphone it's just so extremelly smooth which makes me wonder how I would achieve this on my (fast) mac?

Thanks
Roxlu

PhotoBoothCountdown::PhotoBoothCountdown(float fMillisDuration)
    :duration(fMillisDuration)
    ,start_time(0)
{
    setActionCommand("take_picture");
}

void PhotoBoothCountdown::update() {
    if(start_time != 0) {
        float cur = ofGetElapsedTimeMillis();
        perc = (cur-start_time)/duration;

    }
}

void PhotoBoothCountdown::draw() {
    float resolution = 150;
    int parts_filled = resolution * perc;
    int parts_unfilled = (resolution - parts_filled);
    float part_angle = TWO_PI/resolution;
    //printf("filled: %i/%i/%i\n", parts_filled, parts_unfilled, (parts_filled+parts_unfilled));
    float radius = 300.0f;
    float width = 100;
    float radius_inner = radius-width;
    float center_x = ofGetWidth()/2;
    float center_y = ofGetHeight()/2;
    float angle = 0;

    glBegin(GL_TRIANGLE_STRIP);
        glColor4f(0.2f, 0.2f, 0.2f,0.9f);   
        for(int i = 0; i <= parts_filled; ++i) {
            float cosa = cos(angle);
            float sina = sin(angle);
            glVertex2f(center_x + cosa * radius,center_y + sina * radius);
            glVertex2f(center_x + cosa * radius_inner,center_y + sina * radius_inner);
            angle += part_angle;
        }
    glEnd();
    glBegin(GL_TRIANGLE_STRIP);
        glColor4f(1.0f, 1.0f, 1.0f,0.9f);
        angle -= part_angle;
        for(int i = 0; i <= parts_unfilled; ++i) {
            float cosa = cos(angle);
            float sina = sin(angle);
            glVertex2f(center_x + cosa * radius,center_y + sina * radius);
            glVertex2f(center_x + cosa * radius_inner,center_y + sina * radius_inner);
            angle += part_angle;
        }
    glEnd();

    if(perc >= 1) { 
        printf("should fire");
        fireEvent();
        reset();
    }
}



void PhotoBoothCountdown::start() {
    start_time = ofGetElapsedTimeMillis();
    end_time = start_time + duration;
}

void PhotoBoothCountdown::reset() {
    start_time = 0;
    end_time = 0;
    perc = 0;
}
+1  A: 

Take a look at Double buffering

Eric Fortin
OpenGL is double buffered by default
doc
@doc: OpenGL doesn't know much about single vs double buffers - that's the job of the windowing system interface like wgl or glx. This is why one will typically call `glutSwapBuffers`, for example, and not a mythical `glSwapBuffers`,
Carlos Scheidegger
@doc : no it's not, it depends on the way you initialize it. @Eric : +1
Calvin1602
A: 

It's hard to guess what you are asking about - a flickery, aliasing, smooth motion or drawing artifacts. Free thoughts after looking at your code

  • You assume that if float part_angle = TWO_PI/resolution; then by summing up (angle += part_angle) resolution times you'll get TWO_PI. Unfortunatelly this isn't true in floating point arithmetics. Also sin(TWO_PI) cos(TWO_PI) will not neccessarily return precise 0.0 and 1.0 values.

  • To improve drawing precission I would draw full grey circle and then a white one over it as neccessary. This would bring full grey circle at 100%

  • operations on floats may give you round errors and this errors may affect integral values after conversions. Possibly you may never get all the filled parts by calculating them from expression like this one int parts_filled = resolution * perc;. You can convert your percentage countdown so that it will use integral types or at least test it by printing out percent value and check out if it ever reaches 100%.
  • Additionaly you can
    • Use antialiasing to make edges smoother
    • Increase frame rate (it depends on how you update your drawing)
    • Turn on vertical sync to remove page tearing
doc
Hi doc, thanks for your reply. Maybe, the example I shown wasn't a good choice. Suppose I've just a GL_QUAD with a texture, and I want to rotate it smoothly, even when increasing the angle i.e. + 0.1 each update doesn't give me nice smooth lines/images. The vsync / increase framerate are things which are maybe important. Though I'm not sure if framerate does anything when setting vsync to true...
pollux
@pollux You have to be strict. Is it a problem with not too smooth (leaping) animation or not too smooth looks (jagged, distorted)?
doc
@doc the animation is not smooth
pollux
A: 

The first thing I would do would be to simply draw a single circle, and store it in a display list (you could go for buffer objects, but none of the rest of this advice depends on that, and you're in immediate mode, so we'll stick with the simplest improvement). Draw your divided up circle, putting texture coordinates on each one that corresponds to the percent completion there. Apply a shader which is mostly boring, but has a uniform for the completeness; triangles with their texture coordinates <= that completeness get lit up. The rest stay grey. Then your drawing consists of setting the uniform, and calling the display list. Much faster.

Jay Kominek