自研光线追踪器实现漫反射时球体透明、噪点问题的调试记录
自研光线追踪器漫反射渲染异常排查
问题背景
- 无引导复刻《Ray Tracing in One Weekend》入门渲染逻辑、自研光线追踪器
- 实现简单漫反射计算环节出现渲染异常:输出的球体呈现透明、带噪点的错误效果
- 调试阶段核心实现代码见下方代码段
核心实现代码
球体相交检测模块
#pragma once #include "Camera.h" #include <cmath> #include "Defs.h" template<typename O> class Sphere{ O Radius; Point3<O> Center; Color<O> Col; public: Sphere(O radius, Point3<O> center, Color<O> color); O quadratic(Ray<O> ray_in, O &disc, O t_Min, O t_Max); bool intersect(Ray<O> ray_in, rayInfo<O> &info, O t_Max); }; template<typename O> Sphere<O>::Sphere(O radius, Point3<O> center, Color<O> color) : Radius(radius), Center(center), Col(color) {} template<typename O> O Sphere<O>::quadratic(Ray<O> ray_in, O &disc, O t_Min, O t_Max){ Point3<O> origin = ray_in.Origin; Vec3<O> direction = ray_in.Direction; Vec3<O> o = origin-Center; O a = direction.dot(direction); O b = 2 * direction.dot(o); O c = o.dot(o) - (Radius * Radius); O discriminant = b * b - 4 * (a * c); if (discriminant < 0){ return false; } disc = ((-b - sqrt(discriminant)) / (2 * a)); if (disc > t_Max || t_Min > disc){ disc = ((-b + sqrt(discriminant)) / (2 * a)); if (disc > t_Max || t_Min > disc){ return false; } } return true; } template<typename O> bool Sphere<O>::intersect(Ray<O> ray_in, rayInfo<O> &info, O t_Max){ O disc; if (quadratic(ray_in, disc, info.Min, t_Max)){ Point3<O> p = ray_in.at(disc); Vec3<O> normal = (p - Center) / Radius; info.Point = p; info.Normal = normal; info.front_face(); info.Min = disc; return true; } else{ return false; } }
路径追踪迭代模块
#pragma once #include <iostream> #include "Shapes.h" #include "Defs.h" #include "Image.h" template<typename O> class Tracer{ std::vector<Sphere<O>> Shapes; public: Tracer(std::vector<Sphere<O>> shapes); void iterator(Ray<O> &ray, O &depth, O t_Max, O t_Min); }; template<typename O> Tracer<O>::Tracer(std::vector<Sphere<O>> shapes) : Shapes(shapes) {} template<typename O> void Tracer<O>::iterator(Ray<O> &ray, O &depth, O t_Max, O t_Min){ O conc = 1; Color<O> col(0.4f, 0.8f, 0.9f); bool hit = false; rayInfo<O> info; info.Min = t_Min; if (depth <= 0) conc = 0; while (depth > 0){ for (auto i = Shapes.begin(); i != Shapes.end(); i++){ if (i->intersect(ray, info, t_Max)){ conc *= 0.28; hit = true; } } if (!hit){ break; } Vec3<O> circ = Vec3<O>::random_in_unit_sphere(); Point3<O> target = info.Point + info.Normal + circ; ray = Ray<O>(info.Point, target - info.Point); info.Min = t_Min; hit = false; depth--; } col = col * conc; Image<O>::ColorPixel(std::cout, col); }
主程序入口模块
#include <iostream> #include <cmath> #include "../Matrix.h" #include "Camera.h" #include <vector> #include "Image.h" #include "Shapes.h" #include "Tracer.h" #include "../Defs.h" template<typename O> using Point3 = Vec3<O>; template<typename O> using Color = Vec3<O>; int main(){ const int img_width = 640; const int img_height = 480; const float img_ratio = img_width/img_height; float t_Max = infinity; float t_Min = 0.01; float depth = 50.0f; float inv_width = 1 / float(img_width); float inv_height = 1 / float(img_height); std::vector<Sphere<float>> shapes; Camera<float> cam1(40.0f, img_ratio, Point3<float>(0.0f, 0.0f, 0.0f), Point3<float>(0.0f, 0.0f, -1.0f), Vec3<float>(0.0f, 1.0f, 0.0f)); Sphere<float> cir1(0.4f, Point3<float>(0.0f, 0.0f, -1.0f), Color<float>(0.7f, 0.3f, 0.2f)); Sphere<float> cir2(3.0f, Point3<float>(0.0f, -3.0f, -1.0f), Color<float>(0.2f, 0.7f, 0.8f)); Sphere<float> cir3(0.5f, Point3<float>(1.0f, 0.0f, -1.0f), Color<float>(0.2f, 0.3f, 0.7f)); shapes.push_back(cir1); shapes.push_back(cir2); shapes.push_back(cir3); Tracer<float> tracer(shapes); std::cout << "P3\n" << img_width << ' ' << img_height << "\n255" << std::endl; Ray<float> ray(Point3<float>(0.0f), Vec3<float>(0.0f)); for (int j = 0; j < img_height; j++) { std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush; for (int i = 0; i < img_width; i++){ depth = 50.0f; float x = i; float y = j; cam1.screenCords(x, y, img_width, img_height); ray = cam1.get_raydir(x, y); tracer.iterator(ray, depth, t_Max, t_Min); } } std::cerr << "\n done " << std::endl; }
调试过程记录
- 首次尝试将光强衰减项
conc *= 0.28移出形状遍历循环,画面仅能隐约看到阴影轮廓,远未达到预期效果。 - 根据提示修正逻辑缺陷:原代码在每次相交命中时错误将
info.Min设为当前命中距离,正确逻辑应缩小检测的最大距离阈值,避免光线检测到比当前最近命中点更远的物体。 - 修正上述逻辑缺陷后,添加抗锯齿效果与三次gamma校正,画面质量明显改善,但仍存在渲染异常。
- 最终定位到根本原因:
random_in_unit_sphere()函数的实现错误,采用拒绝采样逻辑修正球内随机点生成方法后,渲染功能恢复正常。
内容的提问来源于stack exchange,提问作者Tippitytopkek
相关产品推荐
相关产品推荐

