SDL2如何实现不清空渲染器的持续绘制?
SDL2 持续绘制参数方程图形的问题解决
问题描述
我正在尝试用SDL2编写一个绘制参数方程的程序,希望参数方程生成的所有点都能保留在屏幕上。当前代码运行后显示异常,绘制出故障的像素化圆圈。以下是完整代码:
#include <iostream> #include <SDL2/SDL.h> #define WIDTH 600 #define HEIGHT 600 // 参数方程 - 在屏幕中心绘制半径200的圆 int parametric(SDL_Renderer* renderer, const float t) { float x = WIDTH / 2.0 + 200 * SDL_cos(t); float y = HEIGHT / 2.0 + 200 * SDL_sin(t); return SDL_RenderDrawPoint(renderer, (int)x, (int)y); } int main(int argc, char ** argv) { bool quit = false; SDL_Event event; float t = 0; SDL_Init(SDL_INIT_VIDEO); SDL_Window * window = SDL_CreateWindow("SDL2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_METAL); SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 0, 100, 255, 255); t = 0; while (!quit) { SDL_PollEvent(&event); switch (event.type) { case SDL_QUIT: quit = true; break; default: ; } parametric(renderer, t); SDL_RenderPresent(renderer); t += 0.02; } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
问题原因
SDL默认使用双缓冲渲染:每次调用SDL_RenderPresent会交换前后两个缓冲区。你只在初始化时清空了一次屏幕,后续循环中仅在当前缓冲区绘制单个点,交换后新的前台缓冲区是空白的,导致之前绘制的点丢失,出现闪烁、像素化的异常。
解决方案
方法1:使用离屏纹理保存绘制内容(推荐)
创建一个和窗口尺寸相同的纹理作为持久化缓冲区,所有绘制操作都先画到这个纹理上,再将纹理整体渲染到窗口。这样纹理会保留所有已绘制的点,不会因为缓冲区交换丢失。
修改后的代码如下:
#include <iostream> #include <SDL2/SDL.h> #define WIDTH 600 #define HEIGHT 600 // 参数方程 - 在屏幕中心绘制半径200的圆 int parametric(SDL_Renderer* renderer, const float t) { float x = WIDTH / 2.0 + 200 * SDL_cos(t); float y = HEIGHT / 2.0 + 200 * SDL_sin(t); return SDL_RenderDrawPoint(renderer, (int)x, (int)y); } int main(int argc, char ** argv) { bool quit = false; SDL_Event event; float t = 0; SDL_Init(SDL_INIT_VIDEO); SDL_Window * window = SDL_CreateWindow("SDL2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_METAL); SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // 创建离屏纹理作为持久化缓冲区 SDL_Texture* canvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, WIDTH, HEIGHT); // 初始化纹理背景为黑色 SDL_SetRenderTarget(renderer, canvas); SDL_RenderClear(renderer); SDL_SetRenderTarget(renderer, nullptr); // 恢复渲染目标到窗口 SDL_SetRenderDrawColor(renderer, 0, 100, 255, 255); t = 0; while (!quit) { SDL_PollEvent(&event); switch (event.type) { case SDL_QUIT: quit = true; break; default: ; } // 将渲染目标切换到离屏纹理 SDL_SetRenderTarget(renderer, canvas); parametric(renderer, t); // 在纹理上绘制新点 SDL_SetRenderTarget(renderer, nullptr); // 切回窗口渲染 // 清空窗口并将纹理渲染到窗口 SDL_RenderClear(renderer); SDL_RenderCopy(renderer, canvas, nullptr, nullptr); SDL_RenderPresent(renderer); t += 0.02; // 可选:限制帧率,避免绘制过快 SDL_Delay(10); } SDL_DestroyTexture(canvas); // 销毁纹理 SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
方法2:每次循环重绘所有点(简单但效率低)
如果不想使用纹理,可以将所有已生成的点存储在容器中,每次循环时先清空窗口,再重绘所有已保存的点,最后绘制新点。这种方法适合简单场景,点数量较多时会明显卡顿。
修改后的代码示例:
#include <iostream> #include <SDL2/SDL.h> #include <vector> #define WIDTH 600 #define HEIGHT 600 // 存储点的结构体 struct Point { int x; int y; }; // 参数方程 - 生成点坐标 Point parametric(const float t) { float x = WIDTH / 2.0 + 200 * SDL_cos(t); float y = HEIGHT / 2.0 + 200 * SDL_sin(t); return {(int)x, (int)y}; } int main(int argc, char ** argv) { bool quit = false; SDL_Event event; float t = 0; std::vector<Point> points; // 存储所有已生成的点 SDL_Init(SDL_INIT_VIDEO); SDL_Window * window = SDL_CreateWindow("SDL2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_METAL); SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(renderer, 0, 100, 255, 255); t = 0; while (!quit) { SDL_PollEvent(&event); switch (event.type) { case SDL_QUIT: quit = true; break; default: ; } // 生成新点并加入容器 points.push_back(parametric(t)); // 清空窗口 SDL_RenderClear(renderer); // 重绘所有已保存的点 for (const auto& p : points) { SDL_RenderDrawPoint(renderer, p.x, p.y); } SDL_RenderPresent(renderer); t += 0.02; SDL_Delay(10); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
内容的提问来源于stack exchange,提问作者ZeroTerabytes
相关产品推荐
相关产品推荐

