implenment a very simple rasterizer.
This commit is contained in:
parent
94759bb51f
commit
d79d9ac1b5
|
@ -1,6 +1,8 @@
|
||||||
cmake_minimum_required(VERSION 3.9)
|
cmake_minimum_required(VERSION 3.9)
|
||||||
PROJECT(minus_renderer)
|
PROJECT(minus_renderer)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
find_package(OpenCV REQUIRED core imgproc)
|
find_package(OpenCV REQUIRED core imgproc)
|
||||||
|
|
||||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2024 Bytedance Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <opencv2/core/core.hpp>
|
||||||
|
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
using Vector3d = Eigen::Vector3d;
|
||||||
|
using Point2d = Eigen::Vector2d;
|
||||||
|
using Point3d = Eigen::Vector3d;
|
||||||
|
using BBox = cv::Rect2i;
|
||||||
|
using PixelProperty = Eigen::Vector4d;
|
|
@ -9,19 +9,18 @@
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/fmt/ostr.h" // github.com/gabime/spdlog/issues/1638
|
#include "spdlog/fmt/ostr.h" // github.com/gabime/spdlog/issues/1638
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
#include "rasterizer.h"
|
#include "rasterizer.h"
|
||||||
#include "triangle.h"
|
#include "triangle.h"
|
||||||
|
|
||||||
using namespace Eigen;
|
using namespace Eigen;
|
||||||
using namespace cv;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
const int width = 1280;
|
const int width = 1280;
|
||||||
const int height = 720;
|
const int height = 720;
|
||||||
|
|
||||||
Triangle t{Triangle::PointType{1, 1, 0}, Triangle::PointType{3, 1, 0},
|
Triangle t{Point3d{1, 1, -2}, Point3d{3, 1, -2}, Point3d{2, 4, -2}};
|
||||||
Triangle::PointType{2, 4, 0}};
|
|
||||||
|
|
||||||
Rasterizer rasterizer{width, height};
|
Rasterizer rasterizer{width, height};
|
||||||
rasterizer.rasterize(t);
|
rasterizer.rasterize(t);
|
||||||
|
|
|
@ -6,9 +6,12 @@
|
||||||
|
|
||||||
Rasterizer::Rasterizer(const int width, const int height)
|
Rasterizer::Rasterizer(const int width, const int height)
|
||||||
: width_(width), height_(height),
|
: width_(width), height_(height),
|
||||||
pixels_(std::vector<std::vector<int>>(
|
pixels_(std::vector<std::vector<PixelProperty>>(
|
||||||
height,
|
height,
|
||||||
std::vector<int>(width, std::numeric_limits<int>::infinity()))) {}
|
std::vector<PixelProperty>(
|
||||||
|
width, PixelProperty{0., 0., 0.,
|
||||||
|
std::numeric_limits<double>::infinity()}))) {
|
||||||
|
}
|
||||||
|
|
||||||
void Rasterizer::rasterize(const Triangle &t) {
|
void Rasterizer::rasterize(const Triangle &t) {
|
||||||
const auto &aabb = t.axis_align_bbox();
|
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 i = x_min; i < x_max; ++i) {
|
||||||
for (int j = y_min; j < y_max; ++j) {
|
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<bool, double> 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};
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@
|
||||||
|
|
||||||
class Rasterizer {
|
class Rasterizer {
|
||||||
public:
|
public:
|
||||||
using RasterizeRange = cv::Rect2i;
|
typedef enum RasterizeMode {
|
||||||
|
Fill,
|
||||||
|
Line,
|
||||||
|
} RasterizeMode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Rasterizer(const int width, const int height);
|
Rasterizer(const int width, const int height);
|
||||||
|
@ -17,8 +20,14 @@ public:
|
||||||
public:
|
public:
|
||||||
void rasterize(const Triangle &t);
|
void rasterize(const Triangle &t);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::pair<bool, double> inside(const Point2d &p_screen,
|
||||||
|
const Triangle &t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
std::vector<std::vector<int>> pixels_;
|
std::vector<std::vector<PixelProperty>> pixels_;
|
||||||
|
|
||||||
|
RasterizeMode mode_ = RasterizeMode::Fill;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,37 +4,34 @@
|
||||||
#include "triangle.h"
|
#include "triangle.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
Triangle::Triangle(const PointType &a, const PointType &b, const PointType &c)
|
Triangle::Triangle(const Point3d &a, const Point3d &b, const Point3d &c)
|
||||||
: points_({HomoPointType{a[0], a[1], a[2], 1},
|
: points_({a, b, c}) {}
|
||||||
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 HomoPointType &a, const HomoPointType &b,
|
Triangle::Triangle(const HomoPointType &a, const HomoPointType &b,
|
||||||
const HomoPointType &c)
|
const HomoPointType &c)
|
||||||
: points_({a, b, c}),
|
: points_({(a / a.z()).head(3), (b / b.z()).head(3), (c / c.z()).head(3)}) {
|
||||||
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)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
void Triangle::affine_transform(const AffineTransformType &m) {
|
||||||
for (auto &p : points_) {
|
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) {
|
void Triangle::projective_transform(const ProjectiveTransformType &m) {
|
||||||
for (auto &p : points_) {
|
for (auto &p : points_) {
|
||||||
p = m * p;
|
p = (m * Eigen::Vector4d(p.x(), p.y(), p.z(), 1)).head(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,38 +3,40 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
#include "common.h"
|
||||||
#include <Eigen/Geometry>
|
#include <Eigen/Geometry>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <opencv2/core/core.hpp>
|
|
||||||
|
|
||||||
class Triangle {
|
class Triangle {
|
||||||
public:
|
public:
|
||||||
using AffineTransformType = Eigen::Transform<double, 3, Eigen::Affine>;
|
using AffineTransformType = Eigen::Transform<double, 3, Eigen::Affine>;
|
||||||
using ProjectiveTransformType =
|
using ProjectiveTransformType =
|
||||||
Eigen::Transform<double, 3, Eigen::Projective>;
|
Eigen::Transform<double, 3, Eigen::Projective>;
|
||||||
using PointType = Eigen::Vector3d;
|
|
||||||
using BBoxType = cv::Rect2i;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using HomoPointType = Eigen::Vector4d;
|
using HomoPointType = Eigen::Vector4d;
|
||||||
|
|
||||||
public:
|
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,
|
Triangle(const HomoPointType &a, const HomoPointType &b,
|
||||||
const HomoPointType &c);
|
const HomoPointType &c);
|
||||||
|
|
||||||
|
template <int size>
|
||||||
|
std::array<Eigen::Vector<double, size>, 3> get_points() const {
|
||||||
|
return std::array<Eigen::Vector<double, size>, 3>{
|
||||||
|
(points_[0]).head(size),
|
||||||
|
(points_[1]).head(size),
|
||||||
|
(points_[2]).head(size),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BBoxType axis_align_bbox() const;
|
BBox axis_align_bbox() const;
|
||||||
|
Vector3d normal_vector() const;
|
||||||
|
|
||||||
void affine_transform(const AffineTransformType &m);
|
void affine_transform(const AffineTransformType &m);
|
||||||
void projective_transform(const ProjectiveTransformType &m);
|
void projective_transform(const ProjectiveTransformType &m);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static BBoxType calculate_aabb(const HomoPointType &a, const HomoPointType &b,
|
std::array<Point3d, 3> points_;
|
||||||
const HomoPointType &c);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<HomoPointType, 3> points_;
|
|
||||||
BBoxType aabb_;
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue