5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rustでコンピューターグラフィックスの基礎を学ぶ その6

Last updated at Posted at 2026-01-27

フルコードはgithubにあります。

週末レイトレーシングのここをやります。

リファクタリング

下記の部分が冗長ではないかとコメントをもらう。
確かにhoge.x()などせずにhoge.xで構造体の中身にアクセスできる。

impl Vec3 {
    pub fn x(&self) -> f64 {
        self.x
    }
}

Rayモジュールにも同様の問題がある。
74de2cdc776e2aで修正した。

PI, INFINITY, clampについては、f64のものをそのまま使えばよいとこれもコメントをもらった。f6adf8eで修正。

f64の定数について

PIINFINITYの扱いに差があって気持ち悪い。ドキュメントによると、どちらも定数だけど、数学的な定数はf64::constsの下にいくと。なんでやねん。

ちなみにRubyでは数学関数はモジュールとして独立している。これなら分かる。

Gemini.mdとMakefile

今更ながら、Gemini.mdMakefileを書く。Makefileを書く理由はcargoコマンドを覚えられないから。

# Project Overview

このプロジェクトは、レイトレーシングの基礎をRustプログラミングを通じて理解するためのものです。
下記のチュートリアルを通して学んでいます。

https://raytracing.github.io/books/RayTracingInOneWeekend.html
https://raytracing.github.io/books/RayTracingTheNextWeek.html
https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html

チュートリアルはC++で書かれていますが、本プロジェクトはRustで記述します

# Tech Stack

- **Languages:** Rust
- **Libraries:** 基礎を学ぶのが目的なので、ライブラリは極力使いません

# Key Commands
- Running the app: `make`
- Building: `cargo check check`
- Running tests: `make test`
all::
	cargo run > output.ppm
	magick convert output.ppm output.png
	open -W output.ppm

test::
	cargo check
	cargo fmt
	cargo test

lint::
	cargo clippy

マテリアルを表すモジュール

チュートリアルに戻る。表面の色と質感を扱うマテリアルモジュールの作成。

use crate::hittable::HitRecord;
use crate::ray::Ray;
use crate::vec3::Color;

pub trait Material {
    fn scatter(
        &self,
        r_in: &Ray,
        rec: &HitRecord,
        attenuation: &mut Color,
        scattered: &mut Ray,
    ) -> bool;
}

HitRecord構造体側を拡張して、Materialのスマートポインタを作る。

pub struct HitRecord {
    pub p: Point3,
    pub normal: Vec3,
    pub t: f64,
    pub mat_ptr: Arc<dyn Material>,
    pub front_face: bool,
}

Material側でもHitRecordを参照しているので、循環参照の形になるが、片側がスマートポインタなのでメモリリークは発生しないという理解でいいのかしら。まあ、今のコードだとそもそもメモリを解放するタイミングがないんだけど。

ランバート反射の抽象化

main.rsにあったランバート反射の計算をMaterialモジュールに移動する。

impl Material for Lambertian {
    fn scatter(
        &self,
        _r_in: &Ray,
        rec: &HitRecord,
        attenuation: &mut Color,
        scattered: &mut Ray,
    ) -> bool {
        let scatter_direction = rec.normal + Vec3::random_unit_vector();
        *scattered = Ray::new(rec.p, scatter_direction);
        *attenuation = self.albedo;
        true
    }
}

以上を踏まえてmain.rsを書き直す(関係ない部分は省略しています)。

fn ray_color(r: &Ray, world: &dyn Hittable, depth: u32) -> Color {
    if depth <= 0 {
        return Color::new(0.0, 0.0, 0.0);
    }

    if let Some(rec) = world.hit(r, 0.001, f64::INFINITY) {
        let mut scattered = Ray::new(Point3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0));
        let mut attenuation = Color::new(0.0, 0.0, 0.0);
        if rec
            .mat_ptr
            .scatter(r, &rec, &mut attenuation, &mut scattered)
        {
            return attenuation * ray_color(&scattered, world, depth - 1);
        }
        return Color::new(0.0, 0.0, 0.0);
    } else {
        let unit_direction = r.direction.unit_vector();
        let t = 0.5 * (unit_direction.y + 1.0);
        (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0)
    }
}

fn main() {
    let material_ground = Arc::new(Lambertian::new(Color::new(0.8, 0.8, 0.0)));
    let material_center = Arc::new(Lambertian::new(Color::new(0.7, 0.3, 0.3)));

    let mut world = HittableList::new();
    world.add(Arc::new(Sphere::new(
        Point3::new(0.0, -100.5, -1.0),
        100.0,
        material_ground,
    )));
    world.add(Arc::new(Sphere::new(
        Point3::new(0.0, 0.0, -1.0),
        0.5,
        material_center,
    )));
}

Lamberian構造体は反射率(albedo)をフィールドに持つ。

pub struct Lambertian {
    pub albedo: Color,
}

impl Lambertian {
    pub fn new(albedo: Color) -> Self {
        Self { albedo }
    }
}

albedoはColor構造体を使っているので、RGBごとに減衰率を指定している。これまではどの色でも50%減衰固定。

main.rsray_color()を修正して、albedoを使う。

-        return 0.5 * ray_color(&Ray::new(rec.p, target - rec.p), world, depth - 1);
+        return attenuation * ray_color(&scattered, world, depth - 1);

これで、物体に任意の色をつけることができるようになった。

output.png

チュートリアルではいきなり金属球も描画しているが、ここでは分けて進めた。
金属マテリアルはその7でやる。続く。

5
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?