同心映射的算法原理如下图,具体阐述参考书籍:
由于同心映射是针对于samples这个数组的映射,所以只需要修改samples这个数组的数据就行了。
需要修改的Sampler类:
#pragma once #ifndef __SAMPLER_HEADER__ #define __SAMPLER_HEADER__ #include "../utilities/geometry.h" class Sampler { public: ... void map_to_unit_disk();//新增函数,同心映射 protected: integer nsamples; integer nsets; std::vector<Point3> samples; std::vector<integer> shuffled_indices; integer count; integer jump; }; #endif
对应函数实现:
void Sampler::map_to_unit_disk() { std::vector<Point3> copySamples = samples;//备份 ldouble r, phi; Point2 sp; for (u_integer i = 0; i < copySamples.size(); i++) { sp.x = 2.0 * copySamples[i].x - 1.0; sp.y = 2.0 * copySamples[i].y - 1.0; if (sp.x > -sp.y) { if (sp.x > sp.y) { r = sp.x; phi = sp.y / sp.x; } else { r = sp.y; phi = 2.0 - sp.x / sp.y; } } else { if (sp.x < sp.y) { r = -sp.x; phi = 4 + sp.y / sp.x; } else { r = -sp.y; phi = (sp.y != 0) ? (6 - sp.x / sp.y) : 0; } } phi *= M_PI / 4.0; samples[i].x = r * cos(phi); samples[i].y = r * sin(phi); } }
需要修改的World:render函数
void World::render() { Ray ray; ldouble x, y; open_window(vp.hres, vp.vres); Point3 sp; ray.o = Point3(0, 0, 1); vp.sampler->map_to_unit_disk();//测试同心映射 for (integer r = vp.vres - 1; r >= 0; r--)//render from left-corner to right-corner for (integer c = 0; c < vp.hres; c++) { RGBColor color; for (integer p = 0; p < vp.nsamples; p++) { sp = vp.sampler->sample_unit_square(); x = vp.s * (c - 0.5 * vp.hres + sp.x); y = vp.s * (r - 0.5 * vp.vres + sp.y); ray.d = Point3(x, y, -1); color += tracer_ptr->trace_ray(ray); } color /= vp.nsamples; display_pixel(r, c, color); } }
本测试结果采用的是Hammersley类的采样算法,然后再加上同心映射。测试结果图如下,明显边角梗圆润了:
其余的半球和全球映射书上都给了代码,就不做测试了。不过我已经实现了。效果和这个差不多。就不发布代码了。请参考书本。