Create Mesh class to load texture coordinates and per vertex normal.
This commit is contained in:
parent
5a1fadc592
commit
974848ad96
|
@ -4,5 +4,5 @@ PROJECT(renderer)
|
||||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} CPP_FILES)
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} CPP_FILES)
|
||||||
|
|
||||||
add_executable(${CMAKE_PROJECT_NAME} ${CPP_FILES})
|
add_executable(${CMAKE_PROJECT_NAME} ${CPP_FILES})
|
||||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS})
|
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${OpenCV_INCLUDE_DIRS})
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC OBJ_Loader spdlog imgui eigen ${OpenCV_LIBS})
|
target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC OBJ_Loader spdlog imgui eigen ${OpenCV_LIBS})
|
||||||
|
|
10
src/common.h
10
src/common.h
|
@ -11,5 +11,13 @@ using Vector3d = Eigen::Vector3d;
|
||||||
using Point2d = Eigen::Vector2d;
|
using Point2d = Eigen::Vector2d;
|
||||||
using Point3d = Eigen::Vector3d;
|
using Point3d = Eigen::Vector3d;
|
||||||
using BBox = cv::Rect2i;
|
using BBox = cv::Rect2i;
|
||||||
using PixelProperty = Eigen::Vector4d;
|
|
||||||
using TransformMatrix = Eigen::Matrix<double, 4, 4>;
|
using TransformMatrix = Eigen::Matrix<double, 4, 4>;
|
||||||
|
|
||||||
|
typedef struct Vertex {
|
||||||
|
Vertex(const Point3d &p, const Vector3d &n, const Point2d &tex_coor)
|
||||||
|
: position(p), normal(n), texture_coordinate(tex_coor) {}
|
||||||
|
|
||||||
|
Point3d position;
|
||||||
|
Vector3d normal;
|
||||||
|
Point2d texture_coordinate;
|
||||||
|
} Vertex;
|
||||||
|
|
|
@ -84,7 +84,7 @@ int main(int argc, char *argv[]) {
|
||||||
// Setup Dear ImGui style
|
// Setup Dear ImGui style
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
// Setup Platform/Renderer backends
|
// Setup Platform backends
|
||||||
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
||||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
Camera camera{Vector3d{0, 1, 0}};
|
Camera camera{Vector3d{0, 1, 0}};
|
||||||
MinusRenderer renderer{near, far, fov, aspect_ratio};
|
MinusRenderer renderer{near, far, fov, aspect_ratio};
|
||||||
assert(renderer.load_mesh(obj_path));
|
renderer.load_mesh(obj_path);
|
||||||
TransformMatrix translate{
|
TransformMatrix translate{
|
||||||
{1., 0., 0., 3.}, {0., 1., 0., 3.}, {0., 0., 1., -4.}, {0., 0., 0., 1.}};
|
{1., 0., 0., 3.}, {0., 1., 0., 3.}, {0., 0., 1., -4.}, {0., 0., 0., 1.}};
|
||||||
renderer.model_transform(translate);
|
renderer.model_transform(translate);
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Copyright 2024 Bytedance Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright 2024 Bytedance Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
|
#pragma once
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2024 Bytedance Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
|
#include "mesh.h"
|
||||||
|
|
||||||
|
#include "OBJ_Loader.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#define OBJL_VEC2_TO_EIGEN_VECTOR(objl_v) \
|
||||||
|
Eigen::Vector2d { objl_v.X, objl_v.Y }
|
||||||
|
#define OBJL_VEC3_TO_EIGEN_VECTOR(objl_v) \
|
||||||
|
Eigen::Vector3d { objl_v.X, objl_v.Y, objl_v.Z }
|
||||||
|
|
||||||
|
Mesh::Mesh(const std::vector<std::shared_ptr<Vertex>> &vertices,
|
||||||
|
const std::vector<Triangle> &primitives)
|
||||||
|
: vertices_(vertices), primitives_(primitives) {}
|
||||||
|
|
||||||
|
Point2d Mesh::get_texture_coordinate(const unsigned int index) const {
|
||||||
|
return (vertices_[index])->texture_coordinate;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point3d Mesh::get_normal_vector(const unsigned int index) const {
|
||||||
|
return (vertices_[index])->normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Mesh> Mesh::load_mesh(const std::string &file_path) {
|
||||||
|
objl::Loader loader{};
|
||||||
|
assert(loader.LoadFile(file_path.c_str()));
|
||||||
|
|
||||||
|
std::vector<Mesh> res;
|
||||||
|
for (const auto &mesh : loader.LoadedMeshes) {
|
||||||
|
spdlog::info("Current mesh has {} vertices.", mesh.Vertices.size());
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Vertex>> vertices;
|
||||||
|
for (int i = 0; i < mesh.Vertices.size(); i++) {
|
||||||
|
vertices.push_back(std::make_shared<Vertex>(
|
||||||
|
OBJL_VEC3_TO_EIGEN_VECTOR(mesh.Vertices[i].Position),
|
||||||
|
OBJL_VEC3_TO_EIGEN_VECTOR(mesh.Vertices[i].Normal),
|
||||||
|
OBJL_VEC2_TO_EIGEN_VECTOR(mesh.Vertices[i].TextureCoordinate)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Triangle> primitives;
|
||||||
|
for (int i = 0; i < mesh.Indices.size(); i += 3) {
|
||||||
|
primitives.push_back(Triangle(vertices, mesh.Indices[i],
|
||||||
|
mesh.Indices[i + 1], mesh.Indices[i + 2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push_back(Mesh(vertices, primitives));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2024 Bytedance Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "triangle.h"
|
||||||
|
|
||||||
|
class Mesh {
|
||||||
|
public:
|
||||||
|
Mesh(const std::vector<std::shared_ptr<Vertex>> &vertices,
|
||||||
|
const std::vector<Triangle> &primitives);
|
||||||
|
|
||||||
|
static std::vector<Mesh> load_mesh(const std::string &file_path);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Point2d get_texture_coordinate(const unsigned int index) const;
|
||||||
|
Point3d get_normal_vector(const unsigned int index) const;
|
||||||
|
std::vector<Triangle> &get_primitives() { return primitives_; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::shared_ptr<Vertex>> vertices_;
|
||||||
|
std::vector<Triangle> primitives_;
|
||||||
|
};
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "OBJ_Loader.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
#include "minus_renderer.h"
|
#include "minus_renderer.h"
|
||||||
|
@ -12,7 +11,7 @@
|
||||||
MinusRenderer::MinusRenderer(float near, float far, float fov,
|
MinusRenderer::MinusRenderer(float near, float far, float fov,
|
||||||
float aspect_ratio)
|
float aspect_ratio)
|
||||||
: near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio),
|
: near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio),
|
||||||
projection_matrix_(orthographic_tranform() * squish_tranform()) {
|
projection_matrix_(orthographic_transform() * squish_transform()) {
|
||||||
spdlog::info("near: {}, far: {}, fov: {}, aspect ratio: {}", near_, far_,
|
spdlog::info("near: {}, far: {}, fov: {}, aspect ratio: {}", near_, far_,
|
||||||
fov_, aspect_ratio_);
|
fov_, aspect_ratio_);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +24,7 @@ float MinusRenderer::calculate_width(const float height, const float ratio) {
|
||||||
return height * ratio;
|
return height * ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransformMatrix MinusRenderer::squish_tranform() {
|
TransformMatrix MinusRenderer::squish_transform() {
|
||||||
// Frustum to Cuboid
|
// Frustum to Cuboid
|
||||||
return TransformMatrix{{near_, 0, 0, 0},
|
return TransformMatrix{{near_, 0, 0, 0},
|
||||||
{0, near_, 0, 0},
|
{0, near_, 0, 0},
|
||||||
|
@ -33,7 +32,7 @@ TransformMatrix MinusRenderer::squish_tranform() {
|
||||||
{0, 0, 1, 0}};
|
{0, 0, 1, 0}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TransformMatrix MinusRenderer::orthographic_tranform() {
|
TransformMatrix MinusRenderer::orthographic_transform() {
|
||||||
const float height = calculate_height(fov_, near_);
|
const float height = calculate_height(fov_, near_);
|
||||||
const float width = calculate_width(height, aspect_ratio_);
|
const float width = calculate_width(height, aspect_ratio_);
|
||||||
const float right = width * 0.5;
|
const float right = width * 0.5;
|
||||||
|
@ -63,18 +62,34 @@ TransformMatrix MinusRenderer::view_port_transform(const float width,
|
||||||
{0, 0, 0, 1}};
|
{0, 0, 0, 1}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point3d MinusRenderer::calculate_barycentric_coordinate(const Triangle &t,
|
||||||
|
const Point2d &p) {
|
||||||
|
const auto &points = t.get_vertex_position();
|
||||||
|
const auto &A = (points[0]).head(2);
|
||||||
|
const auto &B = (points[1]).head(2);
|
||||||
|
const auto &C = (points[2]).head(2);
|
||||||
|
double alpha =
|
||||||
|
((p.x() - B.x()) * (C.y() - B.y()) + (p.y() - B.y()) * (C.x() - B.x())) /
|
||||||
|
(-(A.x() - B.x()) * (C.y() - B.y()) + (A.y() - B.y()) * (C.x() - B.x()));
|
||||||
|
double beta =
|
||||||
|
(-(p.x() - C.x()) * (A.y() - C.y()) + (p.y() - C.y()) * (A.x() - C.x())) /
|
||||||
|
(-(B.x() - C.x()) * (A.y() - C.y()) + (B.y() - C.y()) * (A.x() - C.x()));
|
||||||
|
double gamma = 1. - alpha - beta;
|
||||||
|
return Point3d{alpha, beta, gamma};
|
||||||
|
}
|
||||||
|
|
||||||
void MinusRenderer::model_transform(const TransformMatrix &mtx) {
|
void MinusRenderer::model_transform(const TransformMatrix &mtx) {
|
||||||
for (auto &m : meshes_) {
|
for (auto &m : meshes_) {
|
||||||
for (auto &p : m) {
|
for (auto &t : m.get_primitives()) {
|
||||||
p.affine_transform(mtx);
|
t.set_points(apply_transform(mtx, t.get_vertex_position()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinusRenderer::view_transform(const TransformMatrix &mtx) {
|
void MinusRenderer::view_transform(const TransformMatrix &mtx) {
|
||||||
for (auto &m : meshes_) {
|
for (auto &m : meshes_) {
|
||||||
for (auto &p : m) {
|
for (auto &t : m.get_primitives()) {
|
||||||
p.affine_transform(mtx);
|
t.set_points(apply_transform(mtx, t.get_vertex_position()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,64 +98,51 @@ cv::Mat MinusRenderer::render(const int resolution_width,
|
||||||
const int resolution_height) {
|
const int resolution_height) {
|
||||||
std::vector<Mesh> meshes = meshes_;
|
std::vector<Mesh> meshes = meshes_;
|
||||||
Rasterizer rasterizer{resolution_width, resolution_height};
|
Rasterizer rasterizer{resolution_width, resolution_height};
|
||||||
for (auto &primitives : meshes) {
|
for (auto &m : meshes) {
|
||||||
|
auto &primitives = m.get_primitives();
|
||||||
for (int i = 0; i < primitives.size(); ++i) {
|
for (int i = 0; i < primitives.size(); ++i) {
|
||||||
auto &t = primitives[i];
|
auto &t = primitives[i];
|
||||||
t.projective_transform(projection_matrix_);
|
const auto &triangle_vertices = t.get_vertex_position();
|
||||||
t.affine_transform(
|
auto tmp = apply_transform(projection_matrix_, triangle_vertices);
|
||||||
view_port_transform(resolution_width, resolution_height));
|
|
||||||
|
const auto &triangle_indices = t.get_vertex_index();
|
||||||
|
std::vector<Point3d> triangle_uv;
|
||||||
|
for (int j = 0; j < triangle_indices.size(); ++j) {
|
||||||
|
const auto &uv = m.get_texture_coordinate(triangle_indices[j]);
|
||||||
|
triangle_uv.push_back(Point3d{uv.x(), uv.y(), 1});
|
||||||
|
}
|
||||||
|
apply_transform(projection_matrix_, triangle_uv);
|
||||||
|
|
||||||
|
t.set_points(apply_transform(
|
||||||
|
view_port_transform(resolution_width, resolution_height), tmp));
|
||||||
}
|
}
|
||||||
rasterizer.rasterize(primitives);
|
rasterizer.rasterize(primitives);
|
||||||
}
|
}
|
||||||
const auto &pixels = rasterizer.get_picture();
|
const auto &shading_points = rasterizer.get_picture();
|
||||||
assert(!pixels.empty());
|
assert(!shading_points.empty());
|
||||||
cv::Mat color_image(pixels.size(), (pixels[0]).size(), CV_8UC3);
|
cv::Mat depth_image(shading_points.size(), (shading_points[0]).size(),
|
||||||
cv::Mat depth_image(pixels.size(), (pixels[0]).size(), CV_8UC1);
|
CV_8UC1);
|
||||||
|
|
||||||
for (int i = 0; i < pixels.size(); ++i) {
|
for (int i = 0; i < shading_points.size(); ++i) {
|
||||||
const auto &row = pixels[i];
|
const auto &row = shading_points[i];
|
||||||
for (int j = 0; j < row.size(); ++j) {
|
for (int j = 0; j < row.size(); ++j) {
|
||||||
auto &pixel_color = color_image.at<cv::Vec3b>(pixels.size() - i - 1, j);
|
|
||||||
pixel_color = cv::Vec3b{
|
|
||||||
static_cast<unsigned char>(row[j].x() *
|
|
||||||
std::numeric_limits<unsigned char>::max()),
|
|
||||||
static_cast<unsigned char>(row[j].y() *
|
|
||||||
std::numeric_limits<unsigned char>::max()),
|
|
||||||
static_cast<unsigned char>(
|
|
||||||
row[j].z() * std::numeric_limits<unsigned char>::max())};
|
|
||||||
auto &pixel_depth =
|
auto &pixel_depth =
|
||||||
depth_image.at<unsigned char>(pixels.size() - i - 1, j);
|
depth_image.at<unsigned char>(shading_points.size() - i - 1, j);
|
||||||
if (std::isinf(std::fabs(row[j].w()))) {
|
if (std::isinf(std::fabs(row[j].depth))) {
|
||||||
pixel_depth = 0;
|
pixel_depth = 0;
|
||||||
} else {
|
} else {
|
||||||
const float k =
|
const float k =
|
||||||
static_cast<float>(std::numeric_limits<unsigned char>::max()) * 0.5;
|
static_cast<float>(std::numeric_limits<unsigned char>::max()) * 0.5;
|
||||||
const float b = k;
|
const float b = k;
|
||||||
pixel_depth = static_cast<unsigned char>(k * row[j].w() + b);
|
pixel_depth = static_cast<unsigned char>(k * row[j].depth + b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return depth_image;
|
return depth_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MinusRenderer::load_mesh(const std::string &file_path) {
|
void MinusRenderer::load_mesh(const std::string &file_path) {
|
||||||
objl::Loader loader{};
|
meshes_ = Mesh::load_mesh(file_path);
|
||||||
bool load_status = loader.LoadFile(file_path.c_str());
|
spdlog::info("Mesh size: {}, the primitives size of the first mesh: {}",
|
||||||
if (load_status) {
|
meshes_.size(), meshes_.front().get_primitives().size());
|
||||||
for (const auto &mesh : loader.LoadedMeshes) {
|
|
||||||
spdlog::info("Current mesh has {} vertices.", mesh.Vertices.size());
|
|
||||||
Mesh m{};
|
|
||||||
for (int i = 0; i < mesh.Vertices.size(); i += 3) {
|
|
||||||
const auto objl_v0 = mesh.Vertices[mesh.Indices[i]].Position;
|
|
||||||
Point3d v0(objl_v0.X, objl_v0.Y, objl_v0.Z);
|
|
||||||
const auto objl_v1 = mesh.Vertices[mesh.Indices[i + 1]].Position;
|
|
||||||
Point3d v1(objl_v1.X, objl_v1.Y, objl_v1.Z);
|
|
||||||
const auto objl_v2 = mesh.Vertices[mesh.Indices[i + 2]].Position;
|
|
||||||
Point3d v2(objl_v2.X, objl_v2.Y, objl_v2.Z);
|
|
||||||
m.push_back(Triangle(v0, v1, v2));
|
|
||||||
}
|
|
||||||
meshes_.push_back(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return load_status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,35 +3,33 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "triangle.h"
|
#include "mesh.h"
|
||||||
|
#include "util/math_util.h"
|
||||||
#include <opencv2/core/core.hpp>
|
#include <opencv2/core/core.hpp>
|
||||||
|
|
||||||
class MinusRenderer {
|
class MinusRenderer {
|
||||||
public:
|
public:
|
||||||
MinusRenderer(float near, float far, float fov, float aspect_ratio);
|
MinusRenderer(float near, float far, float fov, float aspect_ratio);
|
||||||
|
void load_mesh(const std::string &file_path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cv::Mat render(const int resolution_width, const int resolution_height);
|
cv::Mat render(const int resolution_width, const int resolution_height);
|
||||||
|
|
||||||
public:
|
|
||||||
bool load_mesh(const std::string &file_path);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void model_transform(const TransformMatrix &mtx);
|
void model_transform(const TransformMatrix &mtx);
|
||||||
void view_transform(const TransformMatrix &mtx);
|
void view_transform(const TransformMatrix &mtx);
|
||||||
|
|
||||||
private:
|
|
||||||
using Mesh = std::vector<Triangle>;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static float calculate_height(const float fov, const float near);
|
static float calculate_height(const float fov, const float near);
|
||||||
static float calculate_width(const float height, const float ratio);
|
static float calculate_width(const float height, const float ratio);
|
||||||
static TransformMatrix view_port_transform(const float width,
|
static TransformMatrix view_port_transform(const float width,
|
||||||
const float height);
|
const float height);
|
||||||
|
static Point3d calculate_barycentric_coordinate(const Triangle &t,
|
||||||
|
const Point2d &p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TransformMatrix squish_tranform();
|
TransformMatrix squish_transform();
|
||||||
TransformMatrix orthographic_tranform();
|
TransformMatrix orthographic_transform();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float near_;
|
float near_;
|
||||||
|
|
|
@ -10,74 +10,67 @@
|
||||||
|
|
||||||
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<PixelProperty>>(
|
shading_points_(std::vector<std::vector<RasterizerResult>>(
|
||||||
height, std::vector<PixelProperty>(
|
height, std::vector<RasterizerResult>(width, RasterizerResult{}))) {}
|
||||||
width, PixelProperty{
|
|
||||||
0., 0., 0.,
|
|
||||||
-std::numeric_limits<double>::infinity()}))) {}
|
|
||||||
|
|
||||||
std::vector<std::vector<PixelProperty>>
|
std::vector<std::vector<RasterizerResult>>
|
||||||
Rasterizer::rasterize(const std::vector<Triangle> &primitives) {
|
Rasterizer::rasterize(const std::vector<Triangle> &primitives) {
|
||||||
for (const auto &t : primitives) {
|
auto partial_rasterize = [&primitives, this](const int begin, const int end) {
|
||||||
const auto &triangle_points = t.get_points<3>();
|
for (int idx = begin; idx < end; ++idx) {
|
||||||
|
const auto &t = primitives[idx];
|
||||||
|
const auto &triangle_points = t.get_vertex_position();
|
||||||
|
|
||||||
const auto &aabb = t.axis_align_bbox();
|
const auto &aabb = t.axis_align_bbox();
|
||||||
const int x_min = std::min(std::max(0, aabb.x), width_);
|
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 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_min = std::min(std::max(0, aabb.y), height_);
|
||||||
const int y_max = std::min(std::max(0, aabb.y + aabb.height), height_);
|
const int y_max = std::min(std::max(0, aabb.y + aabb.height), height_);
|
||||||
|
|
||||||
auto partial_rasterize = [x_min, x_max, &t, this](const int start,
|
for (int i = y_min; i < y_max; ++i) {
|
||||||
const int end) {
|
|
||||||
for (int i = start; i < end; ++i) {
|
|
||||||
for (int j = x_min; j <= x_max; ++j) {
|
for (int j = x_min; j <= x_max; ++j) {
|
||||||
auto &property = (this->pixels_)[i][j];
|
auto &property = (this->shading_points_)[i][j];
|
||||||
const auto &[is_inside, z_screen] =
|
const auto &[is_inside, z_screen] =
|
||||||
inside(Point2d{j + 0.5, i + 0.5}, t);
|
inside(Point2d{j + 0.5, i + 0.5}, t);
|
||||||
if (is_inside && z_screen > property[3]) {
|
if (is_inside && z_screen > property.depth) {
|
||||||
property[0] = 0;
|
property.depth = z_screen;
|
||||||
property[1] = 1;
|
property.triangle_index = idx;
|
||||||
property[2] = 0;
|
|
||||||
property[3] = z_screen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
const int thread_num = 6;
|
|
||||||
std::vector<std::thread> rasterize_threads;
|
|
||||||
for (int start = y_min, offset = (y_max - y_min) / thread_num,
|
|
||||||
remainer = (y_max - y_min) % thread_num;
|
|
||||||
start < y_max;) {
|
|
||||||
int end = start + offset;
|
|
||||||
if (remainer > 0) {
|
|
||||||
end += 1;
|
|
||||||
remainer -= 1;
|
|
||||||
}
|
|
||||||
rasterize_threads.push_back(std::thread(partial_rasterize, start, end));
|
|
||||||
start = end;
|
|
||||||
}
|
}
|
||||||
for (auto &t : rasterize_threads) {
|
};
|
||||||
t.join();
|
|
||||||
|
const int thread_num = 6;
|
||||||
|
std::vector<std::thread> rasterize_threads;
|
||||||
|
for (int begin = 0, offset = primitives.size() / thread_num,
|
||||||
|
remainer = (primitives.size() % thread_num);
|
||||||
|
begin < primitives.size();) {
|
||||||
|
int end = begin + offset;
|
||||||
|
if (remainer > 0) {
|
||||||
|
end += 1;
|
||||||
|
remainer -= 1;
|
||||||
}
|
}
|
||||||
|
rasterize_threads.push_back(std::thread(partial_rasterize, begin, end));
|
||||||
|
begin = end;
|
||||||
}
|
}
|
||||||
return pixels_;
|
for (auto &t : rasterize_threads) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
return shading_points_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<PixelProperty>> Rasterizer::get_picture() const {
|
std::vector<std::vector<RasterizerResult>> Rasterizer::get_picture() const {
|
||||||
return pixels_;
|
return shading_points_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rasterizer::reset() {
|
void Rasterizer::reset() {
|
||||||
pixels_ = std::vector<std::vector<PixelProperty>>(
|
shading_points_ = std::vector<std::vector<RasterizerResult>>(
|
||||||
height_,
|
height_, std::vector<RasterizerResult>(width_, RasterizerResult{}));
|
||||||
std::vector<PixelProperty>(
|
|
||||||
width_,
|
|
||||||
PixelProperty{0., 0., 0., -std::numeric_limits<double>::infinity()}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, double> Rasterizer::inside(const Point2d &p_screen,
|
std::pair<bool, double> Rasterizer::inside(const Point2d &p_screen,
|
||||||
const Triangle &t) {
|
const Triangle &t) {
|
||||||
const auto points = t.get_points<3>();
|
const auto points = t.get_vertex_position();
|
||||||
|
|
||||||
const auto plane_normal = t.normal_vector();
|
const auto plane_normal = t.normal_vector();
|
||||||
const auto plane_point = points[0];
|
const auto plane_point = points[0];
|
||||||
|
|
|
@ -7,15 +7,20 @@
|
||||||
#include <opencv2/core/core.hpp>
|
#include <opencv2/core/core.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct RasterizerResult {
|
||||||
|
double depth{-std::numeric_limits<double>::infinity()};
|
||||||
|
int64_t triangle_index{-1};
|
||||||
|
} RasterizerResult;
|
||||||
|
|
||||||
class Rasterizer {
|
class Rasterizer {
|
||||||
public:
|
public:
|
||||||
Rasterizer(const int width, const int height);
|
Rasterizer(const int width, const int height);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::vector<std::vector<PixelProperty>>
|
std::vector<std::vector<RasterizerResult>>
|
||||||
rasterize(const std::vector<Triangle> &primitives);
|
rasterize(const std::vector<Triangle> &primitives);
|
||||||
|
|
||||||
std::vector<std::vector<PixelProperty>> get_picture() const;
|
std::vector<std::vector<RasterizerResult>> get_picture() const;
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -25,5 +30,5 @@ private:
|
||||||
private:
|
private:
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
std::vector<std::vector<PixelProperty>> pixels_;
|
std::vector<std::vector<RasterizerResult>> shading_points_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,37 +5,48 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
Triangle::Triangle(const Point3d &a, const Point3d &b, const Point3d &c)
|
Triangle::Triangle(const Point3d &a, unsigned int idx_a, const Point3d &b,
|
||||||
: points_({a, b, c}) {}
|
unsigned int idx_b, const Point3d &c, unsigned int idx_c)
|
||||||
|
: indices_({idx_a, idx_b, idx_c}), vertices_({a, b, c}) {}
|
||||||
|
|
||||||
Triangle::Triangle(const HomoPointType &a, const HomoPointType &b,
|
Triangle::Triangle(const std::vector<std::shared_ptr<Vertex>> &vertices,
|
||||||
const HomoPointType &c)
|
unsigned int idx_a, unsigned int idx_b, unsigned int idx_c)
|
||||||
: points_({(a / a.z()).head(3), (b / b.z()).head(3), (c / c.z()).head(3)}) {
|
: indices_({idx_a, idx_b, idx_c}),
|
||||||
|
vertices_({(vertices[idx_a])->position, (vertices[idx_b])->position,
|
||||||
|
(vertices[idx_c])->position}) {}
|
||||||
|
|
||||||
|
std::vector<unsigned int> Triangle::get_vertex_index() const {
|
||||||
|
return {indices_[0], indices_[1], indices_[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Point3d> Triangle::get_vertex_position() const {
|
||||||
|
return std::vector<Point3d>{
|
||||||
|
vertices_[0],
|
||||||
|
vertices_[1],
|
||||||
|
vertices_[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Triangle::set_points(const std::vector<Point3d> &points) {
|
||||||
|
vertices_[0] = points[0];
|
||||||
|
vertices_[1] = points[1];
|
||||||
|
vertices_[2] = points[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
BBox Triangle::axis_align_bbox() const {
|
BBox Triangle::axis_align_bbox() const {
|
||||||
const int x_min = std::min({points_[0].x(), points_[1].x(), points_[2].x()});
|
const int x_min =
|
||||||
const int y_min = std::min({points_[0].y(), points_[1].y(), points_[2].y()});
|
std::min({vertices_[0].x(), vertices_[1].x(), vertices_[2].x()});
|
||||||
const int x_max = std::max({points_[0].x(), points_[1].x(), points_[2].x()});
|
const int y_min =
|
||||||
const int y_max = std::max({points_[0].y(), points_[1].y(), points_[2].y()});
|
std::min({vertices_[0].y(), vertices_[1].y(), vertices_[2].y()});
|
||||||
|
const int x_max =
|
||||||
|
std::max({vertices_[0].x(), vertices_[1].x(), vertices_[2].x()});
|
||||||
|
const int y_max =
|
||||||
|
std::max({vertices_[0].y(), vertices_[1].y(), vertices_[2].y()});
|
||||||
return BBox{x_min, y_min, (x_max - x_min + 1), (y_max - y_min + 1)};
|
return BBox{x_min, y_min, (x_max - x_min + 1), (y_max - y_min + 1)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3d Triangle::normal_vector() const {
|
Vector3d Triangle::normal_vector() const {
|
||||||
const auto v1 = points_[1] - points_[0];
|
const auto v1 = vertices_[1] - vertices_[0];
|
||||||
const auto v2 = points_[2] - points_[1];
|
const auto v2 = vertices_[2] - vertices_[1];
|
||||||
return v1.cross(v2).normalized();
|
return v1.cross(v2).normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Triangle::affine_transform(const TransformMatrix &m) {
|
|
||||||
for (auto &p : points_) {
|
|
||||||
p = (m * Eigen::Vector4d(p.x(), p.y(), p.z(), 1.)).head<3>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Triangle::projective_transform(const TransformMatrix &m) {
|
|
||||||
for (auto &p : points_) {
|
|
||||||
auto p_homo = m * Eigen::Vector4d(p.x(), p.y(), p.z(), 1.);
|
|
||||||
p = (p_homo / p_homo.w()).head<3>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,34 +4,26 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <Eigen/Geometry>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Triangle {
|
class Triangle {
|
||||||
private:
|
public:
|
||||||
using HomoPointType = Eigen::Vector4d;
|
Triangle(const Point3d &a, unsigned int idx_a, const Point3d &b,
|
||||||
|
unsigned int idx_b, const Point3d &c, unsigned int idx_c);
|
||||||
|
Triangle(const std::vector<std::shared_ptr<Vertex>> &vertices,
|
||||||
|
unsigned int idx_a, unsigned int idx_b, unsigned int idx_c);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Triangle(const Point3d &a, const Point3d &b, const Point3d &c);
|
std::vector<unsigned int> get_vertex_index() const;
|
||||||
Triangle(const HomoPointType &a, const HomoPointType &b,
|
std::vector<Point3d> get_vertex_position() const;
|
||||||
const HomoPointType &c);
|
void set_points(const std::vector<Point3d> &points);
|
||||||
|
|
||||||
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:
|
||||||
BBox axis_align_bbox() const;
|
BBox axis_align_bbox() const;
|
||||||
Vector3d normal_vector() const;
|
Vector3d normal_vector() const;
|
||||||
|
|
||||||
void affine_transform(const TransformMatrix &m);
|
|
||||||
void projective_transform(const TransformMatrix &m);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<Point3d, 3> points_;
|
std::array<unsigned int, 3> indices_;
|
||||||
|
std::array<Point3d, 3> vertices_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2024 Bytedance Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@bytedance.com (tianlei.richard)
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
template <typename FloatType>
|
||||||
|
bool fequal(const FloatType a, const FloatType b) {
|
||||||
|
return std::fabs(a - b) <= std::numeric_limits<FloatType>::epsilon();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int d>
|
||||||
|
std::vector<Eigen::Vector<double, d>>
|
||||||
|
apply_transform(const TransformMatrix &mtx,
|
||||||
|
const std::vector<Eigen::Vector<double, d>> &points) {
|
||||||
|
std::vector<Eigen::Vector<double, d>> res;
|
||||||
|
for (const auto &p : points) {
|
||||||
|
auto tmp_p = Eigen::Vector<double, 1 + d>();
|
||||||
|
tmp_p << p, 1.;
|
||||||
|
tmp_p = mtx * tmp_p;
|
||||||
|
if (!fequal(tmp_p.w(), 1.)) {
|
||||||
|
tmp_p /= tmp_p.w();
|
||||||
|
}
|
||||||
|
res.push_back(tmp_p.template head<d>());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
Loading…
Reference in New Issue