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); }
问题原因分析
兄弟,你的代码里有几个明显的逻辑问题,直接导致了间隙和闪烁:
重复绘制+未清屏:你在交换缓冲前后各画了一次完全相同的线段,而且全程没在每帧开头清屏。这就导致前一帧的画面和当前帧的内容叠加,加上交换缓冲的时序问题,画面肯定会乱跳、闪烁。
缓冲交换时序错误:
glfwSwapBuffers是把后台缓冲的内容显示到前台。你现在交换前画一次到后台,交换后又画一次(这时候后台缓冲其实是上一帧的前台内容),等于把新线段画到了旧画面上,叠加起来就会出现间隙和错乱。垂直同步设为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; }
关键修改点总结
- 每帧开头用
glClear(GL_COLOR_BUFFER_BIT)清屏,避免画面叠加; - 把所有绘制逻辑放在
glfwSwapBuffers之前,每帧只画一次,交换缓冲后处理事件; - 把
glfwSwapInterval(2)改为1,同步显示器刷新率; - 窗口大小更新逻辑移到循环内,适配窗口缩放。
这样修改后,动画应该就流畅无间隙了~
内容来源于stack exchange
相关产品推荐
相关产品推荐

