《Ray Tracing in a Weekend》学习笔记04

想看原书可以看本系列的第一篇《Ray Tracing in a Weekend》学习笔记01

7. Antialiasing

这章要解决边缘锯齿的问题。

7.2 Some Random Number Utilities

首先需要的是一个返回真实随机数的随机数生成器。制作一个返回规范随机数的函数,该函数通常会返回0<=r<1范围内的随机实数。

//rtweekend.h  random_double() functions
#include <cstdlib>
...

inline double random_double() {
    // Returns a random real in [0,1).
    return rand() / (RAND_MAX + 1.0);
}

inline double random_double(double min, double max) {
    // Returns a random real in [min,max).
    return min + (max-min)*random_double();
}

C ++传统上没有标准的随机数生成器,但是较新版本的C ++已使用标头解决了此问题。 如果要使用此函数,则可以按照以下条件获得随机数:

//rtweekend.h random_double(), alternate implemenation
#include <random>
inline double random_double() {
    static std::uniform_real_distribution<double> distribution(0.0, 1.0);
    static std::mt19937 generator;
    return distribution(generator);
}
7.2 Generating Pixels with Multiple Samples

在这里插入图片描述

上次的运行结果能很明显的看到球面的边缘很锐利。我们一直都是发射一条射线去扫描单个像素,以达到采样的目的,很明显这是不够的。为了让边缘更平滑,可以向同一个像素用更多的射线去采样。红色框表示一个像素,用更多的射线去扫描该像素,原先只是选择一个射线,扫描的是像素的中心位置进行计算,得到像素值,用它啦代替整个方框的颜色。现在就是让方框中出现有更多的点来决定方框的颜色。

在这里插入图片描述

现在需要一个摄像机类来管理摄像机和场景漫游的任务。下面这个类使用之前的轴对齐相机实现了一个简单相机。

//camera.h  The camera class
#ifndef CAMERA_H
#define CAMERA_H

#include "rtweekend.h"

class camera {
    public:
        camera() {
            auto aspect_ratio = 16.0 / 9.0;
            auto viewport_height = 2.0;
            auto viewport_width = aspect_ratio * viewport_height;
            auto focal_length = 1.0;

            origin = point3(0, 0, 0);
            horizontal = vec3(viewport_width, 0.0, 0.0);
            vertical = vec3(0.0, viewport_height, 0.0);
            lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);
        }

        ray get_ray(double u, double v) const {
            return ray(origin, lower_left_corner + u*horizontal + v*vertical - origin);
        }

    private:
        point3 origin;
        point3 lower_left_corner;
        vec3 horizontal;
        vec3 vertical;
};
#endif

有了camera.h就可以控制多条射线。为了处理多次采样的颜色计算,需要更新write_color()函数。在每次迭代的过程中增加全色,最后根据采样的样本数做一次除法,来写入颜色。在rtweeken.h中添加一个函数clamp(x,min,max),将x的值限制在(min,max)中。

//rtweekend.h  The clamp() utility function
inline double clamp(double x, double min, double max) {
    if (x < min) return min;
    if (x > max) return max;
    return x;
}
//color.h The multi-sample write_color() function
void write_color(std::ostream &out, color pixel_color, int samples_per_pixel) {
    auto r = pixel_color.x();
    auto g = pixel_color.y();
    auto b = pixel_color.z();

    // Divide the color by the number of samples.
    auto scale = 1.0 / samples_per_pixel;
    r *= scale;
    g *= scale;
    b *= scale;

    // Write the translated [0,255] value of each color component.
    out << static_cast<int>(256 * clamp(r, 0.0, 0.999)) << ' '
        << static_cast<int>(256 * clamp(g, 0.0, 0.999)) << ' '
        << static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}

此时,main.c也需要修改:

//main.cc Rendering with multi-sampled pixels
#include "camera.h"

...

int main() {

    // Image

    const auto aspect_ratio = 16.0 / 9.0;
    const int image_width = 400;
    const int image_height = static_cast<int>(image_width / aspect_ratio);
    const int samples_per_pixel = 100;

    // World

    hittable_list world;
    world.add(make_shared<sphere>(point3(0,0,-1), 0.5));
    world.add(make_shared<sphere>(point3(0,-100.5,-1), 100));

    // Camera
    camera cam;

    // Render

    std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";

    for (int j = image_height-1; j >= 0; --j) {
        std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
        for (int i = 0; i < image_width; ++i) {
            color pixel_color(0, 0, 0);
            for (int s = 0; s < samples_per_pixel; ++s) {
                auto u = (i + random_double()) / (image_width-1);
                auto v = (j + random_double()) / (image_height-1);
                ray r = cam.get_ray(u, v);
                pixel_color += ray_color(r, world);
            }
            write_color(std::cout, pixel_color, samples_per_pixel);
        }
    }

    std::cerr << "\nDone.\n";
}

