Rustでコンパイル時レイトレーシング3
2022/02/04 00:20 | 公開 |
2022/02/05 12:52 | 背景色が間違っていたのを修正 |
2022/02/16 01:38 | 背景のグラデーションの向きの修正とリポジトリへのリンクを修正 |
前回記事
リポジトリ
記事の内容はコミットIDa22c5917e25e505aaeefe75e34121e1817f9ae6c
のものです。
今回はようやくレイを飛ばすところまで行けました!
といってもオブジェクトにヒットするとかはしておらずレイの向きによって色が変わっているだけなのでまだレイトレーシングっぽくないですね。
レンダリング結果
今回の変更点
Ray
型を追加(ray.rs
)- 新しい
feature
(const_eval_limit
)を追加 main.rs
でRay
を使用する
ray.rs
まずRay
型の内容からです。
レイの始点と方向を持つだけのstruct
です。特別注意が必要なことはないと思います。
use crate::{Point3, Vec3};
#[derive(Clone, Copy, Debug)]
pub struct Ray {
pub orig: Point3,
pub dir: Vec3,
}
impl Ray {
pub const fn new(origin: &Point3, direction: &Vec3) -> Self {
Ray {
orig: origin.clone(),
dir: direction.clone(),
}
}
pub const fn origin(&self) -> Point3 {
self.orig
}
pub const fn direction(&self) -> Vec3 {
self.dir
}
pub const fn at(&self, t: f64) -> Vec3 {
self.orig + self.dir * t
}
}
lib.rs
lib.rs
の内容はray
moduleの宣言などの他const_eval_limit
というfeature
の宣言が増えています。
#![feature(const_trait_impl)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_mut_refs)]
#![feature(const_eval_limit)]
#![const_eval_limit = "0"]
pub mod ray;
pub mod util;
pub mod vec3;
pub use ray::*;
pub use vec3::*;
pub type Point3 = Vec3;
pub type Color = Vec3;
const_eval_limit
const_eval_limit
とはconst fn
の中でのステップ数の制限をコントロールするためのfeature
です。
#![feature(const_eval_limit)]
#![const_eval_limit = "N"]
とすることでステップ数の制限をN
に設定できます。N = 0のときは無制限になります。
これを使用しないとconst fn
でsqrt
を呼び出しただけでステップ数の上限に引っかかってしまいまともにコードを書けそうになかったので追加しました。
main.rs
main.rs
の内容を先に貼っておきます。
変更点がない箇所は省略しています。
main.rs
#![feature(const_trait_impl)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_mut_refs)]
#![feature(const_eval_limit)]
#![const_eval_limit = "0"]
use image::ImageFormat;
use ray_tracing_in_one_weekend_compile_time::*;
const ASPECT_RATIO: f64 = 16.0 / 9.0;
const IMAGE_WIDTH: usize = 400;
const IMAGE_HEIGHT: usize = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as usize;
const PIXEL_COUNT: usize = IMAGE_WIDTH * IMAGE_HEIGHT;
const fn ray_color(ray: &Ray) -> Color {
let unit_direction = unit_vector(&ray.direction());
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() -> anyhow::Result<()> {
// 省略
}
const fn ray_trace() -> [Color; PIXEL_COUNT] {
let viewport_height = 2.0;
let viewport_width = viewport_height * ASPECT_RATIO;
let focal_length = 1.0;
let origin = Point3::ZERO;
let horizontal = Vec3::new(viewport_width, 0.0, 0.0);
let vertical = Vec3::new(0.0, viewport_height, 0.0);
let lower_left_corner =
origin - horizontal / 2.0 - vertical / 2.0 - Vec3::new(0.0, 0.0, focal_length);
let mut pixel_colors = [Color::ZERO; PIXEL_COUNT];
let mut i = 0;
let mut j = (IMAGE_HEIGHT - 1) as i32;
let mut pixel_index = 0;
// forが使えないのでwhileで代用
while 0 <= j {
while i < IMAGE_WIDTH {
let u = (i as f64) / (IMAGE_WIDTH - 1) as f64;
let v = (j as f64) / (IMAGE_HEIGHT - 1) as f64;
let r = Ray::new(
&origin,
&(lower_left_corner + u * horizontal + v * vertical - origin),
);
pixel_colors[pixel_index] = ray_color(&r);
i += 1;
pixel_index += 1;
}
i = 0;
j -= 1;
}
pixel_colors
}
// 省略
まずmain.rs
でもconst_eval_limit
が追加されています。
ループの中でステップ数の上限に引っかかるので上限なしにしてあります。
#![feature(const_eval_limit)]
#![const_eval_limit = "0"]
画像は横400を基準としてアスペクト比16:9になるように設定しています。
const ASPECT_RATIO: f64 = 16.0 / 9.0;
const IMAGE_WIDTH: usize = 400;
const IMAGE_HEIGHT: usize = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as usize;
ray_color
関数は引数のRay
を飛ばしてヒットした箇所の色を返す関数です。
現時点ではヒットするオブジェクトがないのでレイの向きを元に返す色を決めています。
const fn ray_color(ray: &Ray) -> Color {
let unit_direction = unit_vector(&ray.direction());
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)
}
ray_trace
関数ではピクセルごとにレイを生成してray_color
関数の返り値を配列に格納しています。
const fn ray_trace() -> [Color; PIXEL_COUNT] {
let viewport_height = 2.0;
let viewport_width = viewport_height * ASPECT_RATIO;
let focal_length = 1.0;
let origin = Point3::ZERO;
let horizontal = Vec3::new(viewport_width, 0.0, 0.0);
let vertical = Vec3::new(0.0, viewport_height, 0.0);
let lower_left_corner =
origin - horizontal / 2.0 - vertical / 2.0 - Vec3::new(0.0, 0.0, focal_length);
let mut pixel_colors = [Color::ZERO; PIXEL_COUNT];
let mut i = 0;
let mut j = (IMAGE_HEIGHT - 1) as i32;
let mut pixel_index = 0;
// forが使えないのでwhileで代用
while 0 <= j {
while i < IMAGE_WIDTH {
let u = (i as f64) / (IMAGE_WIDTH - 1) as f64;
let v = (j as f64) / (IMAGE_HEIGHT - 1) as f64;
let r = Ray::new(
&origin,
&(lower_left_corner + u * horizontal + v * vertical - origin),
);
pixel_colors[pixel_index] = ray_color(&r);
i += 1;
pixel_index += 1;
}
i = 0;
j -= 1;
}
pixel_colors
}
まとめ
今回でようやくRay
が登場しました。
かんたんに書くためにまた新しいconst_eval_limit
というfeature
を使用しました。
どんどんnightlyでしか書けないコードになってきましたね。stableの制限の中でもやりたいと思ったことはあるのですがだいぶ難しそうです。
次は球とレイの交差判定を行います。