745 字
4 分钟
光线追踪:灯光
光照是光线追踪的关键组成部分。早期简单的光线追踪器使用抽象光源,比如空间中的点或方向。现代方法则采用更符合物理规律的光源,它们具有位置和尺寸。要创建这样的光源,我们需要能够将任何常规物体转化为场景中的发光体
自发光材质
//自发光材质class diffuse_light : public material { public: diffuse_light(std::shared_ptr<texture> tex) : tex(tex) {} //纹理作为发光颜色 diffuse_light(const color& emit) : tex(std::make_shared<solid_color>(emit)) {} //传入颜色包装为纯色纹理
// 物体本身发出的光 color emitted(double u, double v, const point3& p) const override { return tex->value(u, v, p); }
private: std::shared_ptr<texture> tex;};为了避免让所有非发光材质都实现 emitted() ,我在基类中让它返回黑色
virtual color emitted(double u, double v, const point3& p) const { return color(0,0,0);}为光线颜色函数添加背景色
接下来,我们希望背景是纯黑的,这样场景中唯一的光源就来自发光体。为此,我们将在 ray_color 函数中添加一个背景色参数,并关注新的 color_from_emission 值。
color ray_color(const ray& r, int depth, const hittable& world) const { hit_record rec; // 递归深度耗尽时返回黑色,表示这条光线不再贡献能量。 if(depth <= 0) return color(0,0,0);
// 如果光线没有击中物体,返回背景颜色 if (!world.hit(r, interval(0.001, infinity), rec)) return background;
// scattered 保存材质散射出的下一条光线,attenuation 表示本次散射后的颜色衰减。 ray scattered; color attenuation;
// 先取材质自身发出的光;普通材质通常返回黑色,光源材质会返回发光颜色。 color color_from_emission = rec.mat->emitted(rec.u,rec.v,rec.p);
// 如果材质不散射光线,说明递归在这里结束,只返回自发光部分。 if(!rec.mat->scatter(r, rec, attenuation, scattered)) return color_from_emission;
// 递归追踪散射光线,并乘以材质衰减,得到反射/折射带来的间接光。 color color_from_scatter = attenuation * ray_color(scattered, depth - 1, world);
// 最终颜色 = 材质自身发光 + 散射光线继续追踪得到的颜色。 return color_from_emission + color_from_scatter; }将物体转化为光源
我们把一个矩形设置为光源:
注意,光源比 (1,1,1) 更亮。这样才能有足够的亮度来照亮物体
void simple_light() { hittable_list world;
auto pertext = make_shared<noise_texture>(4); world.add(make_shared<sphere>(point3(0,-1000,0), 1000, make_shared<lambertian>(pertext))); world.add(make_shared<sphere>(point3(0,2,0), 2, make_shared<lambertian>(pertext)));
auto difflight = make_shared<diffuse_light>(color(4,4,4)); world.add(make_shared<quad>(point3(3,1,-2), vec3(2,0,0), vec3(0,2,0), difflight));
camera cam;
cam.aspect_ratio = 16.0 / 9.0; cam.image_width = 400; cam.samples_per_pixel = 100; cam.max_depth = 50; cam.background = color(0,0,0);
cam.vfov = 20; cam.lookfrom = point3(26,3,6); cam.lookat = point3(0,2,0); cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);}空的康奈尔盒
void cornell_box() { hittable_list world;
auto red = make_shared<lambertian>(color(.65, .05, .05)); auto white = make_shared<lambertian>(color(.73, .73, .73)); auto green = make_shared<lambertian>(color(.12, .45, .15)); auto light = make_shared<diffuse_light>(color(15, 15, 15));
world.add(make_shared<quad>(point3(555,0,0), vec3(0,555,0), vec3(0,0,555), green)); world.add(make_shared<quad>(point3(0,0,0), vec3(0,555,0), vec3(0,0,555), red)); world.add(make_shared<quad>(point3(343, 554, 332), vec3(-130,0,0), vec3(0,0,-105), light)); world.add(make_shared<quad>(point3(0,0,0), vec3(555,0,0), vec3(0,0,555), white)); world.add(make_shared<quad>(point3(555,555,555), vec3(-555,0,0), vec3(0,0,-555), white)); world.add(make_shared<quad>(point3(0,0,555), vec3(555,0,0), vec3(0,555,0), white));
camera cam;
cam.aspect_ratio = 1.0; cam.image_width = 600; cam.samples_per_pixel = 200; cam.max_depth = 50; cam.background = color(0,0,0);
cam.vfov = 40; cam.lookfrom = point3(278, 278, -800); cam.lookat = point3(278, 278, 0); cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);}由于光源很小,大部分随机光线无法击中光源,因此图像噪点非常多
