“谁把我放逐人群,一万人一种表情”

现在我们有可以被正确渲染出来的无光照球体,接下来我们需要定义一些有光照的材质,这样才能真正发挥出光线追踪的优势。

我们首先需要整理一下目前的代码来便利后面的开发过程。

5.1 光线反射

image.png

观察上图我们可以知道,目前我们的光追渲染器还只停留在摄像机发射View Ray这个阶段,而在View Ray击中物体后发出的Shadow Ray还并没有实现。

因此为了实现光线反射的这部分,我们还需要继续改造我们的camera类。

5.1.1 扩展Color类

因为光线在反射过程中需要混合色彩,因此我们需要为Color扩展用以混合色彩的方法:

class Color {
		// ...
public:
		// ...

    Color& operator*=(double t);
    Color& operator*=(const Color &v);
};

Color operator*(const Color &lhs, const Color &rhs);
Color operator*(double t, const Color &v);
Color operator*(const Color &v, double t);
Color &Color::operator*=(double t) {
    SetR(m_r * t);
    SetG(m_g * t);
    SetB(m_b * t);

    return *this;
}

Color &Color::operator*=(const Color &v) {
    SetR(m_r * v.r());
    SetG(m_g * v.g());
    SetB(m_b * v.b());

    return *this;
}

Color operator*(const Color &lhs, const Color &rhs) {
    return {lhs.r() * rhs.r(),
            lhs.g() * rhs.g(),
            lhs.b() * rhs.b()};
}

Color operator*(double t, const Color &v) {
    return {v.r() * t, v.g() * t , v.b() * t};
}

Color operator*(const Color &v, double t) {
    return t * v;
}

5.1.2 定义Material类

基于模拟现实的目的,不同物体的材质会决定物体表面的颜色和以何种方式反射光线,因此我们需要定义一个抽象的材质类,并在后面实现它:

#include "ray.hpp"
#include "color.hpp"

class HitPayload;

class Material {
public:
    virtual bool Scatter(const Ray& rayIn, Ray& rayScattered, const HitPayload& payload, Color& attenuation) const = 0;
};

材质类的核心方法为scatter,这个方法定义了材质反射光线的模式(例如绝对光滑的金属会严格进行反射,漫反射则会因为微平面而进行随机方向的反射)。

5.1.3 改写渲染函数

为摄像机加入RayColor方法,这个方法通过递归调用自身来实现光线的反射并获取最终的颜色。

class Camera {
		// ...
private:
		// ...
    Color getSkyBoxColor(const Ray& ray);

public:
		// ...
    Color RayColor(const Ray &ray, Scene *scene, int depth);
};

再加入获取天空盒颜色的函数,在光线没有命中物体时用以获取背景颜色。