同心映射的算法原理如下图,具体阐述参考书籍:

由于同心映射是针对于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类的采样算法,然后再加上同心映射。测试结果图如下,明显边角梗圆润了:

其余的半球和全球映射书上都给了代码,就不做测试了。不过我已经实现了。效果和这个差不多。就不发布代码了。请参考书本。