0%

RayTracing.vol5

Github Repo


接下来是球面着色,第一步,确定球面法线,法线是球面上点垂直于球面的向量。对于球 C 与 球面上点 P,P 点向外法线方向与$\boldsymbol {CP} $方向一致,如下图所示

因为目前没有光线,所以用颜色映射来可视化法线,将各分量映射至(0,1)的区间(即求其单位向量 $\vec{n}$),然后将 x/y/z 映射至 r/g/b,对于法线来说,我们要求的是交点,而不是相交与否。假定与原点最近的交点(即最小的 t),在 main.rs 做出如下更改使得程序可以计算并可视化 $\vec{n}$.

[main.rs]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn hit_sphere(center: Point3, radius: f32, r: &Ray) -> f32 {
let oc: Vec3 = r.origin() - center;
let a = dot(r.direction(),r.direction());
let b = 2.0 * dot(oc, r.direction());
let c = dot(oc, oc) - radius * radius;
let discriminant = b*b - 4.0 * a * c;
if discriminant < 0.0{
return -1.0;
}else{
return (-b - discriminant.sqrt() ) / (2.0 * a);
}
}
fn ray_color(r: &Ray) -> Color {
let mut t = hit_sphere(Vec3(0.0, 0.0, -1.0), 0.5, r);
if t > 0.0{
let N: Vec3 = unit_vector(r.at(t) - Vec3(0.0, 0.0, -1.0));
return 0.5 * Vec3(N.x() + 1.0, N.y() + 1.0, N.z() + 1.0);
}
let unit_direction = unit_vector(r.direction());
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)
}

运行得出下图:


对现有代码进行数学层面的简化,重新审视射线与球面相交的代码

[main.rs] 先前代码
1
2
3
4
5
6
7
8
9
10
11
12
fn hit_sphere(center: Point3, radius: f32, r: &Ray) -> f32 {
let oc: Vec3 = r.origin() - center;
let a = dot(r.direction(),r.direction());
let b = 2.0 * dot(oc, r.direction());
let c = dot(oc, oc) - radius * radius;
let discriminant = b*b - 4.0 * a * c;
if discriminant < 0.0{
return -1.0;
}else{
return (-b - discriminant.sqrt() ) / (2.0 * a);
}
}

首先,一个向量点乘自身等于这个向量的长度。其次,对于一元二次方程的求根公式,不难发现 b 的定义里有一个乘 2,假设 $ b=2h $,则求根公式可以做以下变形:
$$
\frac {-b \pm \sqrt {b^2-4ac}}{2a}
$$

$$
=\frac {-2h \pm \sqrt {(2h)^2-4ac}}{2a}
$$

$$
=\frac {-2h \pm 2 \sqrt {h^2-ac}}{2a}
$$

$$
=\frac {-h \pm \sqrt {h^2-ac}}{a}
$$

因此我们可以简化射线与球相交的代码

[main.rs] 简化之后代码
1
2
3
4
5
6
7
8
9
10
11
12
fn hit_sphere(center: Point3, radius: f32, r: &Ray) -> f32 {
let oc: Vec3 = r.origin() - center;
let a = r.direction().length_squared();
let half_b = dot(oc, r.direction());
let c = oc.length_squared() - radius * radius;
let discriminant = half_b * half_b - a * c;
if discriminant < 0.0{
return -1.0;
}else{
return (-half_b - discriminant.sqrt() ) / a;
}
}