如何使用 openGL 获得真正平滑的移动物体

发布于 2024-09-12 20:08:14 字数 2400 浏览 9 评论 0原文

我正在开发一个应用程序,在其中绘制一个慢慢“填充”的圆形进度指示器,在这里您可以看到 几张图片。我正在使用中间模式,并且仅通过在每个帧更新时绘制顶点来绘制此圆形形状(请参见下面的代码)。当圆圈填满白色和灰色之间的边界线时,它有点闪烁,我想知道如何使它更平滑。

这是一个具体的例子,但总的来说,我想知道即使使用非常简单的线性插值,如何获得最平滑的运动?当我看到在 iPhone 上翻页时的动作时,它是如此的极其流畅,这让我想知道如何在我的(快速)Mac 上实现这一点?

谢谢
罗克斯鲁

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;
}

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

獨角戲 2024-09-19 20:08:14

很难猜出你在问什么——闪烁、锯齿、平滑运动或绘图伪影。查看代码后的自由想法

  • 您假设如果floatpart_angle = TWO_PI/resolution;然后通过求和(angle +=part_angle)resolution< /code> 次您将获得 TWO_PI。不幸的是,这在浮点运算中并非如此。此外,sin(TWO_PI) cos(TWO_PI) 不一定会返回精确的 0.01.0 值。

  • 为了提高绘图精度,我会绘制完整的灰色圆圈,然后根据需要在其上绘制白色圆圈。这将使完整的灰色圆圈达到 100%

  • 这会在 100%浮点运算 可能会给您带来舍入错误,并且此错误可能会影响转换后的整数值。您可能永远无法通过像这样的表达式计算所有填充的部分 int parts_filled = resolution * perc;。您可以转换百分比倒计时,以便它将使用整数类型,或者至少通过打印百分比值来测试它并检查它是否达到 100%。
  • 另外你可以
    • 使用抗锯齿功能使边缘更平滑
    • 提高帧速率(这取决于您更新绘图的方式)
    • 打开垂直同步以消除页面撕裂

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
骑趴 2024-09-19 20:08:14

我要做的第一件事就是简单地绘制一个圆圈,并将其存储在显示列表中(您可以选择缓冲区对象,但本建议的其余部分都不依赖于此,并且您处于即时模式,所以我们将坚持最简单的改进)。画出分割的圆,在每个圆上放置与完成百分比相对应的纹理坐标。应用一个着色器,虽然很无聊,但为了完整性有一个统一的着色器;纹理坐标 <= 的三角形的完整性会被点亮。其余的保持灰色。然后,您的绘图包括设置制服和调用显示列表。快得多。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文