在函数中修改struct时遇诡异Bug:光线追踪场景渲染异常
光线追踪器函数调用导致的异常Bug排查
问题描述
我正在跟随Peter Shirley的《Ray Tracing In A Weekend》系列用C语言开发光线追踪器,遇到一个诡异Bug:通过random_scene函数向场景添加物体时,生成的图像异常;但把相同的添加逻辑直接写在main函数里,图像就正常。
相关代码
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函数:
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}}); }
- 调用
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);
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
相关产品推荐
相关产品推荐