在这里插入图片描述

在这里插入图片描述

很明显可以看到变化,边缘不再那么锐利,边缘处的像素显得更为模糊,达到了抗锯齿的效果。

8 Diffuse Materials

我们现在已经有对象,并且每个像素都有多条光线采样。现在可以通过光线追踪制作一些逼真的材质。从漫反射材料开始。

8.1 A Simple Diffuse Material

不发光的漫反射对线仅具有周围环境的颜色,但会使用其自身的固有颜色来调和这些颜色。漫反射表面反射的光是随机的。比如我们将三条光线发送到一个漫反射表面,它们反射回来的方向都是随机的。

在这里插入图片描述

它们也可能是被吸收而不是被反射。表面越暗,吸收的可能性就越大。这也是为什么它很暗。任何随机化方向的算法都会产生看起来很粗糙的表面。 最简单的方法之一是理想的漫反射表面。

在这里插入图片描述

①从摄像机位置(eye)发射一条射线,交球面与p点。在p点做两个与球面相切的单位球体,这两个单位球体的球心就在P+nP-n(其中P表示eye->p向量,n表示p点的法向量)。以P-n为球心的单位球体在表面内部,P+n在外部。选择与射线方向在同一侧的单位球体,在其中随机选择一个点s,然后将一条光线从命中点p发送到s(用向量表示为S-P)。

选择s点的方法,使用最简单的拒绝方法。在单位立方体里面选择一个随机点,其中x,y,z都控制在(-1,1)中。如果该点在单位球体之外拒绝该点。

//vec3.h vec3 random utility functions
class vec3 {
  public:
    ...
    inline static vec3 random() {
        return vec3(random_double(), random_double(), random_double());
    }

    inline static vec3 random(double min, double max) {
        return vec3(random_double(min,max), random_double(min,max), random_double(min,max));
    }
//vec3.h The random_in_unit_sphere() function
vec3 random_in_unit_sphere() {
    while (true) {
        auto p = vec3::random(-1,1);
        if (p.length_squared() >= 1) continue;
        return p;
    }
}

更新ray_color()函数来使用新的随机方向生产器:

//main.c  ray_color() using a random ray direction
color ray_color(const ray& r, const hittable& world) {
    hit_record rec;

    if (world.hit(r, 0, infinity, rec)) {
        point3 target = rec.p + rec.normal + random_in_unit_sphere();
        return 0.5 * ray_color(ray(rec.p, target - rec.p), world);
    }

    vec3 unit_direction = unit_vector(r.direction());
    auto t = 0.5*(unit_direction.y() + 1.0);
    return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}
8.2 Limiting the Number of Child Rays

ray_color()是递归的。当射线无法击中任何物体时,要停止递归。但可能会递归很长时间,直到破坏堆栈。为了防止这种事情发生,我们需要限制最大的递归深度,在最大深度不返回任何光源。

//main.c ray_color() with depth limiting
color ray_color(const ray& r, const hittable& world, int depth) {
    hit_record rec;

    // If we've exceeded the ray bounce limit, no more light is gathered.
    if (depth <= 0)
        return color(0,0,0);

    if (world.hit(r, 0, infinity, rec)) {
        point3 target = rec.p + rec.normal + random_in_unit_sphere();
        return 0.5 * ray_color(ray(rec.p, target - rec.p), world, depth-1);
    }

    vec3 unit_direction = unit_vector(r.direction());
    auto t = 0.5*(unit_direction.y() + 1.0);
    return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}

...

int main() {

    // Image

    const auto aspect_ratio = 16.0 / 9.0;
    const int image_width = 400;
    const int image_height = static_cast<int>(image_width / aspect_ratio);
    const int samples_per_pixel = 100;
    const int max_depth = 50;
    ...

    // Render

    std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";

    for (int j = image_height-1; j >= 0; --j) {
        std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
        for (int i = 0; i < image_width; ++i) {
            color pixel_color(0, 0, 0);
            for (int s = 0; s < samples_per_pixel; ++s) {
                auto u = (i + random_double()) / (image_width-1);
                auto v = (j + random_double()) / (image_height-1);
                ray r = cam.get_ray(u, v);
                pixel_color += ray_color(r, world, max_depth);
            }
            write_color(std::cout, pixel_color, samples_per_pixel);
        }
    }

    std::cerr << "\nDone.\n";
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值