古典的レイトレーサーの実装(第2回) 練習問題解答例
第2回練習問題の解答例

問1

Rayの方向d\vec{d}を法線n\vec{n}で反射させた方向を返す関数Vec3 reflect(const Vec3& d, const Vec3& n)を作れ。

反射ベクトル
反射ベクトル

解答例

まず反射した方向r\vec{r}を求める。

d\vec{d}を水平な成分dh\vec{d_h}と垂直な成分(n\vec{n}に平行)dp\vec{d_p}に分解することを考えよう。するとd\vec{d}

d=dh+dp\vec{d} = \vec{d_h} + \vec{d_p}

と書ける。

dp\vec{d_p}

dp=(dn)n\vec{d_p} = (\vec{d}\cdot\vec{n})\vec{n}

と書ける。よってdh\vec{d_h}

dh=ddp=d(dn)n\vec{d_h} = \vec{d} - \vec{d_p} = \vec{d} - (\vec{d}\cdot\vec{n})\vec{n}

と書ける。

反射した方向r\vec{r}も同様に水平成分、垂直成分に分解すると

r=dhdp\vec{r} = \vec{d_h} - \vec{d_p}

と書けるから

r=dhdp=d2(dn)n\vec{r} = \vec{d_h} - \vec{d_p} = \vec{d} - 2(\vec{d}\cdot\vec{n})\vec{n}

この式で反射ベクトルが計算できる。

Vec3 reflect(const Vec3& d, const Vec3& n) {
    return d - 2*dot(d, n)*n;
}

問2

Phongの反射モデルを使って陰影計算を行ってみましょう。

Phongの反射モデルでは、光源の方向をl\vec{l}、Rayの方向をd\vec{d}、衝突点の法線をn\vec{n}とし、l-\vec{l}n\vec{n}で反射させたものをr\vec{r}とすると、光の強さII

I=kdmax{ln,0}+ksmax{(d)r,0}αI = k_d\max\{ \vec{l}\cdot\vec{n}, 0 \} + k_s\max \{ (-\vec{d})\cdot\vec{r} , 0\}^\alpha

で与えられます。

Phong Shading
Phong Shading

kd,ksk_d, k_skd+ks=1k_d + k_s = 1を満たす[0,1][0, 1]の区間に含まれる実数です。α\alphaはハイライトの強さを制御する0以上の実数です。これらのパラメーターを色々と変化させて画像を生成してみてください。

解答例

問1で実装したreflect()を使ってr\vec{r}を計算できる。

#include "vec3.h"
#include "image.h"
#include "sphere.h"
#include "camera.h"


Vec3 reflect(const Vec3& d, const Vec3& n) {
    return d - 2*dot(d, n)*n;
}


int main() {
    Image img(512, 512);
    Camera cam(Vec3(0, 0, -3), Vec3(0, 0, 1));

    Sphere sphere(Vec3(0, 0, 0), 1.0);

    Vec3 sunDir = normalize(Vec3(1, 1, -1));

    double kd = 0.5;
    double ks = 0.5;
    double alpha = 128.0;

    for(int i = 0; i < img.width; i++) {
        for(int j = 0; j < img.height; j++) {
            double u = (2.0*i - img.width)/img.width;
            double v = (2.0*j - img.height)/img.height;
            Ray ray = cam.getRay(u, v);

            Hit hit;
            if(sphere.intersect(ray, hit)) {
                Vec3 color = Vec3(1, 1, 1);
                Vec3 r = reflect(-1*sunDir, hit.hitNormal);
                double I = kd*std::max(dot(hit.hitNormal, sunDir), 0.0) + ks*std::pow(std::max(dot(-1*ray.direction, r), 0.0), alpha);
                img.setPixel(i, j, I*color);
            }
            else {
                img.setPixel(i, j, Vec3(0, 0, 0));
            }
        }
    }

    img.ppm_output();
}

kd=0.8,ks=0.2,α=32k_d=0.8, k_s=0.2, \alpha=32

レンダリング結果
レンダリング結果

kd=0.8,ks=0.2,α=128k_d=0.8, k_s=0.2, \alpha=128

レンダリング結果
レンダリング結果

kd=0.5,ks=0.5,α=128k_d=0.5, k_s=0.5, \alpha=128

レンダリング結果
レンダリング結果