-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathScene.hpp
96 lines (76 loc) · 2.24 KB
/
Scene.hpp
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#pragma once
#include <Siv3D.hpp>
#include "Sphere.hpp"
#include "Camera.hpp"
#include "Ray.hpp"
#include "HitRec.hpp"
#include "ImagePlane.hpp"
namespace RayT {
struct Scene
{
// 球のリスト
Array<Sphere> spheres;
// カメラ
Camera camera;
// 投影面
ImagePlane imagePlane;
Scene(const Camera _camera, const ImagePlane _imagePlane)
: camera(_camera)
, imagePlane(_imagePlane)
{ }
// スクリーン上の座標に向かう光線を返す
constexpr Ray rayAt(const Vec2 pos) const
{
// スクリーン上の3次元座標
const Vec3 pos3D(
imagePlane.center.x - imagePlane.size.x / 2 + pos.x,
-(imagePlane.center.y - imagePlane.size.y / 2 + pos.y),
imagePlane.center.z
);
return Ray(camera.pos, (pos3D - camera.pos).normalized());
}
// 光線と球の衝突判定
// 衝突する場合は光線の始点からの距離を、衝突しない場合はnoneを返す
Optional<double> intersects(const Ray ray, const Sphere sphere) const
{
const auto co = ray.origin - sphere.center;
const double a = ray.direction.dot(ray.direction);
const double b = 2 * ray.direction.dot(co);
const double c = co.dot(co) - sphere.r * sphere.r;
const double d = b * b - 4 * a * c;
// 判別式が0以下だった場合
// 方程式の解は0個か1個であるから、光線と球は交点を持たない
if (d <= 0)
return none;
// 小さい方の解が正だった場合
// 始点から近い交点が始点の表側にある
if (-b - Math::Sqrt(d) > 0.001)
return (-b - Math::Sqrt(d)) / (2 * a);
// 小さい方の解が負で、大きい方の解が正だった場合
// 始点から近い交点は始点の裏側にあるが、
// 始点から遠い交点が始点の表側にある
if (-b + Math::Sqrt(d) > 0.001)
return (-b + Math::Sqrt(d)) / (2 * a);
// 解が2つとも負だった場合
// 2つの交点はどちらも始点の裏側にある
return none;
}
// 光線を飛ばす
// 物体に衝突した場合はその情報を、しなかった場合はnoneを返す
Optional<HitRec> trace(const Ray ray) const
{
Optional<HitRec> closestHit;
for (const auto& sphere : spheres)
{
const auto t = intersects(ray, sphere);
if (!t)
continue;
if (closestHit && closestHit->t < t)
continue;
const auto p = ray.origin + ray.direction * t.value();
closestHit = HitRec(t.value(), p, (p - sphere.center).normalized(), sphere.materialPtr);
}
return closestHit;
}
};
}