bugfix: fix transformed texture coordinates.

This commit is contained in:
tianlei.richard 2024-03-26 21:34:06 +08:00
parent 18bb7e4cd7
commit 590c27988e
7 changed files with 75 additions and 45 deletions

View File

@ -15,9 +15,9 @@ using TransformMatrix = Eigen::Matrix<double, 4, 4>;
typedef struct Vertex { typedef struct Vertex {
Vertex(const Point3d &p, const Vector3d &n, const Point2d &tex_coor) 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; Point3d position;
Vector3d normal; Vector3d normal;
Point2d texture_coordinate; std::pair<Point2d, double> texture_coordinate;
} Vertex; } Vertex;

View File

@ -19,6 +19,7 @@ int main(int argc, char *argv[]) {
spdlog::set_level(spdlog::level::trace); spdlog::set_level(spdlog::level::trace);
const std::string obj_path{argv[1]}; const std::string obj_path{argv[1]};
const std::string texture_path{argv[2]};
const int resolution_width = 640; const int resolution_width = 640;
const int resolution_height = 320; const int resolution_height = 320;
@ -93,7 +94,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};
renderer.load_mesh(obj_path); renderer.load_mesh(obj_path, {texture_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);

View File

@ -16,7 +16,13 @@ Mesh::Mesh(const std::vector<std::shared_ptr<Vertex>> &vertices,
const std::shared_ptr<PhoneMaterial> &phone_material) const std::shared_ptr<PhoneMaterial> &phone_material)
: vertices_(vertices), primitives_(primitives), material_(phone_material) {} : vertices_(vertices), primitives_(primitives), material_(phone_material) {}
Point2d Mesh::get_texture_coordinate(const unsigned int index) const { const std::pair<Point2d, double> &
Mesh::get_texture_coordinate(const unsigned int index) const {
return (vertices_[index])->texture_coordinate;
}
std::pair<Point2d, double> &
Mesh::get_texture_coordinate(const unsigned int index) {
return (vertices_[index])->texture_coordinate; return (vertices_[index])->texture_coordinate;
} }
@ -24,12 +30,13 @@ Point3d Mesh::get_normal_vector(const unsigned int index) const {
return (vertices_[index])->normal; return (vertices_[index])->normal;
} }
std::vector<Mesh> Mesh::load_mesh(const std::string &file_path) { std::vector<Mesh> Mesh::load_mesh(const std::string &obj_path,
const auto &obj_path = std::filesystem::path(file_path); const std::vector<std::string> texture_path) {
const auto &material_directory = obj_path.parent_path(); const auto &obj_file_path = std::filesystem::path(obj_path);
const auto &material_directory = obj_file_path.parent_path();
objl::Loader loader{}; objl::Loader loader{};
assert(loader.LoadFile(file_path.c_str())); assert(loader.LoadFile(obj_path.c_str()));
std::vector<Mesh> res; std::vector<Mesh> res;
for (const auto &mesh : loader.LoadedMeshes) { for (const auto &mesh : loader.LoadedMeshes) {
@ -50,12 +57,17 @@ std::vector<Mesh> Mesh::load_mesh(const std::string &file_path) {
mesh.Indices[i + 1], mesh.Indices[i + 2])); mesh.Indices[i + 1], mesh.Indices[i + 2]));
} }
std::vector<std::filesystem::path> texture_path; std::vector<std::filesystem::path> texture_file_path;
if (!mesh.MeshMaterial.map_Kd.empty()) { if (!texture_path.empty()) {
texture_path.push_back(material_directory / mesh.MeshMaterial.map_Kd); 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 = auto material =
std::shared_ptr<PhoneMaterial>(new PhoneMaterial(texture_path)); std::shared_ptr<PhoneMaterial>(new PhoneMaterial(texture_file_path));
res.push_back(Mesh(vertices, primitives, material)); res.push_back(Mesh(vertices, primitives, material));
} }

View File

@ -16,10 +16,15 @@ public:
const std::vector<Triangle> &primitives, const std::vector<Triangle> &primitives,
const std::shared_ptr<PhoneMaterial> &phone_material); const std::shared_ptr<PhoneMaterial> &phone_material);
static std::vector<Mesh> load_mesh(const std::string &file_path); static std::vector<Mesh> load_mesh(
const std::string &obj_path,
const std::vector<std::string> texture_path = std::vector<std::string>{});
public: public:
Point2d get_texture_coordinate(const unsigned int index) const; const std::pair<Point2d, double> &
get_texture_coordinate(const unsigned int index) const;
std::pair<Point2d, double> &get_texture_coordinate(const unsigned int index);
Point3d get_normal_vector(const unsigned int index) const; Point3d get_normal_vector(const unsigned int index) const;
const std::vector<Triangle> &get_primitives() const { return primitives_; }; const std::vector<Triangle> &get_primitives() const { return primitives_; };

View File

