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

在函数中修改struct时遇诡异Bug:光线追踪场景渲染异常

光线追踪器函数调用导致的异常Bug排查

问题描述

我正在跟随Peter Shirley的《Ray Tracing In A Weekend》系列用C语言开发光线追踪器,遇到一个诡异Bug:通过random_scene函数向场景添加物体时,生成的图像异常;但把相同的添加逻辑直接写在main函数里,图像就正常。

相关代码

  1. main函数中创建场景结构体:
int main() {
    // Image
    const float aspect_ratio = 3.0 / 2.0;
    const int image_width = 400;
    const int image_height = (int) (image_width / aspect_ratio);
    const int samples_per_pixel = 40;
    const int max_depth = 50;

    // World
    hittable_list world; // 存储所有场景物体的结构体
  1. 用于添加物体的random_scene函数:
void random_scene(hittable_list *world) {
    world->current_index = 0;

    material ground_material = (material) {LAMBERTIAN, .lam = col(0.5, 0.5, 0.5)};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(0.0, -1000, 0), 1000, &ground_material}});
    
    material material1 = (material) {DIELECTRIC, .d = 1.5};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(0, 1, 0), 1.0, &material1}});

    material material2 = (material) {LAMBERTIAN, .lam = col(0.4, 0.2, 0.1)};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(-4, 1, 0), 1.0, &material2}});

    material material3 = (material) {METAL, .m = {(color){0.7, 0.6, 0.5}, 0.0}};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(4, 1, 0), 1.0, &material3}});
}
  1. 调用random_scene的main代码(图像异常):
int main() {
    // Image
    const float aspect_ratio = 3.0 / 2.0;
    const int image_width = 400;
    const int image_height = (int) (image_width / aspect_ratio);
    const int samples_per_pixel = 40;
    const int max_depth = 50;

    // World
    hittable_list world;
    random_scene(&world);
  1. main内直接添加物体的代码(图像正常):
int main() {
    // Image
    const float aspect_ratio = 3.0 / 2.0;
    const int image_width = 400;
    const int image_height = (int) (image_width / aspect_ratio);
    const int samples_per_pixel = 40;
    const int max_depth = 50;

    // World
    hittable_list world;
    world.current_index = 0;

    material ground_material = (material) {LAMBERTIAN, .lam = col(0.5, 0.5, 0.5)};
    add_obj(&world, (hittable) {.type = SPHERE, .s = (sphere){p3(0.0, -1000, 0), 1000, &ground_material}});
    
    material material1 = (material) {DIELECTRIC, .d = 1.5};
    add_obj(&world, (hittable) {.type = SPHERE, .s = (sphere){p3(0, 1, 0), 1.0, &material1}});

    material material2 = (material) {LAMBERTIAN, .lam = col(0.4, 0.2, 0.1)};
    add_obj(&world, (hittable) {.type = SPHERE, .s = (sphere){p3(-4, 1, 0), 1.0, &material2}});

    material material3 = (material) {METAL, .m = {(color){0.7, 0.6, 0.5}, 0.0}};
    add_obj(&world, (hittable) {.type = SPHERE, .s = (sphere){p3(4, 1, 0), 1.0, &material3}});

问题原因

核心是栈内存悬空指针:

  • random_scene函数中的ground_material、material1等都是函数局部变量,存储在栈内存中。当函数执行完毕返回后,栈上的这些变量会被销毁,内存会被系统回收或重新分配。
  • 你创建的sphere结构体中存储了这些material的指针,后续光线追踪计算时,程序会访问这些已经失效的指针,导致未定义行为(比如读取乱码数据,最终生成异常图像)。
  • 而main函数中的material变量属于main的局部栈内存,main函数直到程序结束才会返回,所以这些变量的内存在整个光线追踪过程中都有效,指针访问正常,图像生成正确。

解决方案

方案1:使用静态内存存储材质

将random_scene中的material变量声明为static,这样它们会被存储在静态内存区,函数返回后不会被销毁:

void random_scene(hittable_list *world) {
    world->current_index = 0;

    static material ground_material = (material) {LAMBERTIAN, .lam = col(0.5, 0.5, 0.5)};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(0.0, -1000, 0), 1000, &ground_material}});
    
    static material material1 = (material) {DIELECTRIC, .d = 1.5};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(0, 1, 0), 1.0, &material1}});

    static material material2 = (material) {LAMBERTIAN, .lam = col(0.4, 0.2, 0.1)};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(-4, 1, 0), 1.0, &material2}});

    static material material3 = (material) {METAL, .m = {(color){0.7, 0.6, 0.5}, 0.0}};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(4, 1, 0), 1.0, &material3}});
}

方案2:动态分配材质内存

使用malloc为每个material分配堆内存,后续记得在场景销毁时用free释放:

void random_scene(hittable_list *world) {
    world->current_index = 0;

    material *ground_material = malloc(sizeof(material));
    *ground_material = (material) {LAMBERTIAN, .lam = col(0.5, 0.5, 0.5)};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(0.0, -1000, 0), 1000, ground_material}});
    
    material *material1 = malloc(sizeof(material));
    *material1 = (material) {DIELECTRIC, .d = 1.5};
    add_obj(world, (hittable) {.type = SPHERE, .s = (sphere){p3(0, 1, 0), 1.0, material1}});

    // 其余材质同理...
}

方案3:修改结构体设计(推荐)

直接将material嵌入sphere或hittable结构体,避免使用指针:

// 修改sphere结构体,直接包含material而不是指针
typedef struct {
    point3 center;
    float radius;
    material mat; // 不再是指针
} sphere;

// 对应的添加逻辑修改
void random_scene(hittable_list *world) {
    world->current_index = 0;

    add_obj(world, (hittable) {
        .type = SPHERE,
        .s = (sphere){
            p3(0.0, -1000, 0),
            1000,
            (material){LAMBERTIAN, .lam = col(0.5, 0.5, 0.5)}
        }
    });
    
    // 其余球体同理...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 12:45:35