From acd472bacf798760fc249e12e6f456695f6afb74 Mon Sep 17 00:00:00 2001 From: "tianlei.richard" Date: Fri, 29 Mar 2024 18:09:16 +0800 Subject: [PATCH] complete phone shading impl. --- src/mesh.cc | 44 +++++++++++-------- src/mesh.h | 18 ++++---- src/minus_renderer.cc | 100 ++++++++++++++++++------------------------ src/minus_renderer.h | 2 +- src/phone_material.cc | 8 ++-- src/phone_material.h | 4 +- src/rasterizer.cc | 54 +++++++++++------------ src/rasterizer.h | 4 +- src/triangle.cc | 14 +++--- src/triangle.h | 5 ++- src/util/math_util.h | 15 +++++-- 11 files changed, 137 insertions(+), 131 deletions(-) diff --git a/src/mesh.cc b/src/mesh.cc index 23663b9..36aafc7 100644 --- a/src/mesh.cc +++ b/src/mesh.cc @@ -11,31 +11,39 @@ #define OBJL_VEC3_TO_EIGEN_VECTOR(objl_v) \ Eigen::Vector3d { objl_v.X, objl_v.Y, objl_v.Z } -Mesh::Mesh(const std::vector> &vertices, - const std::vector &primitives, +Mesh::Mesh(const std::vector &vertices, + const std::vector> &indices, const std::shared_ptr &phone_material) - : vertices_(vertices), primitives_(primitives), material_(phone_material) {} + : vertices_(vertices), indices_(indices), material_(phone_material) {} -std::vector>::size_type Mesh::get_vertex_size() const { +std::vector &Mesh::get_vertices() { return vertices_; } + +std::vector::size_type Mesh::get_vertex_size() const { return vertices_.size(); }; const decltype(Vertex::texture_coordinate) & Mesh::get_texture_coordinate(const unsigned int index) const { - return (vertices_[index])->texture_coordinate; + return (vertices_[index]).texture_coordinate; } Point3d Mesh::get_normal_vector(const unsigned int index) const { - return (vertices_[index])->normal; + return (vertices_[index]).normal; } -const std::vector &Mesh::get_primitives() const { - return primitives_; -}; +std::vector Mesh::get_primitives() const { + std::vector primitives; + for (const auto &idx : indices_) { + primitives.push_back(Triangle(vertices_, idx[0], idx[1], idx[2])); + } + return primitives; +} -std::vector &Mesh::get_primitives() { return primitives_; }; - -std::shared_ptr &Mesh::get_material() { return material_; } +Triangle Mesh::get_primitive(const size_t index) const { + assert(index >= 0 && index < indices_.size()); + return Triangle(vertices_, indices_[index][0], indices_[index][1], + indices_[index][2]); +} const std::shared_ptr &Mesh::get_material() const { return material_; @@ -54,19 +62,19 @@ std::vector Mesh::load_mesh( for (const auto &mesh : loader.LoadedMeshes) { spdlog::info("Current mesh has {} vertices.", mesh.Vertices.size()); - std::vector> vertices; + std::vector vertices; for (decltype(mesh.Vertices)::size_type i = 0; i < mesh.Vertices.size(); i++) { - vertices.push_back(std::make_shared( + vertices.push_back(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 primitives; + std::vector> indices; for (decltype(mesh.Indices)::size_type i = 0; i < mesh.Indices.size(); i += 3) { - primitives.push_back(Triangle(vertices, mesh.Indices[i], - mesh.Indices[i + 1], mesh.Indices[i + 2])); + indices.push_back( + {mesh.Indices[i], mesh.Indices[i + 1], mesh.Indices[i + 2]}); } std::unordered_map> textures; @@ -82,7 +90,7 @@ std::vector Mesh::load_mesh( auto material = std::make_shared( textures, std::make_shared(Point3d{}, 16.)); - res.push_back(Mesh(vertices, primitives, material)); + res.push_back(Mesh(vertices, indices, material)); } return res; } diff --git a/src/mesh.h b/src/mesh.h index 4723e72..ecb5432 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -12,8 +13,8 @@ class Mesh { public: - Mesh(const std::vector> &vertices, - const std::vector &primitives, + Mesh(const std::vector &vertices, + const std::vector> &indices, const std::shared_ptr &phone_material); static std::vector load_mesh( @@ -21,22 +22,23 @@ public: const std::unordered_map textures); public: - std::vector>::size_type get_vertex_size() const; + std::vector &get_vertices(); + + std::vector::size_type get_vertex_size() const; const decltype(Vertex::texture_coordinate) & get_texture_coordinate(const unsigned int index) const; Point3d get_normal_vector(const unsigned int index) const; - const std::vector &get_primitives() const; - std::vector &get_primitives(); + std::vector get_primitives() const; + Triangle get_primitive(const size_t index) const; - std::shared_ptr &get_material(); const std::shared_ptr &get_material() const; private: - std::vector> vertices_; - std::vector primitives_; + std::vector vertices_; + std::vector> indices_; std::shared_ptr material_; }; diff --git a/src/minus_renderer.cc b/src/minus_renderer.cc index 00c172a..cd1bdf8 100644 --- a/src/minus_renderer.cc +++ b/src/minus_renderer.cc @@ -19,8 +19,6 @@ MinusRenderer::MinusRenderer(float near, float far, float fov, void MinusRenderer::set_meshes(const std::vector &meshes) { meshes_ = meshes; - spdlog::info("Mesh size: {}, the primitives size of the first mesh: {}", - meshes_.size(), meshes_.front().get_primitives().size()); } float MinusRenderer::calculate_height(const float fov, const float near) { @@ -94,35 +92,38 @@ MinusRenderer::construct_transform(const int resolution_width, std::tuple MinusRenderer::calculate_barycentric_coordinate(const Triangle &t, - const Point2d &p) { + const Point3d &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; + const auto &pa = points[0]; + const auto &pb = points[1]; + const auto &pc = points[2]; + const auto &triangle_normal = t.normal_vector(); + + const double squared_area = triangle_normal.norm() * triangle_normal.norm(); + const double alpha = + (triangle_normal.dot((pc - pb).cross(p - pb))) / squared_area; + const double beta = + (triangle_normal.dot((pa - pc).cross(p - pc))) / squared_area; + const double gamma = + (triangle_normal.dot((pb - pa).cross(p - pa))) / squared_area; + return {alpha, beta, gamma}; } void MinusRenderer::model_transform(const TransformMatrix &mtx) { for (auto &m : meshes_) { - for (auto &t : m.get_primitives()) { - const auto &[res, _] = apply_transform(mtx, t.get_vertex_position()); - t.set_points(res); + for (auto &v : m.get_vertices()) { + v.position = (mtx * v.position.homogeneous()).hnormalized(); + v.normal = (mtx * v.normal.homogeneous()).hnormalized(); } } } void MinusRenderer::view_transform(const TransformMatrix &mtx) { for (auto &m : meshes_) { - for (auto &t : m.get_primitives()) { - const auto &[res, _] = apply_transform(mtx, t.get_vertex_position()); - t.set_points(res); + for (auto &v : m.get_vertices()) { + v.position = (mtx * v.position.homogeneous()).hnormalized(); + v.normal = (mtx * v.normal.homogeneous()).hnormalized(); } } } @@ -133,22 +134,16 @@ cv::Mat MinusRenderer::render(const int resolution_width, const auto [ss_transform, inv_ss_transform] = construct_transform(resolution_width, resolution_height); - // 主要为了把顶点坐标拷贝一份,否则 camera space 的坐标被变换到 screen space, - // 下次渲染时拿到的就不是 camera space 的坐标 - std::vector meshes = meshes_; Rasterizer rasterizer{resolution_width, resolution_height}; - for (int mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) { - auto &mesh = meshes[mesh_index]; - auto &primitives = mesh.get_primitives(); - for (int i = 0; i < primitives.size(); ++i) { - auto &t = primitives[i]; - const auto &triangle_vertices = t.get_vertex_position(); - const auto &[res, _] = - apply_transform(ss_transform, t.get_vertex_position()); - t.set_points(res); + for (int mesh_index = 0; mesh_index < meshes_.size(); ++mesh_index) { + auto primitives = (meshes_[mesh_index]).get_primitives(); + for (auto &t : primitives) { + t.set_points(apply_transform(ss_transform, t.get_vertex_position())); } + rasterizer.rasterize(mesh_index, primitives); } - const auto &shading_points = rasterizer.rasterize(meshes); + + const auto &shading_points = rasterizer.get_shading_points(); assert(!shading_points.empty()); cv::Mat color_image(shading_points.size(), (shading_points[0]).size(), CV_8UC3); @@ -162,23 +157,16 @@ cv::Mat MinusRenderer::render(const int resolution_width, if (fragment.triangle.mesh_index >= 0 && fragment.triangle.mesh_index < meshes_.size()) { const auto &mesh = meshes_[fragment.triangle.mesh_index]; - const auto &primitives = mesh.get_primitives(); - if (fragment.triangle.triangle_index >= 0 && - fragment.triangle.triangle_index < primitives.size()) { + if (fragment.depth > -std::numeric_limits::infinity()) { const Point3d &position = (inv_ss_transform * (Point3d{x + 0.5, y + 0.5, fragment.depth}.homogeneous())) .hnormalized(); - spdlog::trace("Screen space potions: ({}, {}, {}), camera space " - "position: ({}, {}, {})", - x + 0.5, y + 0.5, fragment.depth, position.x(), - position.y(), position.z()); - const auto &triangle = primitives[fragment.triangle.triangle_index]; + const auto &triangle = + mesh.get_primitive(fragment.triangle.triangle_index); const auto &[alpha, beta, gamma] = - calculate_barycentric_coordinate(triangle, position.head(2)); - spdlog::trace("barycentric coordinate: ({}, {}, {})", alpha, beta, - gamma); + calculate_barycentric_coordinate(triangle, position); std::vector triangle_uv; std::vector triangle_normal; @@ -191,22 +179,18 @@ cv::Mat MinusRenderer::render(const int resolution_width, gamma * triangle_uv[2].x(), alpha * triangle_uv[0].y() + beta * triangle_uv[1].y() + gamma * triangle_uv[2].y()}; - spdlog::trace("UV: ({}, {}, {})", uv.x(), uv.y()); + const auto &normal = -1. * Point3d(alpha * triangle_normal[0].x() + + beta * triangle_normal[1].x() + + gamma * triangle_normal[2].x(), + alpha * triangle_normal[0].y() + + beta * triangle_normal[1].y() + + gamma * triangle_normal[2].y(), + alpha * triangle_normal[0].z() + + beta * triangle_normal[1].z() + + gamma * triangle_normal[2].z()) + .normalized(); - const auto &normal = Point3d{alpha * triangle_normal[0].x() + - beta * triangle_normal[1].x() + - gamma * triangle_normal[2].x(), - alpha * triangle_normal[0].y() + - beta * triangle_normal[1].y() + - gamma * triangle_normal[2].y(), - alpha * triangle_normal[0].z() + - beta * triangle_normal[1].z() + - gamma * triangle_normal[2].z()} - .normalized(); - spdlog::trace("Normal: ({}, {}, {})", normal.x(), normal.y(), - normal.z()); - - auto &material = mesh.get_material(); + const auto &material = mesh.get_material(); pixel_color = material->shade(Vertex{position, normal, uv}, Point3d{}); } diff --git a/src/minus_renderer.h b/src/minus_renderer.h index 95554b3..4dd2d6d 100644 --- a/src/minus_renderer.h +++ b/src/minus_renderer.h @@ -24,7 +24,7 @@ private: static float calculate_height(const float fov, const float near); static float calculate_width(const float height, const float ratio); static std::tuple - calculate_barycentric_coordinate(const Triangle &t, const Point2d &p); + calculate_barycentric_coordinate(const Triangle &t, const Point3d &p); private: std::pair diff --git a/src/phone_material.cc b/src/phone_material.cc index b40624d..770093d 100644 --- a/src/phone_material.cc +++ b/src/phone_material.cc @@ -10,19 +10,19 @@ PhoneMaterial::PhoneMaterial( const std::unordered_map> &textures, const std::shared_ptr &lamp) - : point_lamp_(lamp), textures_(textures), Ka(cv::Vec3b{32, 32, 32}), - Ks(cv::Vec3b(64, 64, 64)), + : point_lamp_(lamp), textures_(textures), Ka(cv::Vec3b{24, 24, 24}), + Ks(cv::Vec3b(48, 48, 48)), specular_attenuation_factor_(defaultAttenuationFactor) { spdlog::info("Specular attenuation factor: {}", specular_attenuation_factor_); } cv::Vec3b PhoneMaterial::shade(const Vertex &shading_point, - const Point3d &view_point) { + const Point3d &view_point) const { cv::Vec3b shading_res{0, 0, 0}; // Diffuse assert(textures_.find(Texture::DiffuseMap) != textures_.end()); - const auto &diffuse_map = textures_[Texture::DiffuseMap]; + const auto &diffuse_map = textures_.at(Texture::DiffuseMap); const auto &Kd = diffuse_map->sample(shading_point.texture_coordinate); const auto &point2light_vector = diff --git a/src/phone_material.h b/src/phone_material.h index 768284a..d7532a2 100644 --- a/src/phone_material.h +++ b/src/phone_material.h @@ -17,10 +17,10 @@ public: std::shared_ptr> &textures, const std::shared_ptr &lamp); - cv::Vec3b shade(const Vertex &shading_point, const Point3d &view_point); + cv::Vec3b shade(const Vertex &shading_point, const Point3d &view_point) const; private: - static constexpr int defaultAttenuationFactor = 16; + static constexpr int defaultAttenuationFactor = 12; private: std::shared_ptr point_lamp_; diff --git a/src/rasterizer.cc b/src/rasterizer.cc index eef104b..7db233d 100644 --- a/src/rasterizer.cc +++ b/src/rasterizer.cc @@ -13,34 +13,29 @@ Rasterizer::Rasterizer(const int width, const int height) shading_points_(std::vector>( height, std::vector(width, RasterizerResult{}))) {} -std::vector> -Rasterizer::rasterize(const std::vector &meshes) { - for (int m_id = 0; m_id < meshes.size(); ++m_id) { - const auto &primitives = (meshes[m_id]).get_primitives(); - - const int thread_num = 8; - std::vector 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( - [this, m_id](const std::vector &primitives, const int begin, - const int end) { - this->rasterize(m_id, primitives, begin, end); - }, - (meshes[m_id]).get_primitives(), begin, end)); - begin = end; - } - for (auto &t : rasterize_threads) { - t.join(); +void Rasterizer::rasterize(const int mesh_idx, + const std::vector &primitives) { + const int thread_num = 8; + std::vector 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( + [this, mesh_idx](const std::vector &primitives, + const int begin, const int end) { + this->rasterize(mesh_idx, primitives, begin, end); + }, + primitives, begin, end)); + begin = end; + } + for (auto &t : rasterize_threads) { + t.join(); } - return shading_points_; } void Rasterizer::rasterize(const int mesh_idx, @@ -71,6 +66,11 @@ void Rasterizer::rasterize(const int mesh_idx, } } +const std::vector> & +Rasterizer::get_shading_points() const { + return shading_points_; +} + void Rasterizer::reset() { shading_points_ = std::vector>( height_, std::vector(width_, RasterizerResult{})); @@ -80,7 +80,7 @@ std::pair Rasterizer::inside(const Point2d &p_screen, const Triangle &t) { const auto points = t.get_vertex_position(); - const auto plane_normal = t.normal_vector(); + const auto plane_normal = t.unit_normal_vector(); const auto plane_point = points[0]; const auto z_screen = plane_point.z() - (plane_normal.x() * (p_screen.x() - plane_point.x()) + diff --git a/src/rasterizer.h b/src/rasterizer.h index 4a97e25..d21cd00 100644 --- a/src/rasterizer.h +++ b/src/rasterizer.h @@ -21,9 +21,9 @@ public: Rasterizer(const int width, const int height); public: - std::vector> - rasterize(const std::vector &meshes); + void rasterize(const int mesh_idx, const std::vector &primitives); + const std::vector> &get_shading_points() const; void reset(); private: diff --git a/src/triangle.cc b/src/triangle.cc index d71a6e8..ce2a429 100644 --- a/src/triangle.cc +++ b/src/triangle.cc @@ -9,11 +9,11 @@ Triangle::Triangle(const Point3d &a, unsigned int idx_a, const Point3d &b, 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 std::vector> &vertices, - unsigned int idx_a, unsigned int idx_b, unsigned int idx_c) +Triangle::Triangle(const std::vector &vertices, unsigned int idx_a, + unsigned int idx_b, unsigned int idx_c) : indices_({idx_a, idx_b, idx_c}), - vertices_({(vertices[idx_a])->position, (vertices[idx_b])->position, - (vertices[idx_c])->position}) {} + vertices_({(vertices[idx_a]).position, (vertices[idx_b]).position, + (vertices[idx_c]).position}) {} std::vector Triangle::get_vertex_index() const { return {indices_[0], indices_[1], indices_[2]}; @@ -45,8 +45,12 @@ BBox Triangle::axis_align_bbox() const { return BBox{x_min, y_min, (x_max - x_min + 1), (y_max - y_min + 1)}; } +Vector3d Triangle::unit_normal_vector() const { + return normal_vector().normalized(); +} + Vector3d Triangle::normal_vector() const { const auto v1 = vertices_[1] - vertices_[0]; const auto v2 = vertices_[2] - vertices_[1]; - return v1.cross(v2).normalized(); + return v1.cross(v2); } diff --git a/src/triangle.h b/src/triangle.h index 7c291a6..4fc4cbf 100644 --- a/src/triangle.h +++ b/src/triangle.h @@ -11,8 +11,8 @@ class Triangle { public: 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> &vertices, - unsigned int idx_a, unsigned int idx_b, unsigned int idx_c); + Triangle(const std::vector &vertices, unsigned int idx_a, + unsigned int idx_b, unsigned int idx_c); public: std::vector get_vertex_index() const; @@ -22,6 +22,7 @@ public: public: BBox axis_align_bbox() const; Vector3d normal_vector() const; + Vector3d unit_normal_vector() const; private: std::array indices_; diff --git a/src/util/math_util.h b/src/util/math_util.h index 83cc57c..f52b250 100644 --- a/src/util/math_util.h +++ b/src/util/math_util.h @@ -11,14 +11,21 @@ bool fequal(const FloatType a, const FloatType b) { } template -std::pair>, std::vector> +std::vector> apply_transform(const TransformMatrix &mtx, const std::vector> &points) { - std::pair>, std::vector> res{}; + std::vector> res{}; for (const auto &p : points) { const auto &transform_res = mtx * p.homogeneous(); - res.first.push_back(transform_res.hnormalized()); - res.second.push_back(transform_res.w()); + res.push_back(transform_res.hnormalized()); } return res; } + +template +void apply_transform_in_place(const TransformMatrix &mtx, + std::vector> &points) { + for (auto &p : points) { + p = mtx * p.homogeneous().hnormalized(); + } +}