-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
191 lines (171 loc) · 5.37 KB
/
main.cpp
File metadata and controls
191 lines (171 loc) · 5.37 KB
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include "random.h"
#include "vector.h"
#include "world.h"
#include "sphere.h"
#include "camera.h"
#include "material.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_video.h>
#include <thread>
#include <mutex>
/// Semaphore
struct Semaphore {
private:
size_t value = 0;
std::mutex mut;
public:
explicit Semaphore(size_t val): value(val) {}
void post(size_t val = 1) {
std::unique_lock<std::mutex> lk(mut);
value += val;
}
size_t get_value() {
std::unique_lock<std::mutex> lk(mut);
return value;
}
bool try_wait(size_t* val) {
std::unique_lock<std::mutex> lk(mut);
if (value == 0) {
*val = value;
return false;
} else {
value--;
*val = value;
return true;
}
}
};
Vec3f color(const Ray& r, const World& world, const int depth) {
Hit hit;
if (world.hit(r, 0.001, MAXFLOAT, hit)) {
Ray scattered;
Vec3f attenuation;
Vec3f emission = hit.mat->emitted(0.0, 0.0, hit.p);
if (depth < 50 && hit.mat->scatter(r, hit, attenuation, scattered)) {
// TODO: Shadow rays
return emission + attenuation*color(scattered, world, depth + 1);
} else {
return emission;
}
} else {
// return Vec3f{0.0};
Vec3f dir = r.direction().normalized();
float t = 0.5*(dir.y + 1.0);
return (1.0 - t) * Vec3f{1.0, 1.0, 1.0} + t * Vec3f{0.5, 0.7, 1.0};
}
}
struct Region {
const size_t nx, ny;
size_t x0, x1;
size_t y0, y1;
};
struct ThreadArgs {
const World& world;
const Camera& cam;
/// Screen buffer
uint32_t* pixels;
/// Number of samples per pixels
int ns;
/// All work items (a.k.a Regions of the screen)
const std::vector<Region>& regions;
/// Number of work items remaining
Semaphore& sem;
/// Number of threads done
Semaphore& sem_done;
};
void thread_work(ThreadArgs args) {
size_t row = 0;
while (args.sem.try_wait(&row)) {
const Region& reg = args.regions[row];
for (size_t j = reg.y0; j <= reg.y1; j++) {
for (size_t i = reg.x0; i <= reg.x1; i++) {
Vec3f t_color;
for (int s = 0; s < args.ns; s++) {
float u = float(i + rand_0_1()) / float(reg.nx);
float v = float(j + rand_0_1()) / float(reg.ny);
Ray r = args.cam.get_ray(u, v);
t_color += color(r, args.world, 0);
}
// if (t_color == NAN) { std::cerr << "NAN" << std::endl; }
// if (t_color == INFINITY) { std::cerr << "INF" << std::endl; }
t_color /= float(args.ns);
t_color = {std::sqrt(t_color.x), std::sqrt(t_color.y), std::sqrt(t_color.z)}; // Gamma-2 correction
auto ir = uint32_t(t_color.x * 255.99f);
auto ig = uint32_t(t_color.y * 255.99f);
auto ib = uint32_t(t_color.z * 255.99f);
auto ia = uint32_t(1);
uint32_t pixel = 0;
pixel += (ia << (8 * 3));
pixel += (ir << (8 * 2));
pixel += (ig << (8 * 1));
pixel += (ib << (8 * 0));
args.pixels[((reg.ny - j) * reg.nx) + i] = pixel;
}
}
}
args.sem_done.post();
}
int main() {
SDL_Init(SDL_INIT_EVERYTHING);
init_random_generator();
const size_t nx = 720;
const size_t ny = 400;
const size_t ns = 10; // Number of samples per pixel
SDL_Window* window = SDL_CreateWindow("RayTracer", 0, 0, nx, ny, 0);
SDL_Surface* scr = SDL_GetWindowSurface(window);
uint32_t* pixels = (uint32_t*) scr->pixels;
Vec3f lookfrom = {13, 2, 3};
Vec3f lookat = {0, 0, 0};
float dist_to_focus = (lookfrom - lookat).length();
float aperature = 0.0;
Camera cam{lookfrom, lookat, 20, nx / ny, aperature, dist_to_focus};
World world;
world.simple_light();
const size_t num_threads = std::thread::hardware_concurrency() == 0 ? 4 : std::thread::hardware_concurrency();
std::cout << "Starting " << num_threads << " number of threads." << std::endl;
std::vector<std::thread> threads{};
auto start = std::chrono::high_resolution_clock::now();
std::vector<Region> regions{};
const size_t num_rows = ny / num_threads;
std::cout << " - Number of rows per thread: " << num_rows << std::endl;
for (size_t i = 0; i < size_t(ny / num_rows); i++) {
regions.emplace_back(Region{nx, ny, 0, nx, i * num_rows, (i + 1) * num_rows});
}
Semaphore sem{0};
Semaphore sem_done{0};
sem.post(regions.size());
for (size_t i = 0; i < num_threads; i++) {
ThreadArgs args{world, cam, pixels, ns, regions, sem, sem_done};
threads.emplace_back(std::thread{thread_work, args});
}
bool quit = false;
bool finished = false;
while (!quit) {
SDL_Event event{};
while (SDL_PollEvent(&event)) {
if (event.key.keysym.sym == SDLK_ESCAPE || event.type == SDL_QUIT) {
quit = true;
break;
}
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_a:
// TODO: Camera movemments
break;
}
break;
}
}
SDL_UpdateWindowSurface(window);
if (sem_done.get_value() == num_threads && !finished) {
finished = true;
auto end = std::chrono::high_resolution_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << " - " << diff << " ms, " << (ns*nx*ny)/(diff/1000.0)/1'000'000 << " Mrays/s" << std::endl;
}
}
// FIXME: No way to async terminate a thread (except via pthread APIs).
for (auto& t : threads) { t.join(); }
return EXIT_SUCCESS;
}