-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathScene.cpp
156 lines (140 loc) · 5.21 KB
/
Scene.cpp
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
// Created by kent on 2021/11/30.
//
#include "Scene.h"
#include <glm/glm.hpp>
#include "Quad.h"
#include "MathTools.h"
#include <iostream>
#include <string>
#include "Mat_Dielectrics.h"
#include "Mat_Diffuse_Lambert.h"
static void CHECK_WHY(bool cond) {
// if (cond)
// std::cout << "why?";
}
bool Scene::rayHit(const Ray &ray, HitObject *&hitObject, Eigen::Vector3d &pos) {
typedef std::pair<Eigen::Vector3d, std::shared_ptr<HitObject>> HitPair;
std::vector<HitPair> hit_list;
for (auto oit: lights) {
Eigen::Vector3d pt;
if (oit->rayHit(ray, &pt)) {
hit_list.push_back({pt, oit});
}
}
for (auto oit: objects) {
Eigen::Vector3d pt;
if (oit->rayHit(ray, &pt)) {
hit_list.push_back({pt, oit});
}
}
if (!hit_list.empty()) {
std::sort(hit_list.begin(), hit_list.end(), [&ray](HitPair v1, HitPair v2) {
return (v1.first - ray.origin).norm() < (v2.first - ray.origin).norm();
});
pos = hit_list.front().first;
hitObject = hit_list.front().second.get();
return true;
}
return false;
}
static Eigen::Vector3d
iterRay(Ray &path_out_to_here, Scene *sc, std::list<Eigen::Vector3d> &light_path, uint32_t max_deep = 30) {
if (!max_deep) {
return {0.0, 0.0, 0.0};
}
HitObject *here;
Eigen::Vector3d pt;
if (sc->rayHit(path_out_to_here, here, pt)) {
Eigen::Vector3d ret = Eigen::Vector3d::Zero();
// compute direct illumination
Ray rayout;
rayout.origin = pt;
rayout.dir = -path_out_to_here.dir;
ret = sc->computeLight(here, rayout, pt);
// russian
// if (MathTools::rand_01() > sc->russian_stop_gate)
// return ret; //{0.0, 0.0, 0.0};
light_path.push_back(pt);
// iteration
Ray path_here_to_other;
Eigen::Vector3d attetion_another;
if (here->material->scatter(path_out_to_here, pt, here, attetion_another, path_here_to_other)) {
Eigen::Vector3d down_level = iterRay(path_here_to_other, sc, light_path, max_deep - 1);
Eigen::Vector3d down_l_o = color_mult(attetion_another, down_level);
double rcp_pdf = 2.0 * M_PI;
double cosTheta = here->normalAtPoint(pt).dot(path_here_to_other.dir);
ret += down_l_o * std::max(0.0, cosTheta) * rcp_pdf;
}
return ret;
}
return Eigen::Vector3d::Zero();
}
Eigen::Vector3d Scene::computeLight(HitObject *hp, const Ray &ray_out, Eigen::Vector3d pos) {
// choice one light source
int light_idx = std::floor((double) lights.size() * MathTools::rand_01());
Quad *only_quad_now = dynamic_cast<Quad *>(lights[light_idx].get());
Eigen::Vector3d dA_light = only_quad_now->randomPick_dA();
// visible test
Ray r; // ray in test
r.origin = pos;
r.dir = (dA_light - pos).normalized();
HitObject *check = nullptr;
Eigen::Vector3d pttest;
if (rayHit(r, check, pttest) && (check == only_quad_now)) {
// pass visible test
Eigen::Vector3d attention;
r.origin = dA_light; // now is from light to this
r.dir = (pos - dA_light).normalized();
if (hp->material->brdf(ray_out, r, pos, hp, attention)) {
// compute brdf result
double receive_cosTheta = hp->normalAtPoint(pos).dot(-r.dir);
double distance_falloff = 1.0 / std::max(1.0, pow((dA_light - ray_out.origin).norm(), 2.0));
double light_cosTheta = r.dir.dot(only_quad_now->normalAtPoint(r.origin));
double rcp_pdf = 1.0 / only_quad_now->area();
return color_mult(attention, only_quad_now->emessive_intensity)
* std::max(0.0, receive_cosTheta)
* distance_falloff
* std::max(0.0, light_cosTheta)
* rcp_pdf;
}
}
return Eigen::Vector3d::Zero(); // no light in
}
Ray Scene::rayAtPixel(double x, double y) {
Ray r;
// get the plane and range
double raydis = 1.0;
Eigen::Vector3d center = cam_pos + cam_dir * raydis;
Eigen::Vector3d up(0.0, 1.0, 0.0);
Eigen::Vector3d right = cam_dir.cross(up);
up = right.cross(cam_dir);
Eigen::Vector2d xy_normed = pixelToNormalized(x, y);
Eigen::Vector3d hit_at_one = center
+ right * xy_normed.x() * xMaxAtDistance(1.0)
+ up * xy_normed.y() * yMaxAtDistance(1.0);
r.origin = cam_pos;
r.dir = (hit_at_one - cam_pos).normalized();
return r;
}
Eigen::Vector2d Scene::pixelToNormalized(double x, double y) {
double ratio_x = (x / pixels_width) * 2.0 - 1.0;
double ratio_y = (y / pixels_height) * 2.0 - 1.0;
return Eigen::Vector2d(ratio_x, ratio_y);
}
double Scene::xMaxAtDistance(double dis) {
return std::tan(glm::radians(fovy / 2.0)) * dis;
}
double Scene::yMaxAtDistance(double dis) {
double ratio = pixels_height / pixels_width;
return xMaxAtDistance(dis) * ratio;
}
Eigen::Vector3d Scene::computeLo(Ray &r) {
HitObject *hp;
Eigen::Vector3d pt;
std::list<Eigen::Vector3d> light_path;
if (rayHit(r, hp, pt)) {
return hp->emessive_intensity + iterRay(r, this, light_path);
}
return Eigen::Vector3d::Zero();
}