#include #include "our_gl.h" mat<4,4> ModelView, Viewport, Perspective; // "OpenGL" state matrices std::vector 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(bbminx, 0); x<=std::min(bbmaxx, framebuffer.width()-1); x++) { // clip the bounding box by the screen for (int y=std::max(bbminy, 0); y<=std::min(bbmaxy, framebuffer.height()-1); y++) { vec3 bc_screen = ABC.invert_transpose() * vec3{static_cast(x), static_cast(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 } } }