You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GLFW半圈圆形动画存在间隙与闪烁问题求助

GLFW半圈圆形动画存在间隙与闪烁问题求助

我写了一段GLFW代码,本来想实现粒子匀速运动、逐步画出顺时针半圈的动画效果,但运行起来总是出问题——最终显示的半圈总有间隙,还见过整个窗口闪烁、画面错乱的情况。不过如果改成一次性把整个半圈画完再交换帧缓冲,效果就完全正常。

现在我的代码逻辑是每次迭代添加一段线段,交换缓冲,然后又重复添加一次相同的线段,实在搞不懂哪里错了,麻烦帮忙看看!

我的原始代码

// gcc simple6.c -o simple6 -lglfw -lGL -lm
// Fedora Linux 42 (Workstation Edition), graphics NV137, Wayland
#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

static void error_callback(int error, const char* description) {
    fputs(description, stderr);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

int main(void) {
    float t=0, h = .05, ratio;
    int width, height;
    GLFWwindow* window;

    glfwSetErrorCallback(error_callback);
    if (!glfwInit())
        exit(EXIT_FAILURE);

    window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
    if (!window) {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);

    glfwGetFramebufferSize(window, &width, &height);
    ratio = width / (float) height;
    glViewport(0, 0, width, height);
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
    glLineWidth(1);

    glfwSwapInterval(2);

    while (!glfwWindowShouldClose(window)) {
        if (t < 6.28/2) {
            glBegin(GL_LINES);
            glVertex3f(sin(t), cos(t), .0);
            glVertex3f(sin(t+h), cos(t+h), .0);
            glEnd();
            glFlush();
            glfwSwapBuffers(window);

            glBegin(GL_LINES);
            glVertex3f(sin(t), cos(t), .0);
            glVertex3f(sin(t+h), cos(t+h), .0);
            glEnd();
            glFlush();
            t += h;
        }
        glfwPollEvents();
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

问题原因分析

兄弟,你的代码里有几个明显的逻辑问题,直接导致了间隙和闪烁:

  1. 重复绘制+未清屏:你在交换缓冲前后各画了一次完全相同的线段,而且全程没在每帧开头清屏。这就导致前一帧的画面和当前帧的内容叠加,加上交换缓冲的时序问题,画面肯定会乱跳、闪烁。

  2. 缓冲交换时序错误:glfwSwapBuffers是把后台缓冲的内容显示到前台。你现在交换前画一次到后台,交换后又画一次(这时候后台缓冲其实是上一帧的前台内容),等于把新线段画到了旧画面上,叠加起来就会出现间隙和错乱。

  3. 垂直同步设为2帧:glfwSwapInterval(2)会让画面每2帧才更新一次,延迟拉满,闪烁感会更严重,这种动画场景设为1就够了。

修复后的代码(两种效果可选)

效果1:每次只显示当前的线段(粒子运动的轨迹段)

// gcc simple6.c -o simple6 -lglfw -lGL -lm
// Fedora Linux 42 (Workstation Edition), graphics NV137, Wayland
#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

static void error_callback(int error, const char* description) {
    fputs(description, stderr);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

int main(void) {
    float t=0, h = .05, ratio;
    int width, height;
    GLFWwindow* window;

    glfwSetErrorCallback(error_callback);
    if (!glfwInit())
        exit(EXIT_FAILURE);

    window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
    if (!window) {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);

    glfwSwapInterval(1); // 改为1帧垂直同步,减少延迟

    while (!glfwWindowShouldClose(window)) {
        // 每帧更新窗口大小和投影矩阵,适配窗口缩放
        glfwGetFramebufferSize(window, &width, &height);
        ratio = width / (float) height;
        glViewport(0, 0, width, height);

        // 每帧开头必须清屏,清除上一帧内容
        glClear(GL_COLOR_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
        glLineWidth(1);

        if (t < 6.28/2) {
            // 每帧只画一次当前的线段
            glBegin(GL_LINES);
            glVertex3f(sin(t), cos(t), .0);
            glVertex3f(sin(t+h), cos(t+h), .0);
            glEnd();
            glFlush();
            t += h;
        }

        // 先画完当前帧所有内容,再交换缓冲
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

效果2:逐步绘制出完整的半圈(保留所有轨迹)

如果你要的是从无到有逐步画出整个半圈,那得每帧重新绘制所有已经画过的线段(因为清屏后旧线段会消失),修改循环内的绘制部分即可:

if (t < 6.28/2) {
    glBegin(GL_LINES);
    // 从0到当前t,重新绘制所有线段
    for (float i=0; i < t; i += h) {
        glVertex3f(sin(i), cos(i), .0);
        glVertex3f(sin(i+h), cos(i+h), .0);
    }
    glEnd();
    glFlush();
    t += h;
}

关键修改点总结

  1. 每帧开头用glClear(GL_COLOR_BUFFER_BIT)清屏,避免画面叠加;
  2. 把所有绘制逻辑放在glfwSwapBuffers之前,每帧只画一次,交换缓冲后处理事件;
  3. 把glfwSwapInterval(2)改为1,同步显示器刷新率;
  4. 窗口大小更新逻辑移到循环内,适配窗口缩放。

这样修改后,动画应该就流畅无间隙了~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:38:07