本文主要是介绍《学一辈子光线追踪》 四 重要性采样材料,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
蒙特卡洛光线追踪技术系列 见 蒙特卡洛光线追踪技术
在接下来的两章中,我们的目标是利用我们的程序向光源发送一束额外的光线,这样我们的图片噪声就不会太多了。假设我们可以使用pdf pLight(direction) 向光源发送一束光线。假设我们有一个与s相关的pdf,我们称之为pSurface(direction)。pdf 的一个优点是,可以使用它们的线性混合来形成混合密度,这也是pdf。
例如,最简单的方法是:
p(direction) = 0.5*pLight(direction) + 0.5*pSurface(direction)
只要权重是正的并且加起来是1,任何这种pdf的混合都是pdf。记住,我们可以使用任何pdf-它不会改变我们的答案!所以,目标是要找出当乘积 s(direction)*color(direction) 更大的时候,如何使pdf更大。对于漫反射曲面,这主要是猜测颜色(方向)高的地方。对于镜子来说,s()只在一个方向附近是巨大的,所以它更重要。事实上,大多数渲染器都会将镜面作为一种特殊情况,而只是将s/p隐式化——我们的代码目前就是这样做的。
让我们做一个简单的重构,暂时删除所有不是Lambertian的材质(不想删除也得改改里面继承的函数)。我们可以再次使用康奈尔盒子场景,还是使用之前的视点:
vec3 lookfrom(278, 278, -800);//
vec3 lookat(278, 278, 0);
框架也是我在上本书的最后实现的每次渲染都输出结果的框架。
减少噪音是我们的目标。我们将通过构建一个pdf来实现这一点,它可以向光发送更多的光线。
首先,让我们测试代码,以便它显式地对一些 pdf 进行采样,然后对其进行规范化。记住MC基础:积分 f(x) 大约等于 f(r)/p(r)。对于朗伯材料,让我们像现在这样取样:p(direction)=cos(theta)/Pi。
我们修改材料以启用此重要性采样:
class material {
public:virtual bool scatter(const ray &r_in, const hit_record& rec, vec3& albedo, ray& scattered,float &pdf)const {return false;}virtual float scattering_pdf(const ray &r_in, const hit_record& rec, const ray& scattered)const {return false;}virtual vec3 emitted(float u, float v, const vec3&p)const {return vec3(0, 0, 0);}};class lambertian :public material {
public:lambertian(texture*a):albedo(a){}virtual float scattering_pdf(const ray& r_in, const hit_record& rec, ray& scattered)const {float cosine = dot(rec.normal, unitVector(scattered.direction()));if (cosine < 0)cosine = 0;return cosine / M_PI;}virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& alb, ray& scattered, float &pdf)const {vec3 target = rec.p + rec.normal + random_in_unit_sphere();scattered = ray(rec.p, unitVector(target - rec.p));//r_in.time()alb = albedo->value(rec.u, rec.v, rec.p);pdf = dot(rec.normal, unitVector(scattered.direction())) / M_PI;return true;}texture* albedo;
};
注意修改一些其他代码,因为凡是继承后的类,都需要修改继承的虚函数。
vec3 color(const ray&r, hitable *world, int depth) {hit_record rec;if (world->hit(r, 0.001, MAXFLOAT, rec)) {ray scattered;vec3 emitted = rec.mat_ptr->emitted(rec.u, rec.v, rec.p);float pdf;vec3 albedo;if (depth < 50 && rec.mat_ptr->scatter(r, rec, albedo, scattered,pdf)) {return emitted + albedo* rec.mat_ptr->scattering_pdf(r,rec,scattered) *color(scattered, world, depth + 1);}else {return emitted;}}else {return vec3(0, 0, 0);}
}
结果竟然只显示一个灯,物体和墙都没有了。我们开始调试,首先修改一下color函数:
vec3 color(const ray&r, hitable *world, int depth) {hit_record rec;if (world->hit(r, 0.001, MAXFLOAT, rec)) {ray scattered;vec3 emitted = rec.mat_ptr->emitted(rec.u, rec.v, rec.p);float pdf;vec3 albedo;if (depth < 50 && rec.mat_ptr->scatter(r, rec, albedo, scattered,pdf)) {return emitted + color(scattered, world, depth + 1);}//albedo* rec.mat_ptr->scattering_pdf(r,rec,scattered) *else {return emitted;}}else {return vec3(0, 0, 0);}
}
得到结果:
隐约能看到物体,说明是 albedo* rec.mat_ptr->scattering_pdf(r,rec,scattered) 出了问题。
深入检查,发现
virtual float scattering_pdf(const ray& r_in, const hit_record& rec, ray& scattered)const
的最后一个参数 ray 竟然少了一个const修饰符。改过来以后就可以了。效果和前面的一样。
现在,为了体验,尝试不同的抽样策略。让我们从高于表面的半球中随机选择。这就是p(direction) = 1/(2*Pi)
virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& alb, ray& scattered, float &pdf)const {vec3 direction;do {direction = random_in_unit_sphere();} while (dot(direction, rec.normal) < 0);scattered = ray(rec.p, unitVector(direction), r_in.time());alb = albedo->value(rec.u, rec.v, rec.p);pdf = 0.5 / M_PI;return true;}
得到结果:
相同背景物体和光照条件下的对比:
再说一遍,我应该得到相同的图片,除了不同的变化幅度,但并没有。第二张照片的长方体非常均匀,这是为什么呢?
它很接近我们的老照片,但有一些差异不是噪声。这个高盒子的正面颜色要均匀得多。所以我有最难在蒙特卡罗程序中找到的一种错误——一种能产生合理外观图像的错误。我不知道这个bug是程序的第一个版本还是第二个版本,甚至两者都是!
让我们建立一些基础设施来解决这个问题。
这篇关于《学一辈子光线追踪》 四 重要性采样材料的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!