-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expand file tree
/
Copy pathour_gl.cpp
More file actions
52 lines (43 loc) · 3.15 KB
/
our_gl.cpp
File metadata and controls
52 lines (43 loc) · 3.15 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
#include <algorithm>
#include "our_gl.h"
mat<4,4> ModelView, Viewport, Perspective; // "OpenGL" state matrices
std::vector<double> zbuffer; // depth buffer
void lookat(const vec3 eye, const vec3 center, const vec3 up) {
vec3 n = normalized(eye-center);
vec3 l = normalized(cross(up,n));
vec3 m = normalized(cross(n, l));
ModelView = mat<4,4>{{{l.x,l.y,l.z,0}, {m.x,m.y,m.z,0}, {n.x,n.y,n.z,0}, {0,0,0,1}}} *
mat<4,4>{{{1,0,0,-center.x}, {0,1,0,-center.y}, {0,0,1,-center.z}, {0,0,0,1}}};
}
void init_perspective(const double f) {
Perspective = {{{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0, -1/f,1}}};
}
void init_viewport(const int x, const int y, const int w, const int h) {
Viewport = {{{w/2., 0, 0, x+w/2.}, {0, h/2., 0, y+h/2.}, {0,0,1,0}, {0,0,0,1}}};
}
void init_zbuffer(const int width, const int height) {
zbuffer = std::vector(width*height, -1000.);
}
void rasterize(const Triangle &clip, const IShader &shader, TGAImage &framebuffer) {
vec4 ndc[3] = { clip[0]/clip[0].w, clip[1]/clip[1].w, clip[2]/clip[2].w }; // normalized device coordinates
vec2 screen[3] = { (Viewport*ndc[0]).xy(), (Viewport*ndc[1]).xy(), (Viewport*ndc[2]).xy() }; // screen coordinates
mat<3,3> ABC = {{ {screen[0].x, screen[0].y, 1.}, {screen[1].x, screen[1].y, 1.}, {screen[2].x, screen[2].y, 1.} }};
if (ABC.det()<1) return; // backface culling + discarding triangles that cover less than a pixel
auto [bbminx,bbmaxx] = std::minmax({screen[0].x, screen[1].x, screen[2].x}); // bounding box for the triangle
auto [bbminy,bbmaxy] = std::minmax({screen[0].y, screen[1].y, screen[2].y}); // defined by its top left and bottom right corners
#pragma omp parallel for
for (int x=std::max<int>(bbminx, 0); x<=std::min<int>(bbmaxx, framebuffer.width()-1); x++) { // clip the bounding box by the screen
for (int y=std::max<int>(bbminy, 0); y<=std::min<int>(bbmaxy, framebuffer.height()-1); y++) {
vec3 bc_screen = ABC.invert_transpose() * vec3{static_cast<double>(x), static_cast<double>(y), 1.}; // barycentric coordinates of {x,y} w.r.t the triangle
vec3 bc_clip = { bc_screen.x/clip[0].w, bc_screen.y/clip[1].w, bc_screen.z/clip[2].w }; // check https://github.com/ssloy/tinyrenderer/wiki/Technical-difficulties-linear-interpolation-with-perspective-deformations
bc_clip = bc_clip / (bc_clip.x + bc_clip.y + bc_clip.z);
if (bc_screen.x<0 || bc_screen.y<0 || bc_screen.z<0) continue; // negative barycentric coordinate => the pixel is outside the triangle
double z = bc_screen * vec3{ ndc[0].z, ndc[1].z, ndc[2].z }; // linear interpolation of the depth
if (z <= zbuffer[x+y*framebuffer.width()]) continue; // discard fragments that are too deep w.r.t the z-buffer
auto [discard, color] = shader.fragment(bc_clip);
if (discard) continue; // fragment shader can discard current fragment
zbuffer[x+y*framebuffer.width()] = z; // update the z-buffer
framebuffer.set(x, y, color); // update the framebuffer
}
}
}