-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrasterizer.hpp
308 lines (246 loc) · 11.1 KB
/
rasterizer.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
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#pragma once
#include "shader.hpp"
#include "matrix.hpp"
#include "common_header.hpp"
#include "transforms.hpp"
#include <vector>
#include <iostream>
#include <cmath>
#include <list>
template <typename T>
T min3(const T& t1, const T& t2, const T& t3) {
return std::min(std::min(t1, t2), t3);
}
template <typename T>
T max3(const T& t1, const T& t2, const T& t3) {
return std::max(std::max(t1, t2), t3);
}
class ObjectDescriptor {
public:
std::shared_ptr<AbstractObject> object;
Mat3 dir; // 3 x 3, [inWorld(objX) inWorld(objY) inWorld(objZ)]
Vec3 pos; // 3 x 1
};
bool to_left(const Vec2& a, const Vec2& b) {
return a[0] * b[1] - b[0] * a[1] > 0;
}
bool in_triangle(const Vec2& pt, const Vec2& v1, const Vec2& v2, const Vec2& v3) {
auto e1 = v1 - v2;
auto p1 = pt - v2;
auto e2 = v2 - v3;
auto p2 = pt - v3;
auto e3 = v3 - v1;
auto p3 = pt - v1;
auto r1 = to_left(e1, p1);
auto r2 = to_left(e2, p2);
auto r3 = to_left(e3, p3);
return r1 == r2 && r2 == r3;
}
std::tuple<float, float, float> bary_centric(
const Vec3& center,
const Vec3& v1,
const Vec3& v2,
const Vec3& v3
) {
auto area1 = cross_product(center - v2, center - v3).norm2();
auto area2 = cross_product(center - v3, center - v1).norm2();
auto area3 = cross_product(center - v1, center - v2).norm2();
auto area_sum = area1 + area2 + area3;
float k1 = area1 / area_sum;
float k2 = area2 / area_sum;
float k3 = area3 / area_sum;
return {k1, k2, k3};
}
template <typename T>
std::vector<std::vector<T>> matrix_of_size(int n_rows, int n_cols, const T& default_value) {
return std::vector<std::vector<T>>(n_rows, std::vector<T>(n_cols, default_value));
}
float deg_to_rad(float deg) {
return deg / 180 * acos(-1.0);
}
// Object<Uniform, Attribute, Property, Shader>
// VertexData<Attribute> --- VertexShader<Attribute, Uniform> --> Vertex<Property> ---
// --- Fragment<Vertex> --> RGBAColor
template<typename Uniform>
class Rasterizer {
static constexpr int POOL_SIZE = 1024 * 1024 * 4; // 4MB per pool
std::list<void*> fragment_pools; // 这里,没有重复利用!
decltype(fragment_pools.begin()) pool_it = fragment_pools.begin(); // TODO: 顺序
size_t mem_index = 0;
void reset_mem() {
pool_it = fragment_pools.begin();
mem_index = 0;
}
void* alloc_mem(size_t size) {
if(size > POOL_SIZE) {
throw "fragment size is too large.";
}
if(pool_it != fragment_pools.end() && mem_index + size > POOL_SIZE) {
pool_it++;
mem_index = 0;
}
// 如果当前有 pool,那么空间一定足够
if(pool_it == fragment_pools.end()) {
std::cerr << "pool: malloc" << std::endl;
pool_it = fragment_pools.insert(pool_it, malloc(POOL_SIZE));
mem_index = 0;
}
// 那么当前一定有 pool
void* out = (void*)((char*)*pool_it + mem_index);
mem_index += size;
return out;
}
std::vector<ObjectDescriptor> objects;
public:
Uniform uniform;
Rasterizer() = default;
Rasterizer(const Rasterizer& r): objects(r.objects), uniform(r.uniform) {}
Rasterizer(Rasterizer&& r): objects(std::move(r.objects)), uniform(std::move(r.uniform)) {}
Rasterizer& operator=(const Rasterizer& r) {
objects = r.objects;
uniform = r.uniform;
return *this;
}
~Rasterizer() {
for(auto ptr: fragment_pools) {
free(ptr);
ptr = nullptr;
}
}
template<typename T, typename P, typename VShaderT, typename FShaderT>
Rasterizer& addObject(
const Object<T, P, Uniform, VShaderT, FShaderT>& obj,
const Mat3& dir,
const Vec3& pos
) {
objects.push_back(ObjectDescriptor{std::make_shared<Object<T, P, Uniform, VShaderT, FShaderT>>(obj), dir, pos});
return *this;
}
Rasterizer& clearObjects() {
objects.clear();
return *this;
}
Image rasterize(
const Vec3& camera_pos,
const Vec3& camera_dir,
const Vec3& camera_top,
float near, // should be positive
float far, // should be positive
float field_of_view_y, // 和渲染结果里面的范围有关
float aspect_ratio,
int width, // 和实际的图片大小有关
int height // width / height == aspect ratio should hold
) /* const cast here */ {
auto h = 2 * tan(field_of_view_y / 2) * near;
auto w = aspect_ratio * h;
Mat4 S {
{ (float)(2.0/w), 0, 0, 0 },
{ 0, (float)(2.0/h), 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 }
};
auto V = view_transform(camera_pos, camera_dir, camera_top);
auto P = projection_transform(near, far);
auto SPV = S * P * V;
auto f_buffer = matrix_of_size<std::pair<const AbstractFragment*, const AbstractFShader*>>(width, height, std::make_pair(nullptr, nullptr));
auto d_buffer = matrix_of_size<float>(width, height, far + 1); // TODO: -inf
RasterizerInfo info;
info.V = V;
info.P = S * P;
info.camera_dir = camera_dir;
info.camera_top = camera_top;
info.camera_pos = camera_pos;
for(auto desp: objects) {
auto& pObj = desp.object;
auto& vShader = pObj->getVShader();
auto& fShader = pObj->getFShader();
auto M = model_transform(desp.pos, desp.dir);
info.M = M;
for(auto [i1, i2, i3]: pObj->triangles) {
const int VertexSize = pObj->getVShader().vertexSize();
char* mem = (char*) alloc_mem (VertexSize * 3);
auto& v1 = vShader.shade(pObj->getVertexData(i1), uniform, info, mem + 0 * VertexSize);
auto& v2 = vShader.shade(pObj->getVertexData(i2), uniform, info, mem + 1 * VertexSize);
auto& v3 = vShader.shade(pObj->getVertexData(i3), uniform, info, mem + 2 * VertexSize);
auto pos1_world_vec4 = M * to_vec4_as_pos(v1.pos_model);
auto pos2_world_vec4 = M * to_vec4_as_pos(v2.pos_model);
auto pos3_world_vec4 = M * to_vec4_as_pos(v3.pos_model);
auto pos1_screen_vec4 = SPV * pos1_world_vec4;
auto pos2_screen_vec4 = SPV * pos2_world_vec4;
auto pos3_screen_vec4 = SPV * pos3_world_vec4;
auto pos1_screen_vec3 = to_vec3_as_pos(pos1_screen_vec4);
auto pos2_screen_vec3 = to_vec3_as_pos(pos2_screen_vec4);
auto pos3_screen_vec3 = to_vec3_as_pos(pos3_screen_vec4);
float fragment_width = 2.0 / width;
float fragment_height = 2.0 / height;
int x_min = (min3(pos1_screen_vec3[0], pos2_screen_vec3[0], pos3_screen_vec3[0]) + 1) / fragment_width - 0.5 - 1;
int x_max = (max3(pos1_screen_vec3[0], pos2_screen_vec3[0], pos3_screen_vec3[0]) + 1) / fragment_width - 0.5 + 2;
int y_min = (min3(pos1_screen_vec3[1], pos2_screen_vec3[1], pos3_screen_vec3[1]) + 1) / fragment_height - 0.5 - 1;
int y_max = (max3(pos1_screen_vec3[1], pos2_screen_vec3[1], pos3_screen_vec3[1]) + 1) / fragment_height - 0.5 + 2;
for(int x_index = std::max(0, x_min); x_index < std::min(width, x_max); x_index++) {
for(int y_index = std::max(0, y_min); y_index < std::min(height, y_max); y_index++) {
float x = x_index * fragment_width + 0.5 * fragment_width - 1;
float y = y_index * fragment_height + 0.5 * fragment_height - 1;
Vec2 pt {x, y};
Vec2 pos1_screen_vec2 = { pos1_screen_vec3[0], pos1_screen_vec3[1] };
Vec2 pos2_screen_vec2 = { pos2_screen_vec3[0], pos2_screen_vec3[1] };
Vec2 pos3_screen_vec2 = { pos3_screen_vec3[0], pos3_screen_vec3[1] };
if(in_triangle(pt, pos1_screen_vec2, pos2_screen_vec2, pos3_screen_vec2)) {
Vec3 center {x, y, 0};
// 如果我不强制令z=0呢?那就会四点不共面
auto [k1, k2, k3] = bary_centric(
center,
{ pos1_screen_vec2[0], pos1_screen_vec2[1], 0 },
{ pos2_screen_vec2[0], pos2_screen_vec2[1], 0 },
{ pos3_screen_vec2[0], pos3_screen_vec2[1], 0 }
);
float z1 = pos1_screen_vec3[2];
float z2 = pos2_screen_vec3[2];
float z3 = pos3_screen_vec3[2];
float z = k1 * z1 + k2 * z2 + k3 * z3;;
if(z <= far && z >= near && z < d_buffer[x_index][y_index]) {
d_buffer[x_index][y_index] = z;
auto mem = alloc_mem(v1.fragment_size());
auto nk1 = k1 / pos1_screen_vec4[3];
auto nk2 = k2 / pos2_screen_vec4[3];
auto nk3 = k3 / pos3_screen_vec4[3];
auto nksum = nk1 + nk2 + nk3;
nk1 /= nksum;
nk2 /= nksum;
nk3 /= nksum;
auto& fragment = v1.linear_interpolation(
nk2, v2,
nk3, v3,
mem
);
fragment.pos_world = to_vec3_as_pos(pos1_world_vec4) * nk1 +
to_vec3_as_pos(pos2_world_vec4) * nk2 +
to_vec3_as_pos(pos3_world_vec4) * nk3 ;
if (f_buffer[x_index][y_index].first) {
f_buffer[x_index][y_index].first->~AbstractFragment();
}
f_buffer[x_index][y_index].first = &fragment;
f_buffer[x_index][y_index].second = &pObj->getFShader();
}
}
}
}
v1.~AbstractVertex();
v2.~AbstractVertex();
v3.~AbstractVertex();
}
}
reset_mem();
Image image(width, height);
for(int x_index = 0; x_index < width; x_index++) {
for(int y_index = 0; y_index < height; y_index++) {
auto [fragment, shader] = f_buffer[x_index][y_index];
if(fragment != nullptr) {
image.setPixel(x_index, y_index, shader->shade(*fragment, uniform));
fragment->~AbstractFragment();
}
}
}
return image;
}
};