invole OBJ_Loader to load existed obj model, rasterize with multi-thread.
This commit is contained in:
parent
a806f5151e
commit
411e6cd653
|
@ -0,0 +1,7 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(OBJ_Loader)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} INTERFACE)
|
||||||
|
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.9)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
PROJECT(minus_renderer)
|
project(minus_renderer)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
@ -12,4 +12,4 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/src)
|
||||||
# Third Party
|
# Third Party
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/eigen)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/eigen)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/spdlog)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/spdlog)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/OBJ_Loader)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
cmake_minimum_required(VERSION 3.9)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
PROJECT(renderer)
|
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 ${OpenCV_INCLUDE_DIRS})
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC spdlog eigen ${OpenCV_LIBS})
|
target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC OBJ_Loader spdlog eigen ${OpenCV_LIBS})
|
||||||
|
|
30
src/main.cc
30
src/main.cc
|
@ -9,36 +9,42 @@
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "minus_renderer.h"
|
#include "minus_renderer.h"
|
||||||
#include "triangle.h"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
spdlog::set_level(spdlog::level::trace);
|
spdlog::set_level(spdlog::level::trace);
|
||||||
|
|
||||||
const int resolution_width = 320;
|
const std::string obj_path{argv[1]};
|
||||||
const int resolution_height = 180;
|
|
||||||
const int far = -1000;
|
const int resolution_width = 640;
|
||||||
const int near = -10;
|
const int resolution_height = 320;
|
||||||
|
const int far = -100;
|
||||||
|
const int near = -1;
|
||||||
const float fov = 120. / 180. * M_PI;
|
const float fov = 120. / 180. * M_PI;
|
||||||
const float aspect_ratio = static_cast<float>(resolution_width) /
|
const float aspect_ratio = static_cast<float>(resolution_width) /
|
||||||
static_cast<float>(resolution_height);
|
static_cast<float>(resolution_height);
|
||||||
|
|
||||||
std::vector<Triangle> primitives{
|
|
||||||
{Point3d{10, 5, -20}, Point3d{30, 25, -20}, Point3d{55, 5, -20}}};
|
|
||||||
|
|
||||||
cv::namedWindow("MinusRenderer", cv::WINDOW_AUTOSIZE);
|
cv::namedWindow("MinusRenderer", cv::WINDOW_AUTOSIZE);
|
||||||
const auto start = std::chrono::steady_clock::now();
|
const auto start = std::chrono::steady_clock::now();
|
||||||
Camera camera{Vector3d{0, 1, 0}};
|
Camera camera{Vector3d{0, 1, 0}};
|
||||||
MinusRenderer renderer{near, far, fov, aspect_ratio, std::move(primitives)};
|
MinusRenderer renderer{near, far, fov, aspect_ratio};
|
||||||
|
assert(renderer.load_mesh(obj_path));
|
||||||
|
TransformMatrix translate{
|
||||||
|
{1., 0., 0., 3.}, {0., 1., 0., 3.}, {0., 0., 1., -4.}, {0., 0., 0., 1.}};
|
||||||
|
renderer.model_transform(translate);
|
||||||
do {
|
do {
|
||||||
const auto elapse = std::chrono::duration_cast<std::chrono::milliseconds>(
|
const auto elapse = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
std::chrono::steady_clock::now() - start)
|
std::chrono::steady_clock::now() - start)
|
||||||
.count();
|
.count();
|
||||||
TransformMatrix m{
|
TransformMatrix move2origin{
|
||||||
|
{1, 0, 0, 0}, {0, 1, 0, -3}, {0, 0, 1, 4}, {0, 0, 0, 1}};
|
||||||
|
TransformMatrix rotation{
|
||||||
Eigen::Transform<double, 3, Eigen::Affine>(
|
Eigen::Transform<double, 3, Eigen::Affine>(
|
||||||
Eigen::AngleAxisd(0.03125 * M_PI, Eigen::Vector3d::UnitZ())
|
Eigen::AngleAxisd(0.0625 * M_PI, Eigen::Vector3d::UnitX())
|
||||||
.toRotationMatrix())
|
.toRotationMatrix())
|
||||||
.matrix()};
|
.matrix()};
|
||||||
renderer.model_transform(m);
|
TransformMatrix move_back{
|
||||||
|
{1, 0, 0, 0}, {0, 1, 0, 3}, {0, 0, 1, -4}, {0, 0, 0, 1}};
|
||||||
|
renderer.model_transform(move_back * rotation * move2origin);
|
||||||
|
|
||||||
camera.set_position(Point3d{0, 0, 0});
|
camera.set_position(Point3d{0, 0, 0});
|
||||||
camera.set_gaze(Vector3d{0, 0, -1});
|
camera.set_gaze(Vector3d{0, 0, -1});
|
||||||
|
|
|
@ -3,16 +3,18 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "minus_renderer.h"
|
#include "OBJ_Loader.h"
|
||||||
#include "rasterizer.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include "minus_renderer.h"
|
||||||
|
#include "rasterizer.h"
|
||||||
|
|
||||||
MinusRenderer::MinusRenderer(float near, float far, float fov,
|
MinusRenderer::MinusRenderer(float near, float far, float fov,
|
||||||
float aspect_ratio,
|
float aspect_ratio)
|
||||||
std::vector<Triangle> &&primitives)
|
|
||||||
: near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio),
|
: near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio),
|
||||||
primitives_(primitives) {
|
projection_matrix_(orthographic_tranform() * squish_tranform()) {
|
||||||
spdlog::info("fov: {}, aspect ratio: {}", fov_, aspect_ratio_);
|
spdlog::info("near: {}, far: {}, fov: {}, aspect ratio: {}", near_, far_,
|
||||||
|
fov_, aspect_ratio_);
|
||||||
}
|
}
|
||||||
|
|
||||||
float MinusRenderer::calculate_height(const float fov, const float near) {
|
float MinusRenderer::calculate_height(const float fov, const float near) {
|
||||||
|
@ -61,45 +63,84 @@ TransformMatrix MinusRenderer::view_port_transform(const float width,
|
||||||
{0, 0, 0, 1}};
|
{0, 0, 0, 1}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinusRenderer::model_transform(const TransformMatrix &m) {
|
void MinusRenderer::model_transform(const TransformMatrix &mtx) {
|
||||||
for (auto &t : primitives_) {
|
for (auto &m : meshes_) {
|
||||||
t.affine_transform(m);
|
for (auto &p : m) {
|
||||||
|
p.affine_transform(mtx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinusRenderer::view_transform(const TransformMatrix &m) {
|
void MinusRenderer::view_transform(const TransformMatrix &mtx) {
|
||||||
for (auto &t : primitives_) {
|
for (auto &m : meshes_) {
|
||||||
t.affine_transform(m);
|
for (auto &p : m) {
|
||||||
|
p.affine_transform(mtx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat MinusRenderer::render(const int resolution_width,
|
cv::Mat MinusRenderer::render(const int resolution_width,
|
||||||
const int resolution_height) {
|
const int resolution_height) {
|
||||||
std::vector<Triangle> primitives = primitives_;
|
std::vector<Mesh> meshes = meshes_;
|
||||||
for (auto &t : primitives) {
|
|
||||||
t.projective_transform(squish_tranform());
|
|
||||||
t.affine_transform(orthographic_tranform());
|
|
||||||
t.affine_transform(
|
|
||||||
view_port_transform(resolution_width, resolution_height));
|
|
||||||
}
|
|
||||||
|
|
||||||
Rasterizer rasterizer{resolution_width, resolution_height};
|
Rasterizer rasterizer{resolution_width, resolution_height};
|
||||||
const auto &pixels = rasterizer.rasterize(primitives);
|
for (auto &primitives : meshes) {
|
||||||
|
for (int i = 0; i < primitives.size(); ++i) {
|
||||||
|
auto &t = primitives[i];
|
||||||
|
t.projective_transform(projection_matrix_);
|
||||||
|
t.affine_transform(
|
||||||
|
view_port_transform(resolution_width, resolution_height));
|
||||||
|
}
|
||||||
|
rasterizer.rasterize(primitives);
|
||||||
|
}
|
||||||
|
const auto &pixels = rasterizer.get_picture();
|
||||||
assert(!pixels.empty());
|
assert(!pixels.empty());
|
||||||
cv::Mat color_image(pixels.size(), (pixels[0]).size(), CV_8UC3);
|
cv::Mat color_image(pixels.size(), (pixels[0]).size(), CV_8UC3);
|
||||||
|
cv::Mat depth_image(pixels.size(), (pixels[0]).size(), CV_8UC1);
|
||||||
|
|
||||||
for (int i = 0; i < pixels.size(); ++i) {
|
for (int i = 0; i < pixels.size(); ++i) {
|
||||||
const auto &row = pixels[i];
|
const auto &row = pixels[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);
|
auto &pixel_color = color_image.at<cv::Vec3b>(pixels.size() - i - 1, j);
|
||||||
pixel_color =
|
pixel_color = cv::Vec3b{
|
||||||
cv::Vec3b{static_cast<unsigned char>(
|
static_cast<unsigned char>(row[j].x() *
|
||||||
row[j].x() * std::numeric_limits<char>::max()),
|
std::numeric_limits<unsigned char>::max()),
|
||||||
static_cast<unsigned char>(
|
static_cast<unsigned char>(row[j].y() *
|
||||||
row[j].y() * std::numeric_limits<char>::max()),
|
std::numeric_limits<unsigned char>::max()),
|
||||||
static_cast<unsigned char>(
|
static_cast<unsigned char>(
|
||||||
row[j].z() * std::numeric_limits<char>::max())};
|
row[j].z() * std::numeric_limits<unsigned char>::max())};
|
||||||
|
auto &pixel_depth =
|
||||||
|
depth_image.at<unsigned char>(pixels.size() - i - 1, j);
|
||||||
|
if (std::isinf(std::fabs(row[j].w()))) {
|
||||||
|
pixel_depth = 0;
|
||||||
|
} else {
|
||||||
|
const float k =
|
||||||
|
static_cast<float>(std::numeric_limits<unsigned char>::max()) * 0.5;
|
||||||
|
const float b = k;
|
||||||
|
pixel_depth = static_cast<unsigned char>(k * row[j].w() + b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return color_image;
|
return depth_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinusRenderer::load_mesh(const std::string &file_path) {
|
||||||
|
objl::Loader loader{};
|
||||||
|
bool load_status = loader.LoadFile(file_path.c_str());
|
||||||
|
if (load_status) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,20 @@
|
||||||
|
|
||||||
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);
|
||||||
std::vector<Triangle> &&primitives);
|
|
||||||
|
|
||||||
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:
|
public:
|
||||||
void model_transform(const TransformMatrix &m);
|
bool load_mesh(const std::string &file_path);
|
||||||
void view_transform(const TransformMatrix &m);
|
|
||||||
|
public:
|
||||||
|
void model_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);
|
||||||
|
@ -34,5 +39,7 @@ private:
|
||||||
float fov_; // In radian
|
float fov_; // In radian
|
||||||
float aspect_ratio_;
|
float aspect_ratio_;
|
||||||
|
|
||||||
std::vector<Triangle> primitives_;
|
TransformMatrix projection_matrix_;
|
||||||
|
|
||||||
|
std::vector<Mesh> meshes_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
// Author: tianlei.richard@qq.com (tianlei.richard)
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
#include "rasterizer.h"
|
#include "rasterizer.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
Rasterizer::Rasterizer(const int width, const int height)
|
Rasterizer::Rasterizer(const int width, const int height)
|
||||||
: width_(width), height_(height),
|
: width_(width), height_(height),
|
||||||
|
@ -25,24 +27,54 @@ Rasterizer::rasterize(const std::vector<Triangle> &primitives) {
|
||||||
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_);
|
||||||
|
|
||||||
// spdlog::info("Range, ({},{},{},{})", x_min, x_max, y_min, y_max);
|
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 j = x_min; j < x_max; ++j) {
|
for (int i = start; i < end; ++i) {
|
||||||
auto &property = pixels_[i][j];
|
for (int j = x_min; j <= x_max; ++j) {
|
||||||
const auto &[is_inside, z_screen] =
|
auto &property = (this->pixels_)[i][j];
|
||||||
inside(Point2d{j + 0.5, i + 0.5}, t);
|
const auto &[is_inside, z_screen] =
|
||||||
if (is_inside && z_screen > property[3]) {
|
inside(Point2d{j + 0.5, i + 0.5}, t);
|
||||||
property[0] = 0;
|
if (is_inside && z_screen > property[3]) {
|
||||||
property[1] = 1;
|
property[0] = 0;
|
||||||
property[2] = 0;
|
property[1] = 1;
|
||||||
property[3] = z_screen;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pixels_;
|
return pixels_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<PixelProperty>> Rasterizer::get_picture() const {
|
||||||
|
return pixels_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rasterizer::reset() {
|
||||||
|
pixels_ = std::vector<std::vector<PixelProperty>>(
|
||||||
|
height_,
|
||||||
|
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_points<3>();
|
||||||
|
|
|
@ -8,12 +8,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Rasterizer {
|
class Rasterizer {
|
||||||
public:
|
|
||||||
typedef enum RasterizeMode {
|
|
||||||
Fill,
|
|
||||||
Line,
|
|
||||||
} RasterizeMode;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Rasterizer(const int width, const int height);
|
Rasterizer(const int width, const int height);
|
||||||
|
|
||||||
|
@ -21,6 +15,9 @@ public:
|
||||||
std::vector<std::vector<PixelProperty>>
|
std::vector<std::vector<PixelProperty>>
|
||||||
rasterize(const std::vector<Triangle> &primitives);
|
rasterize(const std::vector<Triangle> &primitives);
|
||||||
|
|
||||||
|
std::vector<std::vector<PixelProperty>> get_picture() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::pair<bool, double> inside(const Point2d &p_screen,
|
static std::pair<bool, double> inside(const Point2d &p_screen,
|
||||||
const Triangle &t);
|
const Triangle &t);
|
||||||
|
@ -29,6 +26,4 @@ private:
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
std::vector<std::vector<PixelProperty>> pixels_;
|
std::vector<std::vector<PixelProperty>> pixels_;
|
||||||
|
|
||||||
RasterizeMode mode_ = RasterizeMode::Fill;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@ BBox Triangle::axis_align_bbox() const {
|
||||||
const int y_min = std::min({points_[0].y(), points_[1].y(), points_[2].y()});
|
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 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()});
|
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)};
|
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 {
|
||||||
|
|
Loading…
Reference in New Issue