From a063b29d07f2d8c6e0cec1def9391bb60f0f221a Mon Sep 17 00:00:00 2001 From: "tianlei.richard" Date: Wed, 6 Mar 2024 17:21:53 +0800 Subject: [PATCH] invole transformation to triangle primitives. --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 1 + src/main.cc | 44 +++++++++++++++++++++++++++++ src/minus_renderer.cc | 66 ++++++++++++++++++++++++++++--------------- src/minus_renderer.h | 34 ++++++++++++++++++++++ src/rasterizer.cc | 53 +++++++++++++++++++++------------- src/rasterizer.h | 3 +- src/triangle.cc | 7 +++-- src/triangle.h | 2 +- 9 files changed, 165 insertions(+), 47 deletions(-) create mode 100644 src/main.cc create mode 100644 src/minus_renderer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bda8646..b5bf8ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ PROJECT(minus_renderer) set(CMAKE_CXX_STANDARD 17) -find_package(OpenCV REQUIRED core imgproc) +find_package(OpenCV REQUIRED core imgproc highgui) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 63759f0..ed5fdb7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,4 +4,5 @@ PROJECT(renderer) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} CPP_FILES) add_executable(${CMAKE_PROJECT_NAME} ${CPP_FILES}) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS}) target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC spdlog eigen ${OpenCV_LIBS}) diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..0092b60 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,44 @@ +// Copyright 2024 Bytedance Inc. All Rights Reserved. +// Author: tianlei.richard@bytedance.com (tianlei.richard) + +#include +#include + +#include "spdlog/spdlog.h" + +#include "common.h" +#include "minus_renderer.h" +#include "triangle.h" + +int main(int argc, char *argv[]) { + spdlog::set_level(spdlog::level::trace); + + const int resolution_width = 320; + const int resolution_height = 180; + const int far = -1000; + const int near = -10; + const float fov = 120. / 360. * M_PI; + const float aspect_ratio = static_cast(resolution_width) / + static_cast(resolution_height); + + std::vector primitives{ + {Point3d{90, 45, -2}, Point3d{180, 135, -2}, Point3d{270, 45, -2}}}; + + cv::namedWindow("MinusRenderer", cv::WINDOW_AUTOSIZE); + const auto start = std::chrono::steady_clock::now(); + MinusRenderer renderer{near, far, fov, aspect_ratio, std::move(primitives)}; + do { + const auto elapse = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start) + .count(); + renderer.model_transform( + Eigen::AngleAxisd(0.125 * M_PI, Eigen::Vector3d::UnitY()) + .toRotationMatrix()); + const auto &color_image = + renderer.render(resolution_width, resolution_height); + cv::imshow("MinusRenderer", color_image); + } while (cv::waitKey(50) == -1); + cv::destroyWindow("MinusRenderer"); + + return 0; +} \ No newline at end of file diff --git a/src/minus_renderer.cc b/src/minus_renderer.cc index 2336dff..1b92e93 100644 --- a/src/minus_renderer.cc +++ b/src/minus_renderer.cc @@ -1,29 +1,51 @@ // Copyright 2024 Bytedance Inc. All Rights Reserved. // Author: tianlei.richard@qq.com (tianlei.richard) -#include - -#include -#include - -#include "spdlog/spdlog.h" -#include "spdlog/fmt/ostr.h" // github.com/gabime/spdlog/issues/1638 - -#include "common.h" +#include "minus_renderer.h" #include "rasterizer.h" -#include "triangle.h" +#include "spdlog/spdlog.h" +#include -using namespace Eigen; +MinusRenderer::MinusRenderer(float near, float far, float fov, + float aspect_ratio, + std::vector &&primitives) + : near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio), + height_(calculate_height(fov, near)), + width_(calculate_width(height_, aspect_ratio)), primitives_(primitives) {} -int main(int argc, char *argv[]) { - - const int width = 1280; - const int height = 720; - - Triangle t{Point3d{1, 1, -2}, Point3d{3, 1, -2}, Point3d{2, 4, -2}}; - - Rasterizer rasterizer{width, height}; - rasterizer.rasterize(t); - - return 0; +int MinusRenderer::calculate_height(const float fov, const float near) { + return std::fabs(near) * std::tan(fov * 0.5) * 2; +} + +int MinusRenderer::calculate_width(const float height, const float ratio) { + return height * ratio; +} + +void MinusRenderer::model_transform(const Triangle::AffineTransformType &m) { + for (auto &t : primitives_) { + t.affine_transform(m); + } +} + +cv::Mat MinusRenderer::render(const int resolution_width, + const int resolution_height) { + Rasterizer rasterizer{resolution_width, resolution_height}; + const auto &pixels = rasterizer.rasterize(primitives_); + assert(!pixels.empty()); + cv::Mat color_image(pixels.size(), (pixels[0]).size(), CV_8UC3); + + for (int i = 0; i < pixels.size(); ++i) { + const auto &row = pixels[i]; + for (int j = 0; j < row.size(); ++j) { + auto &pixel_color = color_image.at(pixels.size() - i - 1, j); + pixel_color = + cv::Vec3b{static_cast( + row[j].x() * std::numeric_limits::max()), + static_cast( + row[j].y() * std::numeric_limits::max()), + static_cast( + row[j].z() * std::numeric_limits::max())}; + } + } + return color_image; } diff --git a/src/minus_renderer.h b/src/minus_renderer.h new file mode 100644 index 0000000..313b2e7 --- /dev/null +++ b/src/minus_renderer.h @@ -0,0 +1,34 @@ +// Copyright 2024 Bytedance Inc. All Rights Reserved. +// Author: tianlei.richard@bytedance.com (tianlei.richard) + +#pragma once + +#include "triangle.h" +#include + +class MinusRenderer { +public: + MinusRenderer(float near, float far, float fov, float aspect_ratio, + std::vector &&primitives); + +public: + cv::Mat render(const int resolution_width, const int resolution_height); + +public: + void model_transform(const Triangle::AffineTransformType &m); + +private: + static int calculate_height(const float fov, const float near); + static int calculate_width(const float height, const float ratio); + +private: + float near_; + float far_; + float fov_; // In radian + float aspect_ratio_; + + int height_; + int width_; + + std::vector primitives_; +}; diff --git a/src/rasterizer.cc b/src/rasterizer.cc index 1dd5fe6..6e29ef9 100644 --- a/src/rasterizer.cc +++ b/src/rasterizer.cc @@ -2,36 +2,45 @@ // Author: tianlei.richard@qq.com (tianlei.richard) #include "rasterizer.h" +#include "spdlog/fmt/ostr.h" // github.com/gabime/spdlog/issues/1638 +#include "spdlog/spdlog.h" #include Rasterizer::Rasterizer(const int width, const int height) : width_(width), height_(height), pixels_(std::vector>( - height, - std::vector( - width, PixelProperty{0., 0., 0., - std::numeric_limits::infinity()}))) { -} + height, std::vector( + width, PixelProperty{ + 0., 0., 0., + -std::numeric_limits::infinity()}))) {} -void Rasterizer::rasterize(const Triangle &t) { - const auto &aabb = t.axis_align_bbox(); - const int x_min = aabb.x; - const int x_max = aabb.x + aabb.width; - const int y_min = aabb.y; - const int y_max = aabb.y + aabb.height; +std::vector> +Rasterizer::rasterize(const std::vector &primitives) { + for (const auto &t : primitives) { + const auto &aabb = t.axis_align_bbox(); + const int x_min = std::min(std::max(0, aabb.x), width_); + const int x_max = std::min(std::max(0, aabb.x + aabb.width), width_); + const int y_min = std::min(std::max(0, aabb.y), height_); + const int y_max = std::min(std::max(0, aabb.y + aabb.height), height_); - 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; + spdlog::info("Triangle range, ({},{},{},{})", x_min, x_max, y_min, y_max); + for (int i = y_min; i < y_max; ++i) { + for (int j = x_min; j < x_max; ++j) { + auto &property = pixels_[i][j]; + const auto &[is_inside, z_screen] = + inside(Point2d{j + 0.5, i + 0.5}, t); + // spdlog::debug("is_inside: {}, current depth: {}, closest depth: {}", + // is_inside, z_screen, property[3]); + if (is_inside && z_screen > property[3]) { + property[0] = 0; + property[1] = 1; + property[2] = 0; + property[3] = z_screen; + } } } } + return pixels_; } std::pair Rasterizer::inside(const Point2d &p_screen, @@ -39,6 +48,9 @@ std::pair Rasterizer::inside(const Point2d &p_screen, const auto points = t.get_points<3>(); const auto plane_normal = t.normal_vector(); + // spdlog::debug("normal vector: ({},{},{})", plane_normal.x(), + // plane_normal.y(), + // plane_normal.z()); const auto plane_point = points[0]; const auto z_screen = plane_point.z() - (plane_normal.x() * (p_screen.x() - plane_point.x()) + @@ -58,5 +70,6 @@ std::pair Rasterizer::inside(const Point2d &p_screen, const auto r2 = c1.dot(c3); const auto r3 = c2.dot(c3); + // spdlog::debug("r1: {}, r2: {}, r3: {}", r1, r2, r3); return {r1 > 0 && r2 > 0 && r3 > 0, z_screen}; } diff --git a/src/rasterizer.h b/src/rasterizer.h index 80e5a4e..ebc21e4 100644 --- a/src/rasterizer.h +++ b/src/rasterizer.h @@ -18,7 +18,8 @@ public: Rasterizer(const int width, const int height); public: - void rasterize(const Triangle &t); + std::vector> + rasterize(const std::vector &primitives); private: static std::pair inside(const Point2d &p_screen, diff --git a/src/triangle.cc b/src/triangle.cc index 68c3f3b..2227d6c 100644 --- a/src/triangle.cc +++ b/src/triangle.cc @@ -21,12 +21,15 @@ BBox Triangle::axis_align_bbox() const { } Vector3d Triangle::normal_vector() const { - return points_[0].cross(points_[1]).normalized(); + const auto v1 = points_[1] - points_[0]; + const auto v2 = points_[2] - points_[1]; + return v1.cross(v2).normalized(); } void Triangle::affine_transform(const AffineTransformType &m) { + Eigen::Transform affine_matrix(m); for (auto &p : points_) { - p = (m * Eigen::Vector4d(p.x(), p.y(), p.z(), 1)).head(3); + p = (affine_matrix * Eigen::Vector4d(p.x(), p.y(), p.z(), 1)).head(3); } } diff --git a/src/triangle.h b/src/triangle.h index 75648cb..2fd577c 100644 --- a/src/triangle.h +++ b/src/triangle.h @@ -9,7 +9,7 @@ class Triangle { public: - using AffineTransformType = Eigen::Transform; + using AffineTransformType = Eigen::Matrix; using ProjectiveTransformType = Eigen::Transform;