bugfix: fix transformed texture coordinates.
This commit is contained in:
parent
18bb7e4cd7
commit
658b1ed377
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
30
src/mesh.cc
30
src/mesh.cc
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_; };
|
||||||
|
|
|
@ -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,23 +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()) +
|
auto w_reciprocal = alpha * (1. / w_coeff[0]) +
|
||||||
beta * (projective_uv[1].z()) +
|
beta * (1. / w_coeff[1]) +
|
||||||
gamma * (2 * projective_uv[2].z());
|
gamma * (1. / w_coeff[2]);
|
||||||
Point2d pixel_uv{
|
Point2d pixel_uv{(alpha * (triangle_uv[0].x() / w_coeff[0]) +
|
||||||
(alpha * projective_uv[0].x() + beta * projective_uv[1].x() +
|
beta * (triangle_uv[1].x() / w_coeff[1]) +
|
||||||
gamma * projective_uv[2].x()) /
|
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]) +
|
||||||
w_reciprocal};
|
gamma * (triangle_uv[2].y() / w_coeff[2])) /
|
||||||
|
w_reciprocal};
|
||||||
auto &material = mesh.get_material();
|
auto &material = mesh.get_material();
|
||||||
|
|
||||||
pixel_color = material->sample_texture(0, pixel_uv);
|
pixel_color = material->sample_texture(0, pixel_uv);
|
||||||
|
@ -158,8 +171,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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue