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

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

 

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

posted on 2020-03-31 11:12  dalgleish  阅读(275)  评论(0编辑  收藏  举报