0%

RayTracing.vol3

Github Repo


定义一个 Ray 结构体表示射线,分为两个字段,orig 表示射线的起点,dir 表示射线的方向,定义一个方法at(t),输出 orig + dir*t,使其能够表示由原点与方向确定的唯一直线上的所有点。

[rayh.rs] 定义 Ray 结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
use ray::Color;
use ray::Point3;
use ray::Vec3;

pub struct Ray {
pub orig: Point3,
pub dir: Vec3,
}

impl Default for Ray {
fn default() -> Self {
Ray {
orig: Default::default(),
dir: Default::default(),
}
}
}

impl Ray {
pub fn new(origin: &Point3, direction: &Vec3) -> Self {
Ray {
orig: origin.clone(),
dir: direction.clone(),
}
}

pub fn origin(&self) -> Point3 {
self.orig
}

pub fn direction(&self) -> Vec3 {
self.dir
}

pub fn at(&self, t: f32) -> Point3 {
self.orig + t * self.dir
}
}

ray tracer 分为三个步骤,计算从眼睛到像素的射线,确定与射线有交点的物品,计算交点处的颜色。
首先把图像投影在三维空间的一个平面上,这个平面的长宽比和渲染图片相同,投影平面与投影点之间的举例记为一个单位,称为焦长,即下图的 F 所表示。在(0,0,0)处设置观察点,从图像左下角开始,遍历屏幕,并使用沿屏幕两侧的两个偏移矢量在屏幕上移动射线端点。

更改后的 main 代码如下:

[main.rs]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
mod color;
mod rayh;
use color::*;
use ray::*;
use rayh::*;

const IMAGE_WIDTH: u16 = 400;
const ASPECT_RATIO: f32 = 16.0 / 9.0;

fn ray_color(r: &Ray) -> Color {
let unit_direction = unit_vector(r.direction());
let t = 0.5 * (unit_direction.y() + 1.0);
(1.0 - t) * Vec3(1.0, 1.0, 1.0) + t * Vec3(0.5, 0.7, 1.0)
}
fn main() {
let IMAGE_HEIGHT = unsafe {
(IMAGE_WIDTH as f32 / ASPECT_RATIO).to_int_unchecked::<u16>()
}; //unsafe 函数无法使用 const 定义
println! {"P3\n{} {}\n255",IMAGE_WIDTH,IMAGE_HEIGHT};

let viewport_height = 2.0;
let viewport_width: f32 = ASPECT_RATIO * viewport_height;
let focal_length = 1.0;

let origin: Point3 = Vec3(0.0, 0.0, 0.0);
let horizontal = Vec3(viewport_width, 0.0, 0.0);
let vertical = Vec3(0.0, viewport_height, 0.0);
let lower_left_corner =
origin - horizontal / 2 as f32 - vertical / 2 as f32 - Vec3(0.0, 0.0, focal_length);

for j in (0..IMAGE_HEIGHT).rev() {
eprintln!("\rScanlines remaining:{}", j);
for i in 0..IMAGE_WIDTH {
let u = i as f32 / (IMAGE_WIDTH - 1) as f32;
let v = j as f32 / (IMAGE_HEIGHT - 1) as f32;
let r = Ray {
orig: origin,
dir: lower_left_corner + u * horizontal + v * vertical - origin,
};
let pixel_color = ray_color(&r);
print!("{}", write_color(pixel_color));
}
}
eprintln!("\nDone");
}

所得图片如下

ray_color(r) 方法根据 r 的射线方向的 y 坐标确定白色与蓝色的混合程度,y 的值越大,越趋近于蓝色。如果将 main.rs 第十三行改成(1.0 - t) * Vec3(0.2, 0.9, 0.2) + t * Vec3(0.2, 0.0, 1.0)结果如下