Assignment 7¶
这次的作业使用超采样改善锯齿以及摩尔纹,整体难度不是特别大。
Tasks¶
使用 sample
类来存储一个采样点的位置\((0,0)\rightarrow (1,1)\) 以及相应的颜色,然后使用一个新的类 film
用来存储所有像素的采样结果,成员变量为:
其中 samples
是成员为 Sample
的数组,存储 width*height*num_samples
这么多的样本
(1)然后实现不同的采样器 (Sampler),相应的实现一个基类 sampler
以及各个不同采样器的派生类。同时题目还给你了一个函数 RandomSampler
用于生成采样的点彩图。
(2)接着实现 filter
,用于使用采样点以生成超采样后的图像,这个类中最为重要的函数为
利用采样后的结果 film
,返回像素坐标为 \((i,j)\) 的像素的颜色。
要实现这个函数还需要几个辅助函数
这个函数用于返回某个点的权重,不同的 filter 的权重函数不一样
Filter 使用的采样点的范围由 supportRaidus
来确定,这个范围题目中已经说明的很清楚了,这里不再赘述。
(3)更改 render
函数,利用 sampler
和 filter
得到抗锯齿的结果
Implementation¶
sampler
¶
采样器的核心函数为
用于生成第 n 个采样点的采样点位置,分别要实现的采样器为
RandomSampler
随机采样UniformSampler
均匀采样JitteredSampler
抖动采样(均匀加上小范围抖动)
Vec2f RandomSampler::getSamplePosition (int n) {
float x = (float)rand() / (float)RAND_MAX;
float y = (float)rand() / (float)RAND_MAX;
return Vec2f(x, y);
}
Vec2f UniformSampler::getSamplePosition (int n) {
int i = n / nPerSide;
int j = n % nPerSide;
return {(i + 0.5f)*gap, (j + 0.5f)*gap};
}
Vec2f JitteredSampler::getSamplePosition (int n) {
int i = n / nPerSide;
int j = n % nPerSide;
return {(i + rand() / (float)RAND_MAX)*gap, (j + rand() / (float)RAND_MAX)*gap};
}
Warning
注意一下这里返回的点的范围是 \((0,0)\rightarrow(1,1)\)
filter
¶
filter 类中主要实现的三个函数
Vec3f getColor(int i, int j, Film *film);
virtual float getWeight(float x, float y) = 0;
virtual int getSupportRadius() = 0;
后面两个函数分别在派生类中有不同的实现方式
Vec3f Filter::getColor(int i, int j, Film *film)
{
int r = getSupportRadius();
int width = film->getWidth();
int height = film->getHeight();
int n = film->getNumSamples();
Vec3f color(0,0,0);
float weight = 0;
for (int x = std::max(0, i - r); x <= std::min(width - 1, i + r); x++)
{
for (int y = std::max(0, j - r); y <= std::min(height - 1, j + r); y++)
{
for (int k = 0; k < n; k++)
{
//得到该像素点的第k个采样点的结果
Sample sample = film->getSample(x, y, k);
//计算相对于像素中心点的偏移量
Vec2f samplePosition = Vec2f(sample.getPosition().x()+x,
sample.getPosition().y()+y);
Vec2f offset = Vec2f(samplePosition.x()-i-0.5,
samplePosition.y()-j-0.5);
//得到该点的权重,并计算加权后的颜色
float w = getWeight(offset.x(), offset.y());
weight += w;
color += w * sample.getColor();
}
}
}
return {color.x()/weight, color.y()/weight, color.z()/weight};
}
然后不同的 filter 的权重是不一样的
BoxFilter
是均匀的权重TentFilter
是从像素中心到距离为radius
的位置权重是线性变化的GaussianFilter
权重函数是高斯函数
float BoxFilter::getWeight(float x, float y) {
float dist = std::max(std::abs(x), std::abs(y));
if (dist <= radius)
return 1;
else return 0;
}
float TentFilter::getWeight(float x, float y) {
float length = std::sqrt(x*x + y*y);
float weight = 1 - length/radius;
if (weight < 0)
return 0;
else return weight;
}
float GaussianFilter::getWeight(float x, float y) {
float length = std::sqrt(x*x + y*y);
return exp( -length*length/(2*sigma*sigma));
}
Warning
注意 getWeight
函数的参数输入的是相对于像素中心点的偏差,同时这个像素中心点指的是像素坐标为 (i,j) 的像素中心点
render
函数¶
更改相应的渲染函数,加入采样以及filter的过程
void RayTracerRenderer::Render() {
Renderer::Render(); // preparations
auto tracer = new RayTracer(scene, maxBounces, cutoffWeight);
//采样器
Sampler* sampler;
if (randomSampler) {
sampler = new RandomSampler(numSamples);
} else if (jitteredSampler){
sampler = new JitteredSampler(numSamples);
} else {
//如果没有说明采样器,那么就默认为采样数为1的UniformSampler
sampler = new UniformSampler(numSamples);
}
//过滤器
Filter* filter = nullptr;
if (boxFilter){
filter = new BoxFilter(radius);
} else if (tentFilter) {
filter = new TentFilter(radius);
} else if (gaussianFilter) {
filter = new GaussianFilter(sigma);
}
if (nx*ny*nz != 0)
{
Grid* grid = tracer->getGrid();
BoundingBox* bb = grid->getBoundingBox();
int nx,ny,nz;
std::tie(nx, ny, nz) = grid->getGridSize();
RayTracingStats::Initialize(width, height, bb, nx, ny, nz);
}
else
{
RayTracingStats::Initialize(width, height, group->getBoundingBox(), 1, 1, 1);
}
//初始化film,用于存储sample
auto film = new Film(image->Width(), image->Height(), numSamples);
for (int i = 0; i < image->Width(); i++) {
for (int j = 0; j < image->Height(); j++) {
for (int k = 0; k < numSamples; k++)
{
Vec2f offset = sampler->getSamplePosition(k);
auto ray = camera->generateRay(Vec2f(
(offset.x() + i - image->Width() / 2.0f) / image->Width(),
(offset.y() + j - image->Height() / 2.0f) / image->Width()));
Hit hit;
Vec3f color;
if (nx*ny*nz == 0)
{
color = tracer->traceRay(ray, camera->getTMin(), 0, 1, 1, hit);
}
else
{
color = tracer->traceRayFast(ray, camera->getTMin(), 0, 1, 1, hit);
}
//设置采样点的信息
film->setSample(i, j, k, offset, color);
}
}
}
//输出采样的结果
if (sampleFile != nullptr)
{
film->renderSamples(sampleFile, sampleZoom);
}
//输出权重函数函数
if (filterFile != nullptr)
{
film->renderFilter(filterFile, filterZoom, filter);
}
for (int i = 0; i < image->Width(); i++)
{
for (int j = 0; j < image->Height(); j++)
{
Vec3f color = film->getSample(i, j, 0).getColor();
//根据采样信息 film,利用filter输出渲染结果
if (filter!=nullptr) color = filter->getColor(i, j, film);
image->SetPixel(i, j, color);
}
}
if(stats)
RayTracingStats::PrintStatistics();
delete sampler;
delete filter;
delete tracer;
}
Summary¶
这次作业相对前几次作业比较简单,应该也是最后一个光线追踪的作业了。这次作业做的快的主要原因是没怎么出现 bug,同时作业也巩固并加深了我对supersampling的理解。
还剩下两次作业,争取在寒假前再做一个!