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

如何在窗口式光线追踪器中实时渲染每条扫描线?

使用SDL3实现光线追踪器实时扫描线渲染

需要用到的SDL3核心功能

  • SDL初始化与清理:SDL_Init(SDL_INIT_VIDEO)初始化视频子系统,SDL_Quit()退出时释放资源
  • 窗口与渲染器:SDL_CreateWindow()创建显示窗口,SDL_CreateRenderer()创建硬件加速渲染器
  • 纹理操作:SDL_CreateTexture()创建可更新的纹理(存储像素数据),SDL_UpdateTexture()更新纹理的指定区域(单条扫描线)
  • 渲染与显示:SDL_RenderCopy()将纹理绘制到渲染器,SDL_RenderPresent()刷新窗口显示
  • 事件处理:SDL_PollEvent()处理窗口关闭等事件,避免程序无响应

具体实现步骤与代码示例

1. 提前初始化SDL资源

在渲染前完成SDL的初始化、窗口和纹理创建,把这些逻辑整合到Camera类中:

#include <SDL3/SDL.h>
#include <vector>
#include <cstdio>
#include <cstdlib>

// 假设你的Color是vec3结构,分量为0~1的浮点数
struct Color {
    double r, g, b;
    Color& operator+=(const Color& other) {
        r += other.r;
        g += other.g;
        b += other.b;
        return *this;
    }
};

// 假设Ray和相关函数已定义
struct Ray {};
Ray Camera::get_ray(uint32_t i, uint32_t j) { /* 原有实现 */ }
Color ray_color(const Ray& r) { /* 原有实现 */ }

class Camera {
private:
    uint32_t _img_width;
    uint32_t _img_height;
    SDL_Window* _window = nullptr;
    SDL_Renderer* _renderer = nullptr;
    SDL_Texture* _texture = nullptr;

    // 将Color转换为SDL兼容的RGBA像素(RGBA8888格式)
    uint32_t color_to_rgba(const Color& c) {
        // 可选:添加gamma校正(比如sqrt),匹配原有write_color的逻辑
        uint8_t r = static_cast<uint8_t>(255.999 * clamp(c.r, 0.0, 1.0));
        uint8_t g = static_cast<uint8_t>(255.999 * clamp(c.g, 0.0, 1.0));
        uint8_t b = static_cast<uint8_t>(255.999 * clamp(c.b, 0.0, 1.0));
        uint8_t a = 255; // 不透明通道
        return SDL_MapRGBA(SDL_PIXELFORMAT_RGBA8888, r, g, b, a);
    }

    // 颜色分量钳制函数
    double clamp(double x, double min, double max) {
        if (x < min) return min;
        if (x > max) return max;
        return x;
    }

public:
    Camera(uint32_t width, uint32_t height) : _img_width(width), _img_height(height) {
        // 初始化SDL视频子系统
        if (SDL_Init(SDL_INIT_VIDEO) < 0) {
            fprintf(stderr, "SDL初始化失败: %s\n", SDL_GetError());
            exit(1);
        }

        // 创建窗口
        _window = SDL_CreateWindow("光线追踪实时渲染", _img_width, _img_height, 0);
        if (!_window) {
            fprintf(stderr, "创建窗口失败: %s\n", SDL_GetError());
            SDL_Quit();
            exit(1);
        }

        // 创建硬件加速渲染器
        _renderer = SDL_CreateRenderer(_window, nullptr, SDL_RENDERER_ACCELERATED);
        if (!_renderer) {
            fprintf(stderr, "创建渲染器失败: %s\n", SDL_GetError());
            SDL_DestroyWindow(_window);
            SDL_Quit();
            exit(1);
        }

        // 创建可流式更新的纹理
        _texture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_RGBA8888,
                                     SDL_TEXTUREACCESS_STREAMING, _img_width, _img_height);
        if (!_texture) {
            fprintf(stderr, "创建纹理失败: %s\n", SDL_GetError());
            SDL_DestroyRenderer(_renderer);
            SDL_DestroyWindow(_window);
            SDL_Quit();
            exit(1);
        }
    }

    // 析构时自动清理资源
    ~Camera() {
        SDL_DestroyTexture(_texture);
        SDL_DestroyRenderer(_renderer);
        SDL_DestroyWindow(_window);
        SDL_Quit();
    }

2. 修改render函数为实时扫描线渲染

替换原有输出PPM的逻辑,改为每渲染完一行就更新纹理并刷新窗口:

void render() {
        SDL_Event event;
        bool running = true;

        for (uint32_t j = 0; j < _img_height && running; ++j) {
            // 存储当前行的RGBA像素数据
            std::vector<uint32_t> row_pixels(_img_width);

            // 渲染当前行的所有像素
            for (uint32_t i = 0; i < _img_width; ++i) {
                Color pixel_color;
                Ray r = get_ray(i, j);
                pixel_color += ray_color(r);
                row_pixels[i] = color_to_rgba(pixel_color);
            }

            // 更新纹理的第j行区域
            SDL_UpdateTexture(_texture,
                              &SDL_Rect{0, static_cast<int>(j), static_cast<int>(_img_width), 1},
                              row_pixels.data(), _img_width * sizeof(uint32_t));

            // 将纹理渲染到窗口并刷新显示
            SDL_RenderCopy(_renderer, _texture, nullptr, nullptr);
            SDL_RenderPresent(_renderer);

            // 处理窗口事件(比如关闭窗口)
            while (SDL_PollEvent(&event)) {
                if (event.type == SDL_EVENT_QUIT) {
                    running = false;
                }
            }
        }
    }
};

关键注意点

  • 颜色转换:确保Color的浮点数分量正确映射到0~255的uint8_t,如果原有ray_color返回高动态范围值,记得添加gamma校正(比如对分量开平方),避免画面过暗
  • 纹理格式:示例中使用RGBA8888是SDL通用格式,也可换成RGB888,但要同步修改SDL_MapRGBA和纹理创建的格式参数
  • 性能优化:如果单线程渲染过慢,后续可扩展多线程渲染多行后批量更新纹理;SDL_TEXTUREACCESS_STREAMING适合频繁更新的场景
  • 事件处理:必须在循环中处理事件,否则操作系统会判定窗口无响应并强制关闭

内容的提问来源于stack exchange,提问作者Niccolò Tiezzi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:47:08