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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:44:52