Making a Scene

Chapter 7

とりあえず object(球体)をそれっぽくレンダリングすることはできるようになった。とはいえ、このままでは object の数が増えると全ての object に対して個別に処理を行わなければならず、面倒くさい。
なので全ての object (と light) を管理する World を用意し、その World に対して Ray をどばすようにする。

"Making a Scene" というタイトルだから World じゃなくて Scene でも良い気もするけど。

Building a World

前述のように World は object と light を集約して管理するためのものなのでメンバもそれになる。

pub struct World {
    /// ライト
    lights: Vec<Light>,
    /// オブジェクト
    shapes: Vec<Sphere>,
}

あとは Ray との交差判定を行う intersect() で shapes に対してループする。

    pub fn intersect(&self, ray: &Ray) -> Vec<Intersection> {
        let mut intersections = vec![];
        for shape in &self.shapes {
            let mut xs = shape.intersect(ray);
            intersections.append(&mut xs);
        }

        intersections.sort_unstable_by(|i1, i2| {
            if i1.t < i2.t {
                std::cmp::Ordering::Less
            } else {
                std::cmp::Ordering::Greater
            }
        });

        intersections
    }

で、ここで得られた intersections の中で最も手前にある物がカメラから見える object になる。

この object に対して shading 等の計算をしていくわけだけれど、そこ用いる情報を必要になる度に計算していたのでは効率が悪いということで、事前にまとめて計算しておく。
本の中では computation という名前にしているが、これだと何を表しているのかが全くわからないということで、ここでは IntersectionState にした。これもあまり良い名前とは思えないから、良い名前を思いついたら変更予定。

pub struct IntersectionState<'a> {
    /// Ray と object が交差する場所での t
    pub(crate) t: f32,
    /// Ray と交差した object
    pub(crate) object: &'a Sphere,
    /// ワールド座標系における交差位置
    pub(crate) point: Point3D,
    /// ワールド座標系における視線ベクトル
    pub(crate) eyev: Vector3D,
    /// ワールド座標系における法線ベクトル
    pub(crate) normalv: Vector3D,
    /// Ray の起点が object 内部であるか
    pub(crate) inside: bool,
}

Defining a View Transformation

特に書くことはない。

Camera

今までは main() の中で行っていた Ray の生成処理を Camera にまとめる。この時、内部処理を出力画像のサイズから独立させるため、ray_for_pixel() ではカメラから 1 離れた位置にある平面を用いて Ray の生成を行っている。
この時、撮影する領域を表す平面は field_of_view と出力画像のアスペクト比から決定する。

結果

最終的に以下の画像が得られた。本の内容通りだと 100 x 50 で小さすぎるため、少し大きくしてある。

f:id:mtXTJocj:20201221094101p:plain

f:id:mtXTJocj:20201221092414p:plain