From d79d9ac1b51a3fd6b89dcf05dab0f595e05096c0 Mon Sep 17 00:00:00 2001 From: "tianlei.richard" Date: Tue, 5 Mar 2024 21:43:08 +0800 Subject: [PATCH] implenment a very simple rasterizer. --- CMakeLists.txt | 2 ++ src/common.h | 14 ++++++++++++++ src/minus_renderer.cc | 5 ++--- src/rasterizer.cc | 42 ++++++++++++++++++++++++++++++++++++++++-- src/rasterizer.h | 13 +++++++++++-- src/triangle.cc | 35 ++++++++++++++++------------------- src/triangle.h | 26 ++++++++++++++------------ 7 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 src/common.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fd4dc50..bda8646 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.9) PROJECT(minus_renderer) +set(CMAKE_CXX_STANDARD 17) + find_package(OpenCV REQUIRED core imgproc) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..43bfbfa --- /dev/null +++ b/src/common.h @@ -0,0 +1,14 @@ +// Copyright 2024 Bytedance Inc. All Rights Reserved. +// Author: tianlei.richard@qq.com (tianlei.richard) + +#pragma once + +#include + +#include + +using Vector3d = Eigen::Vector3d; +using Point2d = Eigen::Vector2d; +using Point3d = Eigen::Vector3d; +using BBox = cv::Rect2i; +using PixelProperty = Eigen::Vector4d; diff --git a/src/minus_renderer.cc b/src/minus_renderer.cc index 5a0124b..2336dff 100644 --- a/src/minus_renderer.cc +++ b/src/minus_renderer.cc @@ -9,19 +9,18 @@ #include "spdlog/spdlog.h" #include "spdlog/fmt/ostr.h" // github.com/gabime/spdlog/issues/1638 +#include "common.h" #include "rasterizer.h" #include "triangle.h" using namespace Eigen; -using namespace cv; int main(int argc, char *argv[]) { const int width = 1280; const int height = 720; - Triangle t{Triangle::PointType{1, 1, 0}, Triangle::PointType{3, 1, 0}, - Triangle::PointType{2, 4, 0}}; + Triangle t{Point3d{1, 1, -2}, Point3d{3, 1, -2}, Point3d{2, 4, -2}}; Rasterizer rasterizer{width, height}; rasterizer.rasterize(t); diff --git a/src/rasterizer.cc b/src/rasterizer.cc index bdbaff1..1dd5fe6 100644 --- a/src/rasterizer.cc +++ b/src/rasterizer.cc @@ -6,9 +6,12 @@ Rasterizer::Rasterizer(const int width, const int height) : width_(width), height_(height), - pixels_(std::vector>( + pixels_(std::vector>( height, - std::vector(width, std::numeric_limits::infinity()))) {} + std::vector( + width, PixelProperty{0., 0., 0., + std::numeric_limits::infinity()}))) { +} void Rasterizer::rasterize(const Triangle &t) { const auto &aabb = t.axis_align_bbox(); @@ -19,6 +22,41 @@ void Rasterizer::rasterize(const Triangle &t) { for (int i = x_min; i < x_max; ++i) { for (int j = y_min; j < y_max; ++j) { + auto &property = pixels_[j][i]; + const auto &[is_inside, z_screen] = inside(Point2d{i + 0.5, j + 0.5}, t); + if (is_inside && z_screen > property[3]) { + property[0] = 1; + property[1] = 0; + property[2] = 0; + property[3] = z_screen; + } } } } + +std::pair Rasterizer::inside(const Point2d &p_screen, + const Triangle &t) { + const auto points = t.get_points<3>(); + + const auto plane_normal = t.normal_vector(); + const auto plane_point = points[0]; + const auto z_screen = + plane_point.z() - (plane_normal.x() * (p_screen.x() - plane_point.x()) + + plane_normal.y() * (p_screen.y() - plane_point.y())) / + plane_normal.z(); + const Point3d p{p_screen.x(), p_screen.y(), z_screen}; + + const auto v1 = points[1] - points[0]; + const auto v2 = points[2] - points[1]; + const auto v3 = points[0] - points[2]; + + const auto c1 = v1.cross(p - points[0]).normalized(); + const auto c2 = v2.cross(p - points[1]).normalized(); + const auto c3 = v3.cross(p - points[2]).normalized(); + + const auto r1 = c1.dot(c2); + const auto r2 = c1.dot(c3); + const auto r3 = c2.dot(c3); + + return {r1 > 0 && r2 > 0 && r3 > 0, z_screen}; +} diff --git a/src/rasterizer.h b/src/rasterizer.h index 36d3748..80e5a4e 100644 --- a/src/rasterizer.h +++ b/src/rasterizer.h @@ -9,7 +9,10 @@ class Rasterizer { public: - using RasterizeRange = cv::Rect2i; + typedef enum RasterizeMode { + Fill, + Line, + } RasterizeMode; public: Rasterizer(const int width, const int height); @@ -17,8 +20,14 @@ public: public: void rasterize(const Triangle &t); +private: + static std::pair inside(const Point2d &p_screen, + const Triangle &t); + private: int width_; int height_; - std::vector> pixels_; + std::vector> pixels_; + + RasterizeMode mode_ = RasterizeMode::Fill; }; diff --git a/src/triangle.cc b/src/triangle.cc index 1a4a34e..68c3f3b 100644 --- a/src/triangle.cc +++ b/src/triangle.cc @@ -4,37 +4,34 @@ #include "triangle.h" #include -Triangle::Triangle(const PointType &a, const PointType &b, const PointType &c) - : points_({HomoPointType{a[0], a[1], a[2], 1}, - HomoPointType{b[0], b[1], b[2], 1}, - HomoPointType{c[0], c[1], c[2], 1}}), - aabb_(calculate_aabb(points_[0], points_[1], points_[2])) {} +Triangle::Triangle(const Point3d &a, const Point3d &b, const Point3d &c) + : points_({a, b, c}) {} Triangle::Triangle(const HomoPointType &a, const HomoPointType &b, const HomoPointType &c) - : points_({a, b, c}), - aabb_(calculate_aabb(points_[0], points_[1], points_[2])) {} - -Triangle::BBoxType Triangle::calculate_aabb(const HomoPointType &a, - const HomoPointType &b, - const HomoPointType &c) { - const int x_min = std::min({a.x(), b.x(), c.x()}); - const int y_min = std::min({a.y(), b.y(), c.y()}); - const int x_max = std::max({a.x(), b.x(), c.x()}); - const int y_max = std::max({a.y(), b.y(), c.y()}); - return BBoxType{x_min, y_min, (x_max - x_min), (y_max - y_min)}; + : points_({(a / a.z()).head(3), (b / b.z()).head(3), (c / c.z()).head(3)}) { } -Triangle::BBoxType Triangle::axis_align_bbox() const { return aabb_; } +BBox Triangle::axis_align_bbox() const { + const int x_min = std::min({points_[0].x(), points_[1].x(), points_[2].x()}); + const int y_min = std::min({points_[0].y(), points_[1].y(), points_[2].y()}); + const int x_max = std::max({points_[0].x(), points_[1].x(), points_[2].x()}); + const int y_max = std::max({points_[0].y(), points_[1].y(), points_[2].y()}); + return BBox{x_min, y_min, (x_max - x_min), (y_max - y_min)}; +} + +Vector3d Triangle::normal_vector() const { + return points_[0].cross(points_[1]).normalized(); +} void Triangle::affine_transform(const AffineTransformType &m) { for (auto &p : points_) { - p = m * p; + p = (m * Eigen::Vector4d(p.x(), p.y(), p.z(), 1)).head(3); } } void Triangle::projective_transform(const ProjectiveTransformType &m) { for (auto &p : points_) { - p = m * p; + p = (m * Eigen::Vector4d(p.x(), p.y(), p.z(), 1)).head(3); } } diff --git a/src/triangle.h b/src/triangle.h index 4979790..75648cb 100644 --- a/src/triangle.h +++ b/src/triangle.h @@ -3,38 +3,40 @@ #pragma once -#include +#include "common.h" #include #include -#include class Triangle { public: using AffineTransformType = Eigen::Transform; using ProjectiveTransformType = Eigen::Transform; - using PointType = Eigen::Vector3d; - using BBoxType = cv::Rect2i; private: using HomoPointType = Eigen::Vector4d; public: - Triangle(const PointType &a, const PointType &b, const PointType &c); + Triangle(const Point3d &a, const Point3d &b, const Point3d &c); Triangle(const HomoPointType &a, const HomoPointType &b, const HomoPointType &c); + template + std::array, 3> get_points() const { + return std::array, 3>{ + (points_[0]).head(size), + (points_[1]).head(size), + (points_[2]).head(size), + }; + } + public: - BBoxType axis_align_bbox() const; + BBox axis_align_bbox() const; + Vector3d normal_vector() const; void affine_transform(const AffineTransformType &m); void projective_transform(const ProjectiveTransformType &m); private: - static BBoxType calculate_aabb(const HomoPointType &a, const HomoPointType &b, - const HomoPointType &c); - -private: - std::array points_; - BBoxType aabb_; + std::array points_; };