From 658b1ed37723fbe84d1c2cfd1c1f2e61ded59eaf Mon Sep 17 00:00:00 2001 From: "tianlei.richard" Date: Tue, 26 Mar 2024 21:34:06 +0800 Subject: [PATCH] bugfix: fix transformed texture coordinates. --- src/common.h | 4 ++-- src/main.cc | 3 ++- src/mesh.cc | 30 ++++++++++++++++------- src/mesh.h | 9 +++++-- src/minus_renderer.cc | 56 +++++++++++++++++++++++++++---------------- src/minus_renderer.h | 3 ++- src/util/math_util.h | 14 ++++------- 7 files changed, 74 insertions(+), 45 deletions(-) diff --git a/src/common.h b/src/common.h index ea36e91..f538f33 100644 --- a/src/common.h +++ b/src/common.h @@ -15,9 +15,9 @@ using TransformMatrix = Eigen::Matrix; typedef struct Vertex { Vertex(const Point3d &p, const Vector3d &n, const Point2d &tex_coor) - : position(p), normal(n), texture_coordinate(tex_coor) {} + : position(p), normal(n), texture_coordinate({tex_coor, 1.}) {} Point3d position; Vector3d normal; - Point2d texture_coordinate; + std::pair texture_coordinate; } Vertex; diff --git a/src/main.cc b/src/main.cc index 3be5589..e208554 100644 --- a/src/main.cc +++ b/src/main.cc @@ -19,6 +19,7 @@ int main(int argc, char *argv[]) { spdlog::set_level(spdlog::level::trace); const std::string obj_path{argv[1]}; + const std::string texture_path{argv[2]}; const int resolution_width = 640; const int resolution_height = 320; @@ -93,7 +94,7 @@ int main(int argc, char *argv[]) { Camera camera{Vector3d{0, 1, 0}}; MinusRenderer renderer{near, far, fov, aspect_ratio}; - renderer.load_mesh(obj_path); + renderer.load_mesh(obj_path, {texture_path}); TransformMatrix translate{ {1., 0., 0., 3.}, {0., 1., 0., 3.}, {0., 0., 1., -4.}, {0., 0., 0., 1.}}; renderer.model_transform(translate); diff --git a/src/mesh.cc b/src/mesh.cc index d89d794..82ce9a0 100644 --- a/src/mesh.cc +++ b/src/mesh.cc @@ -16,7 +16,13 @@ Mesh::Mesh(const std::vector> &vertices, const std::shared_ptr &phone_material) : vertices_(vertices), primitives_(primitives), material_(phone_material) {} -Point2d Mesh::get_texture_coordinate(const unsigned int index) const { +const std::pair & +Mesh::get_texture_coordinate(const unsigned int index) const { + return (vertices_[index])->texture_coordinate; +} + +std::pair & +Mesh::get_texture_coordinate(const unsigned int index) { return (vertices_[index])->texture_coordinate; } @@ -24,12 +30,13 @@ Point3d Mesh::get_normal_vector(const unsigned int index) const { return (vertices_[index])->normal; } -std::vector Mesh::load_mesh(const std::string &file_path) { - const auto &obj_path = std::filesystem::path(file_path); - const auto &material_directory = obj_path.parent_path(); +std::vector Mesh::load_mesh(const std::string &obj_path, + const std::vector texture_path) { + const auto &obj_file_path = std::filesystem::path(obj_path); + const auto &material_directory = obj_file_path.parent_path(); objl::Loader loader{}; - assert(loader.LoadFile(file_path.c_str())); + assert(loader.LoadFile(obj_path.c_str())); std::vector res; for (const auto &mesh : loader.LoadedMeshes) { @@ -50,12 +57,17 @@ std::vector Mesh::load_mesh(const std::string &file_path) { mesh.Indices[i + 1], mesh.Indices[i + 2])); } - std::vector texture_path; - if (!mesh.MeshMaterial.map_Kd.empty()) { - texture_path.push_back(material_directory / mesh.MeshMaterial.map_Kd); + std::vector texture_file_path; + if (!texture_path.empty()) { + for (const auto &p : texture_path) { + texture_file_path.push_back(std::filesystem::path(p)); + } + } else if (!mesh.MeshMaterial.map_Kd.empty()) { + texture_file_path.push_back(material_directory / + mesh.MeshMaterial.map_Kd); } auto material = - std::shared_ptr(new PhoneMaterial(texture_path)); + std::shared_ptr(new PhoneMaterial(texture_file_path)); res.push_back(Mesh(vertices, primitives, material)); } diff --git a/src/mesh.h b/src/mesh.h index 85b9795..6a780d3 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -16,10 +16,15 @@ public: const std::vector &primitives, const std::shared_ptr &phone_material); - static std::vector load_mesh(const std::string &file_path); + static std::vector load_mesh( + const std::string &obj_path, + const std::vector texture_path = std::vector{}); public: - Point2d get_texture_coordinate(const unsigned int index) const; + const std::pair & + get_texture_coordinate(const unsigned int index) const; + std::pair &get_texture_coordinate(const unsigned int index); + Point3d get_normal_vector(const unsigned int index) const; const std::vector &get_primitives() const { return primitives_; }; diff --git a/src/minus_renderer.cc b/src/minus_renderer.cc index 334308c..899200b 100644 --- a/src/minus_renderer.cc +++ b/src/minus_renderer.cc @@ -83,7 +83,8 @@ MinusRenderer::calculate_barycentric_coordinate(const Triangle &t, void MinusRenderer::model_transform(const TransformMatrix &mtx) { for (auto &m : meshes_) { for (auto &t : m.get_primitives()) { - t.set_points(apply_transform(mtx, t.get_vertex_position())); + const auto &[res, _] = apply_transform(mtx, t.get_vertex_position()); + t.set_points(res); } } } @@ -91,7 +92,8 @@ void MinusRenderer::model_transform(const TransformMatrix &mtx) { void MinusRenderer::view_transform(const TransformMatrix &mtx) { for (auto &m : meshes_) { for (auto &t : m.get_primitives()) { - t.set_points(apply_transform(mtx, t.get_vertex_position())); + const auto &[res, _] = apply_transform(mtx, t.get_vertex_position()); + t.set_points(res); } } } @@ -105,9 +107,17 @@ cv::Mat MinusRenderer::render(const int resolution_width, for (int i = 0; i < primitives.size(); ++i) { auto &t = primitives[i]; const auto &triangle_vertices = t.get_vertex_position(); - auto tmp = apply_transform(projection_matrix_, triangle_vertices); - t.set_points(apply_transform( - view_port_transform(resolution_width, resolution_height), tmp)); + const auto &[projective_res, w] = + apply_transform(projection_matrix_, triangle_vertices); + const auto &indices = t.get_vertex_index(); + for (int j = 0; j < indices.size(); ++j) { + auto &uv = m.get_texture_coordinate(indices[j]); + uv.second = w[j]; + } + const auto &[res, _] = apply_transform( + view_port_transform(resolution_width, resolution_height), + projective_res); + t.set_points(res); } } const auto &shading_points = rasterizer.rasterize(meshes); @@ -131,23 +141,26 @@ cv::Mat MinusRenderer::render(const int resolution_width, triangle, Point2d{x + 0.5, y + 0.5}); const auto &triangle_indices = triangle.get_vertex_index(); - std::vector triangle_uv; + std::vector triangle_uv; + std::vector w_coeff; for (int j = 0; j < triangle_indices.size(); ++j) { - const auto &uv = mesh.get_texture_coordinate(triangle_indices[j]); - triangle_uv.push_back(Point3d{uv.x(), uv.y(), 1}); + const auto &[uv, w] = + mesh.get_texture_coordinate(triangle_indices[j]); + triangle_uv.push_back(uv); + w_coeff.push_back(w); } - auto projective_uv = apply_transform(projection_matrix_, triangle_uv); // TODO(tianlei): 推导一下为什么需要除以下面这个 - auto w_reciprocal = alpha * (projective_uv[0].z()) + - beta * (projective_uv[1].z()) + - gamma * (2 * projective_uv[2].z()); - Point2d pixel_uv{ - (alpha * projective_uv[0].x() + beta * projective_uv[1].x() + - gamma * projective_uv[2].x()) / - w_reciprocal, - (alpha * projective_uv[0].y() + beta * projective_uv[1].y() + - gamma * projective_uv[2].y()) / - w_reciprocal}; + auto w_reciprocal = alpha * (1. / w_coeff[0]) + + beta * (1. / w_coeff[1]) + + gamma * (1. / w_coeff[2]); + Point2d pixel_uv{(alpha * (triangle_uv[0].x() / w_coeff[0]) + + beta * (triangle_uv[1].x() / w_coeff[1]) + + gamma * (triangle_uv[2].x() / w_coeff[2])) / + w_reciprocal, + (alpha * (triangle_uv[0].y() / w_coeff[0]) + + beta * (triangle_uv[1].y() / w_coeff[1]) + + gamma * (triangle_uv[2].y() / w_coeff[2])) / + w_reciprocal}; auto &material = mesh.get_material(); pixel_color = material->sample_texture(0, pixel_uv); @@ -158,8 +171,9 @@ cv::Mat MinusRenderer::render(const int resolution_width, return color_image; } -void MinusRenderer::load_mesh(const std::string &file_path) { - meshes_ = Mesh::load_mesh(file_path); +void MinusRenderer::load_mesh(const std::string &obj_path, + const std::vector texture_path) { + meshes_ = Mesh::load_mesh(obj_path, texture_path); spdlog::info("Mesh size: {}, the primitives size of the first mesh: {}", meshes_.size(), meshes_.front().get_primitives().size()); } diff --git a/src/minus_renderer.h b/src/minus_renderer.h index a0f92f7..2113d44 100644 --- a/src/minus_renderer.h +++ b/src/minus_renderer.h @@ -10,7 +10,8 @@ class MinusRenderer { public: MinusRenderer(float near, float far, float fov, float aspect_ratio); - void load_mesh(const std::string &file_path); + void load_mesh(const std::string &obj_path, + const std::vector texture_path); public: cv::Mat render(const int resolution_width, const int resolution_height); diff --git a/src/util/math_util.h b/src/util/math_util.h index dbaf968..83cc57c 100644 --- a/src/util/math_util.h +++ b/src/util/math_util.h @@ -11,18 +11,14 @@ bool fequal(const FloatType a, const FloatType b) { } template -std::vector> +std::pair>, std::vector> apply_transform(const TransformMatrix &mtx, const std::vector> &points) { - std::vector> res; + std::pair>, std::vector> res{}; for (const auto &p : points) { - auto tmp_p = Eigen::Vector(); - 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()); + const auto &transform_res = mtx * p.homogeneous(); + res.first.push_back(transform_res.hnormalized()); + res.second.push_back(transform_res.w()); } return res; }