From 9b70ca7b3a1b7bfd3bf434d8b84bde42f5572ca0 Mon Sep 17 00:00:00 2001 From: vimene Date: Tue, 5 Dec 2023 10:42:35 +0100 Subject: added renderer, improved various things - Added renderer to unify frame buffers - Added FrameBuffer class as a parent for frame buffers' classes - Improved formatting in Makefile.am - Added const modifier in Matrix4's methods --- src/renderer.cpp | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/renderer.cpp (limited to 'src/renderer.cpp') diff --git a/src/renderer.cpp b/src/renderer.cpp new file mode 100644 index 0000000..a946ce3 --- /dev/null +++ b/src/renderer.cpp @@ -0,0 +1,145 @@ +#include "renderer.h" +#include +#include +#include +#include +#include "math/vector.h" +#include "o3d/vertex.h" +#include "o3d/tri_vertex.h" +#include "o3d/vertex_data.h" + +using namespace engine; + +Renderer::Renderer(std::unique_ptr fb) : fb{std::move(fb)} { + depth_buf.resize(this->fb->width() * this->fb->height()); +} + +void Renderer::resize(unsigned int w, unsigned int h) { + fb->resize(w, h); + depth_buf.resize(fb->width() * fb->height()); +} + +unsigned int Renderer::width() const { + return fb->width(); +} + +unsigned int Renderer::height() const { + return fb->height(); +} + +void Renderer::clear() { + std::fill(depth_buf.begin(), depth_buf.end(), 1.f); + face_ind = -1; + fb->clear(); +} + +void Renderer::_draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle) { + std::array sorted_vs = { &triangle.vertex1, &triangle.vertex2, &triangle.vertex3 }; + +#define SWAP_IF_LT(X,Y) ({\ + if (sorted_vs[X]->point.y < sorted_vs[Y]->point.y) {\ + engine::o3d::Vertex3* temp = sorted_vs[X];\ + sorted_vs[X] = sorted_vs[Y];\ + sorted_vs[Y] = temp;\ + }\ + }) + SWAP_IF_LT(1, 0); + SWAP_IF_LT(2, 1); + SWAP_IF_LT(1, 0); +#undef SWAP_IF_LT + + engine::o3d::Vertex3 middle_vl = *sorted_vs[1]; + float fac = (sorted_vs[1]->point.y - sorted_vs[0]->point.y) / (sorted_vs[2]->point.y - sorted_vs[0]->point.y); + engine::o3d::Vertex3 middle_vr{ + { + sorted_vs[0]->point.x + fac * (sorted_vs[2]->point.x - sorted_vs[0]->point.x), + sorted_vs[1]->point.y, + sorted_vs[0]->point.z + fac * (sorted_vs[2]->point.z - sorted_vs[0]->point.z) + }, + engine::o3d::VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac) + }; + if (middle_vr.point.x < middle_vl.point.x) { + engine::o3d::Vertex3 temp = middle_vr; + middle_vr = middle_vl; + middle_vl = temp; + } + + // top triangle + { + if (sorted_vs[0]->point.y != sorted_vs[1]->point.y) { + int top_y = static_cast(std::floor(sorted_vs[0]->point.y + 1.f)); + int bottom_y = static_cast(std::ceil(sorted_vs[1]->point.y - 1.f)); + for (int y = top_y; y <= bottom_y; y++) { + float iy = static_cast(y); + float s = (iy - sorted_vs[0]->point.y) / (sorted_vs[1]->point.y - sorted_vs[0]->point.y); + float xl = sorted_vs[0]->point.x + s * (middle_vl.point.x - sorted_vs[0]->point.x); + float xr = sorted_vs[0]->point.x + s * (middle_vr.point.x - sorted_vs[0]->point.x); + int left_x = static_cast(std::ceil(xl)); + int right_x = static_cast(std::ceil(xr - 1.f)); + for (int x = left_x; x <= right_x; x++) { + float ix = static_cast(x); + float t = (ix - xl) / (xr - xl); + // depth and vd don't take into account perspective + float u = s, v = t; + float depth = sorted_vs[0]->point.z + u * (middle_vl.point.z - sorted_vs[0]->point.z + v * (middle_vr.point.z - middle_vl.point.z)); + if (depth < depth_buf[x + y * fb->width()]) { + depth_buf[x + y * fb->width()] = depth; + fb->draw_point(x, y, engine::math::Vector3{static_cast(face_ind) + .5f, 0.f, 0.f}, + engine::o3d::VertexData::bilerp(sorted_vs[0]->data, middle_vl.data, middle_vr.data, u, v)); + } + } + } + } + } + + // bottom triangle + { + if (sorted_vs[1]->point.y != sorted_vs[2]->point.y) { + int bottom_y = static_cast(std::floor(sorted_vs[2]->point.y)); + int top_y = static_cast(std::ceil(sorted_vs[1]->point.y)); + for (int y = top_y; y <= bottom_y; y++) { + float iy = static_cast(y); + float s = (iy - sorted_vs[2]->point.y) / (sorted_vs[1]->point.y - sorted_vs[2]->point.y); + float xl = sorted_vs[2]->point.x + s * (middle_vl.point.x - sorted_vs[2]->point.x); + float xr = sorted_vs[2]->point.x + s * (middle_vr.point.x - sorted_vs[2]->point.x); + int left_x = static_cast(std::ceil(xl)); + int right_x = static_cast(std::ceil(xr - 1.f)); + for (int x = left_x; x <= right_x; x++) { + float ix = static_cast(x); + float t = (ix - xl) / (xr - xl); + // depth and vd don't take into account perspective + float u = s, v = t; + float depth = sorted_vs[2]->point.z + u * (middle_vl.point.z - sorted_vs[2]->point.z + v * (middle_vr.point.z - middle_vl.point.z)); + if (depth < depth_buf[x + y * fb->width()]) { + depth_buf[x + y * fb->width()] = depth; + fb->draw_point(x, y, engine::math::Vector3{static_cast(face_ind) + .5f, 0.f, 0.f}, + engine::o3d::VertexData::bilerp(sorted_vs[2]->data, middle_vl.data, middle_vr.data, u, v)); + } + } + } + } + } +} + +void Renderer::draw_triangle(engine::o3d::TriangleVertex4 triangle) { + face_ind++; + for (auto t1 : triangle.crop_z_out(-1.f, 1.f)) { + for (auto t2 : t1.div_by_w().crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { + engine::math::Vector2 pp1 = t2.vertex1.point.xy(), + pp2 = t2.vertex2.point.xy(), + pp3 = t2.vertex3.point.xy(); + if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue; + t2.vertex1.point = (t2.vertex1.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; + t2.vertex2.point = (t2.vertex2.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; + t2.vertex3.point = (t2.vertex3.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; + float fw = static_cast(fb->width()), fh = static_cast(fb->height()); + t2.vertex1.point.x = t2.vertex1.point.x * fw - .5f; + t2.vertex1.point.y = t2.vertex1.point.y * fh - .5f; + t2.vertex2.point.x = t2.vertex2.point.x * fw - .5f; + t2.vertex2.point.y = t2.vertex2.point.y * fh - .5f; + t2.vertex3.point.x = t2.vertex3.point.x * fw - .5f; + t2.vertex3.point.y = t2.vertex3.point.y * fh - .5f; + _draw_cropped_triangle(t2); + } + } +} -- cgit v1.2.3