diff --git a/src/camera.cc b/src/camera.cc index e5ca6e7..70e4896 100644 --- a/src/camera.cc +++ b/src/camera.cc @@ -9,7 +9,10 @@ Camera::Camera(const Vector3d &up) : gaze_(Vector3d::Identity()), up_(up), camera_speed_(0.3) {} Camera::Camera(const Vector3d &up, const float camera_speed) - : gaze_(Vector3d::Identity()), up_(up), camera_speed_(camera_speed) {} + : gaze_(Vector3d{0, 0, 1}), up_(up), camera_speed_(camera_speed) { + spdlog::debug("Gaze: ({}, {}, {}), Up: ({}, {}, {})", gaze_.x(), gaze_.y(), + gaze_.z(), up_.x(), up_.y(), up_.z()); +} void Camera::move(const Point3d &offset) { position_ = (offset * camera_speed_); diff --git a/src/common.h b/src/common.h index ff1c806..89f6c1e 100644 --- a/src/common.h +++ b/src/common.h @@ -21,11 +21,3 @@ typedef struct Vertex { Vector3d normal; Point2d texture_coordinate; // Of course, in world space } Vertex; - -typedef struct Lamp { - Lamp(const Point3d &pos, const double intensity) - : position(pos), intensity(intensity) {} - - Point3d position; - double intensity; -} Lamp; diff --git a/src/lamp.cc b/src/lamp.cc new file mode 100644 index 0000000..223cc0c --- /dev/null +++ b/src/lamp.cc @@ -0,0 +1,15 @@ +// Copyright 2024 Bytedance Inc. All Rights Reserved. +// Author: tianlei.richard@bytedance.com (tianlei.richard) + +#include "lamp.h" + +PointLamp::PointLamp(const Point3d &pos, const double intensity) + : position_(pos), intensity_(intensity) {} + +double PointLamp::get_intensity() const { return intensity_; } + +const Point3d &PointLamp::get_position() const { return position_; } + +void PointLamp::transform(const TransformMatrix &mtx) { + position_ = (mtx * position_.homogeneous()).hnormalized(); +} diff --git a/src/lamp.h b/src/lamp.h new file mode 100644 index 0000000..82403da --- /dev/null +++ b/src/lamp.h @@ -0,0 +1,21 @@ +// Copyright 2024 Bytedance Inc. All Rights Reserved. +// Author: tianlei.richard@bytedance.com (tianlei.richard) + +#pragma once + +#include "common.h" + +class PointLamp { +public: + PointLamp(const Point3d &pos, const double intensity); + +public: + void transform(const TransformMatrix &mtx); + + double get_intensity() const; + const Point3d &get_position() const; + +private: + Point3d position_; + double intensity_; +}; diff --git a/src/main.cc b/src/main.cc index 2568411..a06f8a9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -34,11 +34,11 @@ int main(int argc, char *argv[]) { const std::string obj_path{result["obj"].as()}; const std::string diffuse_map_path{result["diffuse_map"].as()}; - const int resolution_width = 1280; - const int resolution_height = 720; - const int far = -100; + const int resolution_width = 960; + const int resolution_height = 540; + const int far = -1000; const int near = -1; - const float fov = 120. / 180. * M_PI; + const float fov = 150. / 180. * M_PI; const float aspect_ratio = static_cast(resolution_width) / static_cast(resolution_height); @@ -111,6 +111,7 @@ int main(int argc, char *argv[]) { MinusRenderer renderer{near, far, fov, aspect_ratio}; renderer.set_meshes( Mesh::load_mesh(obj_path, {{Texture::DiffuseMap, diffuse_map_path}})); + renderer.set_lamps({std::make_shared(Point3d{1, 1, -1}, 6.)}); TransformMatrix translate{ {1., 0., 0., 3.}, {0., 1., 0., 3.}, {0., 0., 1., -5.}, {0., 0., 0., 1.}}; renderer.model_transform(translate); diff --git a/src/mesh.cc b/src/mesh.cc index 36aafc7..6f8c621 100644 --- a/src/mesh.cc +++ b/src/mesh.cc @@ -87,8 +87,7 @@ std::vector Mesh::load_mesh( std::make_shared(material_directory / mesh.MeshMaterial.map_Kd)}); } - auto material = std::make_shared( - textures, std::make_shared(Point3d{}, 16.)); + auto material = std::make_shared(textures); res.push_back(Mesh(vertices, indices, material)); } diff --git a/src/minus_renderer.cc b/src/minus_renderer.cc index 182698d..a65468d 100644 --- a/src/minus_renderer.cc +++ b/src/minus_renderer.cc @@ -8,11 +8,11 @@ #include #include "minus_renderer.h" -#include "rasterizer.h" MinusRenderer::MinusRenderer(float near, float far, float fov, float aspect_ratio) - : near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio) { + : near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio), + rasterizer_(std::make_unique()) { spdlog::info("near: {}, far: {}, fov: {}, aspect ratio: {}", near_, far_, fov_, aspect_ratio_); } @@ -21,6 +21,11 @@ void MinusRenderer::set_meshes(const std::vector &meshes) { meshes_ = meshes; } +void MinusRenderer::set_lamps( + const std::vector> &lamps) { + lamps_ = lamps; +} + float MinusRenderer::calculate_height(const float fov, const float near) { return std::fabs(near) * std::tan(fov * 0.5) * 2; } @@ -33,16 +38,15 @@ std::pair MinusRenderer::construct_transform(const int resolution_width, const int resolution_height) const { // Squish Transform - const auto &squish_transform = - TransformMatrix{{near_, 0, 0, 0}, - {0, near_, 0, 0}, - {0, 0, near_ + far_, -near_ * far_}, - {0, 0, 1, 0}}; - const auto &inv_squish_transform = - TransformMatrix{{1, 0, 0, 0}, - {0, 1, 0, 0}, - {0, 0, 0, near_}, - {0, 0, -1. / far_, (near_ + far_) / far_}}; + const TransformMatrix &squish_transform{{near_, 0, 0, 0}, + {0, near_, 0, 0}, + {0, 0, near_ + far_, -near_ * far_}, + {0, 0, 1, 0}}; + const TransformMatrix &inv_squish_transform{ + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 0, near_}, + {0, 0, -1. / far_, (near_ + far_) / far_}}; // Orthographic transform const float height = calculate_height(fov_, near_); @@ -52,7 +56,7 @@ MinusRenderer::construct_transform(const int resolution_width, const float top = height * 0.5; const float bottom = -top; - const auto &ortho_transform = + const TransformMatrix &ortho_transform = TransformMatrix{{2 / (right - left), 0, 0, 0}, {0, 2 / (top - bottom), 0, 0}, {0, 0, 2 / std::fabs(far_ - near_), 0}, @@ -61,19 +65,19 @@ MinusRenderer::construct_transform(const int resolution_width, {0, 1, 0, -0.5 * (top - bottom)}, {0, 0, 1, -0.5 * (far_ - near_)}, {0, 0, 0, 1}}; - const auto &inv_ortho_transform = TransformMatrix{ + const TransformMatrix &inv_ortho_transform{ {(right - left) / 2., 0, 0, 0.5 * (right - left)}, {0, (top - bottom) / 2., 0, 0.5 * (top - bottom)}, {0, 0, std::fabs(far_ - near_) / 2., 0.5 * (far_ - near_)}, {0, 0, 0, 1}}; // View port transform - const auto &view_port_transform = - TransformMatrix{{resolution_width / 2., 0, 0, resolution_width * 0.5}, - {0, resolution_height / 2., 0, resolution_height * 0.5}, - {0, 0, 1, 0}, - {0, 0, 0, 1}}; - const auto &inv_view_port_transform = + const TransformMatrix &view_port_transform{ + {resolution_width / 2., 0, 0, resolution_width * 0.5}, + {0, resolution_height / 2., 0, resolution_height * 0.5}, + {0, 0, 1, 0}, + {0, 0, 0, 1}}; + const TransformMatrix &inv_view_port_transform = TransformMatrix{{2. / resolution_width, 0, 0, 0}, {0, 2. / resolution_height, 0, 0}, {0, 0, 1, 0}, @@ -136,20 +140,22 @@ cv::Mat MinusRenderer::render(const int resolution_width, const auto [ss_transform, inv_ss_transform] = construct_transform(resolution_width, resolution_height); - Rasterizer rasterizer{resolution_width, resolution_height}; + spdlog::trace("Rasterize."); + rasterizer_->reset(resolution_width, resolution_height); for (decltype(meshes_)::size_type 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); + rasterizer_->rasterize(mesh_index, primitives); } - const auto &shading_points = rasterizer.get_shading_points(); + 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); + spdlog::trace("Shading."); for (size_t y = 0; y < shading_points.size(); ++y) { for (size_t x = 0; x < shading_points[y].size(); ++x) { auto &pixel_color = @@ -195,7 +201,7 @@ cv::Mat MinusRenderer::render(const int resolution_width, const auto &material = mesh.get_material(); pixel_color = - material->shade(Vertex{position, normal, uv}, Point3d{}); + material->shade(Vertex{position, normal, uv}, Point3d{}, lamps_); } } } diff --git a/src/minus_renderer.h b/src/minus_renderer.h index 4dd2d6d..68762b3 100644 --- a/src/minus_renderer.h +++ b/src/minus_renderer.h @@ -3,16 +3,19 @@ #pragma once -#include "mesh.h" -#include "util/math_util.h" #include +#include "mesh.h" +#include "rasterizer.h" +#include "util/math_util.h" + class MinusRenderer { public: MinusRenderer(float near, float far, float fov, float aspect_ratio); public: void set_meshes(const std::vector &meshes); + void set_lamps(const std::vector> &lamps); cv::Mat render(const int resolution_width, const int resolution_height); @@ -38,4 +41,7 @@ private: float aspect_ratio_; std::vector meshes_; + std::vector> lamps_; + + std::unique_ptr rasterizer_; }; diff --git a/src/phone_material.cc b/src/phone_material.cc index 770093d..9447d22 100644 --- a/src/phone_material.cc +++ b/src/phone_material.cc @@ -8,52 +8,53 @@ PhoneMaterial::PhoneMaterial( const std::unordered_map> - &textures, - const std::shared_ptr &lamp) - : point_lamp_(lamp), textures_(textures), Ka(cv::Vec3b{24, 24, 24}), - Ks(cv::Vec3b(48, 48, 48)), + &textures) + : textures_(textures), Ka(0.2), Ks(1.6), 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 { +cv::Vec3b PhoneMaterial::shade( + const Vertex &shading_point, const Point3d &view_point, + const std::vector> &lamps) const { cv::Vec3b shading_res{0, 0, 0}; + for (const auto &lamp : lamps) { + // Diffuse + assert(textures_.find(Texture::DiffuseMap) != textures_.end()); + const auto &diffuse_map = textures_.at(Texture::DiffuseMap); + const auto &base_color = + diffuse_map->sample(shading_point.texture_coordinate); - // Diffuse - assert(textures_.find(Texture::DiffuseMap) != textures_.end()); - const auto &diffuse_map = textures_.at(Texture::DiffuseMap); - const auto &Kd = diffuse_map->sample(shading_point.texture_coordinate); + const auto &point2light_vector = + (lamp->get_position() - shading_point.position).normalized(); + const auto &point2light_distance = + (shading_point.position - lamp->get_position()).norm(); + const auto ¤t_intensity = + lamp->get_intensity() / (point2light_distance * point2light_distance); - const auto &point2light_vector = - (point_lamp_->position - shading_point.position).normalized(); - const auto &point2light_distance = - (shading_point.position - point_lamp_->position).norm(); - const auto ¤t_intensity = - point_lamp_->intensity / (point2light_distance * point2light_distance); + const auto diffuse_coeff = + current_intensity * + std::max(shading_point.normal.dot(point2light_vector), 0.); + cv::Vec3b diffuse_color = base_color * diffuse_coeff; + shading_res += diffuse_color; - const auto diffuse_coeff = - current_intensity * - std::max(shading_point.normal.dot(point2light_vector), 0.); - cv::Vec3b diffuse_color = Kd * diffuse_coeff; - shading_res += diffuse_color; + // Specular + const auto &point2view_vector = + (view_point - shading_point.position).normalized(); + const auto &half_vector = + (point2light_vector + point2view_vector).normalized(); + const auto specular_coeff = + current_intensity * + std::max(std::pow(shading_point.normal.dot(half_vector), + specular_attenuation_factor_), + 0.); + cv::Vec3b specular_color = Ks * specular_coeff; + shading_res += specular_color; - // Specular - const auto &point2view_vector = - (view_point - shading_point.position).normalized(); - const auto &half_vector = - (point2light_vector + point2view_vector).normalized(); - const auto specular_coeff = - current_intensity * - std::max(std::pow(shading_point.normal.dot(half_vector), - specular_attenuation_factor_), - 0.); - cv::Vec3b specular_color = Ks * specular_coeff; - shading_res += specular_color; - - // Ambient - cv::Vec3b ambient_color = Ka; - shading_res += ambient_color; + // Ambient + cv::Vec3b ambient_color = Ka * base_color; + shading_res += ambient_color; + } return shading_res; } diff --git a/src/phone_material.h b/src/phone_material.h index 3b5da5f..68da610 100644 --- a/src/phone_material.h +++ b/src/phone_material.h @@ -7,29 +7,29 @@ #include #include "common.h" +#include "lamp.h" #include "texture.h" // Bling-Phone Matrerial class PhoneMaterial { public: - PhoneMaterial(const std::unordered_map> &textures, - const std::shared_ptr &lamp); + explicit PhoneMaterial( + const std::unordered_map> + &textures); - cv::Vec3b shade(const Vertex &shading_point, const Point3d &view_point) const; + cv::Vec3b shade(const Vertex &shading_point, const Point3d &view_point, + const std::vector> &lamps) const; private: - static constexpr int defaultAttenuationFactor = 12; + static constexpr int defaultAttenuationFactor = 128; private: - std::shared_ptr point_lamp_; - std::unordered_map> textures_; // Ambient Related - cv::Vec3b Ka; + float Ka; // Specular Related - cv::Vec3b Ks; + float Ks; int specular_attenuation_factor_; }; diff --git a/src/rasterizer.cc b/src/rasterizer.cc index 7db233d..618d245 100644 --- a/src/rasterizer.cc +++ b/src/rasterizer.cc @@ -13,6 +13,8 @@ Rasterizer::Rasterizer(const int width, const int height) shading_points_(std::vector>( height, std::vector(width, RasterizerResult{}))) {} +Rasterizer::Rasterizer() : Rasterizer(960, 540) {} + void Rasterizer::rasterize(const int mesh_idx, const std::vector &primitives) { const int thread_num = 8; @@ -41,6 +43,7 @@ void Rasterizer::rasterize(const int mesh_idx, void Rasterizer::rasterize(const int mesh_idx, const std::vector &primitives, const int begin, const int end) { + spdlog::trace("Mesh index: {}, begin: {}, end: {}", mesh_idx, begin, end); for (int t_id = begin; t_id < end; ++t_id) { const auto &t = primitives[t_id]; const auto &triangle_points = t.get_vertex_position(); @@ -64,6 +67,7 @@ void Rasterizer::rasterize(const int mesh_idx, } } } + spdlog::trace("Triangles in [{}, {}) is done.", begin, end); } const std::vector> & @@ -71,7 +75,8 @@ Rasterizer::get_shading_points() const { return shading_points_; } -void Rasterizer::reset() { +void Rasterizer::reset(const int width, const int height) { + width_ = width, height_ = height; shading_points_ = std::vector>( height_, std::vector(width_, RasterizerResult{})); } diff --git a/src/rasterizer.h b/src/rasterizer.h index 2021db8..2e89f92 100644 --- a/src/rasterizer.h +++ b/src/rasterizer.h @@ -18,12 +18,13 @@ typedef struct RasterizerResult { class Rasterizer { public: Rasterizer(const int width, const int height); + Rasterizer(); public: void rasterize(const int mesh_idx, const std::vector &primitives); const std::vector> &get_shading_points() const; - void reset(); + void reset(const int width, const int height); private: static std::pair inside(const Point2d &p_screen, diff --git a/src/texture.cc b/src/texture.cc index 74f1922..c2b4bfe 100644 --- a/src/texture.cc +++ b/src/texture.cc @@ -13,7 +13,7 @@ Texture::Texture(const std::filesystem::path &texture_path) { throw std::filesystem::filesystem_error("File not found", std::error_code{}); } - texture_ = (cv::imread(texture_path.c_str())); + texture_ = cv::imread(texture_path.c_str()); spdlog::debug("Texture at {}, shape: ({},{})", texture_path.c_str(), texture_.cols, texture_.rows); } @@ -37,12 +37,24 @@ cv::Vec3b Texture::sample(const Point2d &texture_coordinate) { const int y_c = std::min(texture_.rows - 1, static_cast(y + 1.)); const float t_y = y - y_f; - const cv::Vec3b &a = texture_.at(y_f, x_f); - const cv::Vec3b &b = texture_.at(y_c, x_f); - const cv::Vec3b &c = texture_.at(y_f, x_c); - const cv::Vec3b &d = texture_.at(y_c, x_c); + auto in_range = [](auto index, auto range_begin, auto range_end) -> bool { + return index >= range_begin && index < range_end; + }; - res = bilerp(t_x, t_y, a, b, c, d); + if (in_range(t_x, 0., 1.) && in_range(t_y, 0., 1.) && + in_range(x_f, 0, texture_.cols) && in_range(x_c, 0, texture_.cols) && + in_range(y_f, 0, texture_.rows) && in_range(y_c, 0, texture_.rows)) { + const cv::Vec3b &a = texture_.at(y_f, x_f); + const cv::Vec3b &b = texture_.at(y_c, x_f); + const cv::Vec3b &c = texture_.at(y_f, x_c); + const cv::Vec3b &d = texture_.at(y_c, x_c); + res = bilerp(t_x, t_y, a, b, c, d); + } else { + spdlog::warn( + "Interpolation factor is invalid, {}, {}, {}, {}, {}, {}, ({}, {}).", + t_x, t_y, x_f, x_c, y_f, y_c, texture_coordinate.x(), + texture_coordinate.y()); + } return res; }