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

SDL2绘制曼德博集合时窗口无响应,已处理事件仍未解决

问题描述

尝试用SDL2可视化曼德博集合,编写了如下C++程序:

#include <numeric>
#include <iostream>
#include <complex>
#include <process.h>

double scale_factor = 1000;
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;

int is_in_mandelbrot_set(std::complex<double> c)
{
    std::complex<double> z(0, 0);
    for (int i = 0; i < 200; i++)
    {
        z = std::pow(z, 2) + c;
        if (std::norm(z) > 10)
        {
            return i;
        };
    }
    return 0;
}

float lerp(float a, float b, float f)
{
    return (a * (1.0 - f)) + (b * f);
}

int main(int argv, char **args)
{
    SDL_CreateWindowAndRenderer(1000, 1000, 0, &window, &renderer);
    SDL_RenderSetScale(renderer, 2, 2);

     for (double x = 0.0; x < 1.0; x += 1 / scale_factor)
    {
        for (double y = 0.0; y < 1.0; y += 1 / scale_factor)
        {
            double loc_x = lerp(-2.0, 2.0, x);
            double loc_y = lerp(-2.0, 2.0, y);
            int iters = is_in_mandelbrot_set(std::complex<double>(loc_x, loc_y));
            if (iters == 0)
            {
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
                SDL_RenderDrawPointF(renderer, loc_x * 100 + 200, loc_y * 100 + 200);
            }
            else
            {
                SDL_SetRenderDrawColor(
                    renderer,
                    4 * iters % 255,
                    4 * iters % 255,
                    4 * iters % 255,
                    255);
                SDL_RenderDrawPointF(renderer, loc_x * 100 + 200, loc_y * 100 + 200);
            }
        }
    }
    
    bool quit = false;
    while (!quit)
    {
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                quit = true;
                exit;
            }
        }
        SDL_RenderPresent(renderer);
    }
    
}

目前仅通过将scale_factor降至200左右可避免窗口无响应,但会大幅损失画质。推测绘制过程中事件循环未执行,尝试新增线程也未解决问题,恳请帮助解决。

解决方案

核心问题分析

你的代码把所有像素的计算和绘制放在主事件循环之前,这会导致主线程被长时间阻塞:当scale_factor=1000时,要处理1000*1000=100万像素的曼德博计算,这个过程会持续数秒甚至更久,期间SDL无法处理任何窗口事件(比如关闭窗口、系统的窗口重绘请求),所以系统会判定窗口无响应。

另外,SDL渲染器不是线程安全的,如果在子线程中调用SDL_RenderDrawPointF等渲染函数,会导致不可预期的错误,这也是新增线程无效的原因。

具体修复步骤

  1. 将绘制任务拆分到主事件循环中:每次事件循环只绘制一行或几行像素,这样既能逐步完成渲染,又能保证事件被及时处理。
  2. 优化曼德博计算效率:用z * z替代std::pow(z, 2),后者是通用幂函数,对于平方运算来说直接乘法效率高得多。
  3. 修正坐标映射逻辑:直接将窗口坐标线性映射到曼德博集合的坐标范围(-2~2),避免浮点偏移错误,同时使用整数坐标的SDL_RenderDrawPoint提升绘制速度。
  4. 修复事件处理的语法错误:exit是无效语句,应该使用SDL_Quit()并退出程序。

修改后的代码

#include <iostream>
#include <complex>
#include <SDL.h>

const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 1000;
const int MAX_ITER = 200;

SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;

// 优化曼德博集合计算:用z*z替代std::pow(z,2)
int is_in_mandelbrot_set(std::complex<double> c)
{
    std::complex<double> z(0, 0);
    for (int i = 1; i <= MAX_ITER; ++i)
    {
        z = z * z + c;
        if (std::norm(z) > 4.0) // 通常用4作为逃逸阈值,比10更合理
        {
            return i;
        }
    }
    return 0;
}

int main(int argc, char **args)
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer);
    SDL_SetWindowTitle(window, "Mandelbrot Set");

    // 记录当前绘制的行,逐步渲染
    int current_y = 0;
    bool quit = false;

    while (!quit)
    {
        // 处理事件,保证窗口响应
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                quit = true;
            }
        }

        // 每次循环绘制10行,平衡渲染速度和响应性
        const int batch_size = 10;
        for (int y = current_y; y < current_y + batch_size && y < WINDOW_HEIGHT; ++y)
        {
            for (int x = 0; x < WINDOW_WIDTH; ++x)
            {
                // 将窗口坐标映射到曼德博集合的坐标范围(-2.0~2.0)
                double real = (static_cast<double>(x) / WINDOW_WIDTH) * 4.0 - 2.0;
                double imag = (static_cast<double>(y) / WINDOW_HEIGHT) * 4.0 - 2.0;
                std::complex<double> c(real, imag);

                int iters = is_in_mandelbrot_set(c);
                if (iters == 0)
                {
                    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // 集合内的点用黑色
                }
                else
                {
                    // 生成渐变灰度,避免单一模运算的生硬颜色
                    Uint8 brightness = static_cast<Uint8>(255 * (iters / static_cast<double>(MAX_ITER)));
                    SDL_SetRenderDrawColor(renderer, brightness, brightness, brightness, 255);
                }
                SDL_RenderDrawPoint(renderer, x, y);
            }
        }
        current_y += batch_size;

        // 更新画面
        SDL_RenderPresent(renderer);

        // 所有行绘制完成后,保持事件循环
        if (current_y >= WINDOW_HEIGHT)
        {
            current_y = WINDOW_HEIGHT; // 停止继续绘制
        }
    }

    // 清理资源
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

额外优化建议

  • 如果想要更快的渲染速度,可以使用SDL纹理(SDL_Texture)来批量更新像素,而不是逐点绘制。
  • 可以添加交互功能,比如鼠标拖拽平移、滚轮缩放,提升可视化体验。
  • 对于更高精度的需求,可以使用long double替代double,但会增加计算时间。

内容的提问来源于stack exchange,提问作者SBECK1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:54:54