@ -83,7 +83,8 @@ MinusRenderer::calculate_barycentric_coordinate(const Triangle &t,
void MinusRenderer::model_transform(const TransformMatrix &mtx) { void MinusRenderer::model_transform(const TransformMatrix &mtx) {
for (auto &m : meshes_) { for (auto &m : meshes_) {
for (auto &t : m.get_primitives()) { 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) { void MinusRenderer::view_transform(const TransformMatrix &mtx) {
for (auto &m : meshes_) { for (auto &m : meshes_) {
for (auto &t : m.get_primitives()) { 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) { for (int i = 0; i < primitives.size(); ++i) {
auto &t = primitives[i]; auto &t = primitives[i];
const auto &triangle_vertices = t.get_vertex_position(); const auto &triangle_vertices = t.get_vertex_position();
auto tmp = apply_transform(projection_matrix_, triangle_vertices); const auto &[projective_res, w] =
t.set_points(apply_transform( apply_transform(projection_matrix_, triangle_vertices);
view_port_transform(resolution_width, resolution_height), tmp)); 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); const auto &shading_points = rasterizer.rasterize(meshes);
@ -131,22 +141,26 @@ cv::Mat MinusRenderer::render(const int resolution_width,
triangle, Point2d{x + 0.5, y + 0.5}); triangle, Point2d{x + 0.5, y + 0.5});
const auto &triangle_indices = triangle.get_vertex_index(); const auto &triangle_indices = triangle.get_vertex_index();
std::vector<Point3d> triangle_uv; std::vector<Point2d> triangle_uv;
std::vector<double> w_coeff;
for (int j = 0; j < triangle_indices.size(); ++j) { for (int j = 0; j < triangle_indices.size(); ++j) {
const auto &uv = mesh.get_texture_coordinate(triangle_indices[j]); const auto &[uv, w] =
triangle_uv.push_back(Point3d{uv.x(), uv.y(), 1}); 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): 推导一下为什么需要除以下面这个 // TODO(tianlei): 推导一下为什么需要除以下面这个
auto w_reciprocal = alpha * (projective_uv[0].z()) + // 在 screen space 对 1/w 的插值就相当于在 world space 对 w 的插值
beta * (projective_uv[1].z()) + auto w_reciprocal = alpha * (1. / w_coeff[0]) +
gamma * (2 * projective_uv[2].z()); beta * (1. / w_coeff[1]) +
Point2d pixel_uv{ gamma * (1. / w_coeff[2]);
(alpha * projective_uv[0].x() + beta * projective_uv[1].x() + Point2d pixel_uv{(alpha * (triangle_uv[0].x() / w_coeff[0]) +
gamma * projective_uv[2].x()) / beta * (triangle_uv[1].x() / w_coeff[1]) +
gamma * (triangle_uv[2].x() / w_coeff[2])) /
w_reciprocal, w_reciprocal,
(alpha * projective_uv[0].y() + beta * projective_uv[1].y() + (alpha * (triangle_uv[0].y() / w_coeff[0]) +
gamma * projective_uv[2].y()) / beta * (triangle_uv[1].y() / w_coeff[1]) +
gamma * (triangle_uv[2].y() / w_coeff[2])) /
w_reciprocal}; w_reciprocal};
auto &material = mesh.get_material(); auto &material = mesh.get_material();
@ -158,8 +172,9 @@ cv::Mat MinusRenderer::render(const int resolution_width,
return color_image; return color_image;
} }
void MinusRenderer::load_mesh(const std::string &file_path) { void MinusRenderer::load_mesh(const std::string &obj_path,
meshes_ = Mesh::load_mesh(file_path); const std::vector<std::string> texture_path) {
meshes_ = Mesh::load_mesh(obj_path, texture_path);
spdlog::info("Mesh size: {}, the primitives size of the first mesh: {}", spdlog::info("Mesh size: {}, the primitives size of the first mesh: {}",
meshes_.size(), meshes_.front().get_primitives().size()); meshes_.size(), meshes_.front().get_primitives().size());
} }

View File

@ -10,7 +10,8 @@
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); void load_mesh(const std::string &obj_path,
const std::vector<std::string> texture_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);

View File

@ -11,18 +11,14 @@ bool fequal(const FloatType a, const FloatType b) {
} }
template <int d> template <int d>
std::vector<Eigen::Vector<double, d>> std::pair<std::vector<Eigen::Vector<double, d>>, std::vector<double>>
apply_transform(const TransformMatrix &mtx, apply_transform(const TransformMatrix &mtx,
const std::vector<Eigen::Vector<double, d>> &points) { const std::vector<Eigen::Vector<double, d>> &points) {
std::vector<Eigen::Vector<double, d>> res; std::pair<std::vector<Eigen::Vector<double, d>>, std::vector<double>> res{};
for (const auto &p : points) { for (const auto &p : points) {
auto tmp_p = Eigen::Vector<double, 1 + d>(); const auto &transform_res = mtx * p.homogeneous();
tmp_p << p, 1.; res.first.push_back(transform_res.hnormalized());
tmp_p = mtx * tmp_p; res.second.push_back(transform_res.w());
if (!fequal(tmp_p.w(), 1.)) {
tmp_p /= tmp_p.w();
}
res.push_back(tmp_p.template head<d>());
} }
return res; return res;
} }