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 {
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<Point2d, double> texture_coordinate;
} Vertex;

View File

@ -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);

View File

@ -16,7 +16,13 @@ Mesh::Mesh(const std::vector<std::shared_ptr<Vertex>> &vertices,
const std::shared_ptr<PhoneMaterial> &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;
}
@ -24,12 +30,13 @@ 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) {
const auto &obj_path = std::filesystem::path(file_path);
const auto &material_directory = obj_path.parent_path();
std::vector<Mesh> Mesh::load_mesh(const std::string &obj_path,
const std::vector<std::string> 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<Mesh> res;
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]));
}
std::vector<std::filesystem::path> texture_path;
if (!mesh.MeshMaterial.map_Kd.empty()) {
texture_path.push_back(material_directory / mesh.MeshMaterial.map_Kd);
std::vector<std::filesystem::path> 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<PhoneMaterial>(new PhoneMaterial(texture_path));
std::shared_ptr<PhoneMaterial>(new PhoneMaterial(texture_file_path));
res.push_back(Mesh(vertices, primitives, material));
}

View File

@ -16,10 +16,15 @@ public:
const std::vector<Triangle> &primitives,
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:
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;
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) {
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,22 +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<Point3d> triangle_uv;
std::vector<Point2d> triangle_uv;
std::vector<double> 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()) /
// 在 screen space 对 1/w 的插值就相当于在 world space 对 w 的插值
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 * projective_uv[0].y() + beta * projective_uv[1].y() +
gamma * projective_uv[2].y()) /
(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();
@ -158,8 +172,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<std::string> 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());
}

View File

@ -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<std::string> texture_path);
public:
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>
std::vector<Eigen::Vector<double, d>>
std::pair<std::vector<Eigen::Vector<double, d>>, std::vector<double>>
apply_transform(const TransformMatrix &mtx,
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) {
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>());
const auto &transform_res = mtx * p.homogeneous();
res.first.push_back(transform_res.hnormalized());
res.second.push_back(transform_res.w());
}
return res;
}