complete phone shading impl.

This commit is contained in:
tianlei.richard 2024-03-29 18:09:16 +08:00
parent af09ed9311
commit acd472bacf
11 changed files with 137 additions and 131 deletions

View File

@ -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<std::shared_ptr<Vertex>> &vertices,
const std::vector<Triangle> &primitives,
Mesh::Mesh(const std::vector<Vertex> &vertices,
const std::vector<std::array<size_t, 3>> &indices,
const std::shared_ptr<PhoneMaterial> &phone_material)
: vertices_(vertices), primitives_(primitives), material_(phone_material) {}
: vertices_(vertices), indices_(indices), material_(phone_material) {}
std::vector<std::shared_ptr<Vertex>>::size_type Mesh::get_vertex_size() const {
std::vector<Vertex> &Mesh::get_vertices() { return vertices_; }
std::vector<Vertex>::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<Triangle> &Mesh::get_primitives() const {
return primitives_;
};
std::vector<Triangle> Mesh::get_primitives() const {
std::vector<Triangle> primitives;
for (const auto &idx : indices_) {
primitives.push_back(Triangle(vertices_, idx[0], idx[1], idx[2]));
}
return primitives;
}
std::vector<Triangle> &Mesh::get_primitives() { return primitives_; };
std::shared_ptr<PhoneMaterial> &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<PhoneMaterial> &Mesh::get_material() const {
return material_;
@ -54,19 +62,19 @@ std::vector<Mesh> Mesh::load_mesh(
for (const auto &mesh : loader.LoadedMeshes) {
spdlog::info("Current mesh has {} vertices.", mesh.Vertices.size());
std::vector<std::shared_ptr<Vertex>> vertices;
std::vector<Vertex> vertices;
for (decltype(mesh.Vertices)::size_type i = 0; i < mesh.Vertices.size();
i++) {
vertices.push_back(std::make_shared<Vertex>(
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<Triangle> primitives;
std::vector<std::array<size_t, 3>> 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<Texture::TextureType, std::shared_ptr<Texture>> textures;
@ -82,7 +90,7 @@ std::vector<Mesh> Mesh::load_mesh(
auto material = std::make_shared<PhoneMaterial>(
textures, std::make_shared<Lamp>(Point3d{}, 16.));
res.push_back(Mesh(vertices, primitives, material));
res.push_back(Mesh(vertices, indices, material));
}
return res;
}

View File

@ -3,6 +3,7 @@
#pragma once
#include <array>
#include <memory>
#include <unordered_map>
@ -12,8 +13,8 @@
class Mesh {
public:
Mesh(const std::vector<std::shared_ptr<Vertex>> &vertices,
const std::vector<Triangle> &primitives,
Mesh(const std::vector<Vertex> &vertices,
const std::vector<std::array<size_t, 3>> &indices,
const std::shared_ptr<PhoneMaterial> &phone_material);
static std::vector<Mesh> load_mesh(
@ -21,22 +22,23 @@ public:
const std::unordered_map<Texture::TextureType, std::string> textures);
public:
std::vector<std::shared_ptr<Vertex>>::size_type get_vertex_size() const;
std::vector<Vertex> &get_vertices();
std::vector<Vertex>::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<Triangle> &get_primitives() const;
std::vector<Triangle> &get_primitives();
std::vector<Triangle> get_primitives() const;
Triangle get_primitive(const size_t index) const;
std::shared_ptr<PhoneMaterial> &get_material();
const std::shared_ptr<PhoneMaterial> &get_material() const;
private:
std::vector<std::shared_ptr<Vertex>> vertices_;
std::vector<Triangle> primitives_;
std::vector<Vertex> vertices_;
std::vector<std::array<size_t, 3>> indices_;
std::shared_ptr<PhoneMaterial> material_;
};

View File

@ -19,8 +19,6 @@ MinusRenderer::MinusRenderer(float near, float far, float fov,
void MinusRenderer::set_meshes(const std::vector<Mesh> &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<double, double, double>
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<Mesh> 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<double>::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<Point2d> triangle_uv;
std::vector<Point3d> triangle_normal;
@ -191,9 +179,7 @@ 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 = Point3d{alpha * triangle_normal[0].x() +
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() +
@ -201,12 +187,10 @@ cv::Mat MinusRenderer::render(const int resolution_width,
gamma * triangle_normal[2].y(),
alpha * triangle_normal[0].z() +
beta * triangle_normal[1].z() +
gamma * triangle_normal[2].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{});
}

View File

@ -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<double, double, double>
calculate_barycentric_coordinate(const Triangle &t, const Point2d &p);
calculate_barycentric_coordinate(const Triangle &t, const Point3d &p);
private:
std::pair<TransformMatrix, TransformMatrix>

View File

@ -10,19 +10,19 @@ PhoneMaterial::PhoneMaterial(
const std::unordered_map<Texture::TextureType, std::shared_ptr<Texture>>
&textures,
const std::shared_ptr<Lamp> &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 =

View File

@ -17,10 +17,10 @@ public:
std::shared_ptr<Texture>> &textures,
const std::shared_ptr<Lamp> &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<Lamp> point_lamp_;

View File

@ -13,11 +13,8 @@ Rasterizer::Rasterizer(const int width, const int height)
shading_points_(std::vector<std::vector<RasterizerResult>>(
height, std::vector<RasterizerResult>(width, RasterizerResult{}))) {}
std::vector<std::vector<RasterizerResult>>
Rasterizer::rasterize(const std::vector<Mesh> &meshes) {
for (int m_id = 0; m_id < meshes.size(); ++m_id) {
const auto &primitives = (meshes[m_id]).get_primitives();
void Rasterizer::rasterize(const int mesh_idx,
const std::vector<Triangle> &primitives) {
const int thread_num = 8;
std::vector<std::thread> rasterize_threads;
for (int begin = 0, offset = primitives.size() / thread_num,
@ -29,18 +26,16 @@ Rasterizer::rasterize(const std::vector<Mesh> &meshes) {
remainer -= 1;
}
rasterize_threads.push_back(std::thread(
[this, m_id](const std::vector<Triangle> &primitives, const int begin,
const int end) {
this->rasterize(m_id, primitives, begin, end);
[this, mesh_idx](const std::vector<Triangle> &primitives,
const int begin, const int end) {
this->rasterize(mesh_idx, primitives, begin, end);
},
(meshes[m_id]).get_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<std::vector<RasterizerResult>> &
Rasterizer::get_shading_points() const {
return shading_points_;
}
void Rasterizer::reset() {
shading_points_ = std::vector<std::vector<RasterizerResult>>(
height_, std::vector<RasterizerResult>(width_, RasterizerResult{}));
@ -80,7 +80,7 @@ std::pair<bool, double> 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()) +

View File

@ -21,9 +21,9 @@ public:
Rasterizer(const int width, const int height);
public:
std::vector<std::vector<RasterizerResult>>
rasterize(const std::vector<Mesh> &meshes);
void rasterize(const int mesh_idx, const std::vector<Triangle> &primitives);
const std::vector<std::vector<RasterizerResult>> &get_shading_points() const;
void reset();
private:

View File

@ -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<std::shared_ptr<Vertex>> &vertices,
unsigned int idx_a, unsigned int idx_b, unsigned int idx_c)
Triangle::Triangle(const std::vector<Vertex> &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<unsigned int> 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);
}

View File

@ -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<std::shared_ptr<Vertex>> &vertices,
unsigned int idx_a, unsigned int idx_b, unsigned int idx_c);
Triangle(const std::vector<Vertex> &vertices, unsigned int idx_a,
unsigned int idx_b, unsigned int idx_c);
public:
std::vector<unsigned int> 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<unsigned int, 3> indices_;

View File

@ -11,14 +11,21 @@ bool fequal(const FloatType a, const FloatType b) {
}
template <int d>
std::pair<std::vector<Eigen::Vector<double, d>>, std::vector<double>>
std::vector<Eigen::Vector<double, d>>
apply_transform(const TransformMatrix &mtx,
const std::vector<Eigen::Vector<double, d>> &points) {
std::pair<std::vector<Eigen::Vector<double, d>>, std::vector<double>> res{};
std::vector<Eigen::Vector<double, d>> 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 <int d>
void apply_transform_in_place(const TransformMatrix &mtx,
std::vector<Eigen::Vector<double, d>> &points) {
for (auto &p : points) {
p = mtx * p.homogeneous().hnormalized();
}
}