incompleted phone shading impl.
This commit is contained in:
parent
590c27988e
commit
635a392a8d
|
@ -0,0 +1,7 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(cxxopts)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} INTERFACE)
|
||||||
|
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
|
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,7 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
# Third Party
|
# Third Party
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/cxxopts)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/eigen)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/eigen)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/imgui)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/imgui)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/spdlog)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/spdlog)
|
||||||
|
|
|
@ -12,4 +12,4 @@ file(GLOB_RECURSE CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR} *.cc)
|
||||||
|
|
||||||
add_executable(${CMAKE_PROJECT_NAME} ${CPP_FILES})
|
add_executable(${CMAKE_PROJECT_NAME} ${CPP_FILES})
|
||||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${OpenCV_INCLUDE_DIRS})
|
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${OpenCV_INCLUDE_DIRS})
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC OBJ_Loader spdlog imgui eigen ${OpenCV_LIBS})
|
target_link_libraries(${CMAKE_PROJECT_NAME} LINK_PUBLIC OBJ_Loader cxxopts spdlog imgui eigen ${OpenCV_LIBS})
|
||||||
|
|
12
src/common.h
12
src/common.h
|
@ -15,9 +15,17 @@ 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, 1.}) {}
|
: position(p), normal(n), texture_coordinate({tex_coor}) {}
|
||||||
|
|
||||||
Point3d position;
|
Point3d position;
|
||||||
Vector3d normal;
|
Vector3d normal;
|
||||||
std::pair<Point2d, double> texture_coordinate;
|
Point2d texture_coordinate; // Of course, in world space
|
||||||
} Vertex;
|
} Vertex;
|
||||||
|
|
||||||
|
typedef struct Lamp {
|
||||||
|
Lamp(const Point3d &pos, const double intensity)
|
||||||
|
: position(pos), intensity(intensity) {}
|
||||||
|
|
||||||
|
Point3d position;
|
||||||
|
double intensity;
|
||||||
|
} Lamp;
|
||||||
|
|
22
src/main.cc
22
src/main.cc
|
@ -9,6 +9,7 @@
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <SDL_opengl.h>
|
#include <SDL_opengl.h>
|
||||||
|
|
||||||
|
#include "cxxopts.hpp"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
|
@ -18,8 +19,22 @@
|
||||||
int main(int argc, char *argv[]) {
|
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]};
|
cxxopts::Options options("Minus Renderer",
|
||||||
const std::string texture_path{argv[2]};
|
"A minus soft rasterize renderer.");
|
||||||
|
|
||||||
|
options.add_options()("o,obj", "Object file path",
|
||||||
|
cxxopts::value<std::string>())(
|
||||||
|
"d,diffuse_map", "Diffuse map file path",
|
||||||
|
cxxopts::value<std::string>())("h,help", "Print usage");
|
||||||
|
|
||||||
|
auto result = options.parse(argc, argv);
|
||||||
|
if (result.count("help")) {
|
||||||
|
spdlog::info("{}", options.help());
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string obj_path{result["obj"].as<std::string>()};
|
||||||
|
const std::string diffuse_map_path{result["diffuse_map"].as<std::string>()};
|
||||||
|
|
||||||
const int resolution_width = 640;
|
const int resolution_width = 640;
|
||||||
const int resolution_height = 320;
|
const int resolution_height = 320;
|
||||||
|
@ -94,7 +109,8 @@ 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, {texture_path});
|
renderer.set_meshes(
|
||||||
|
Mesh::load_mesh(obj_path, {{Texture::DiffuseMap, diffuse_map_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);
|
||||||
|
|
43
src/mesh.cc
43
src/mesh.cc
|
@ -16,13 +16,12 @@ 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) {}
|
||||||
|
|
||||||
const std::pair<Point2d, double> &
|
std::vector<std::shared_ptr<Vertex>>::size_type Mesh::get_vertex_size() const {
|
||||||
Mesh::get_texture_coordinate(const unsigned int index) const {
|
return vertices_.size();
|
||||||
return (vertices_[index])->texture_coordinate;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Point2d, double> &
|
const decltype(Vertex::texture_coordinate) &
|
||||||
Mesh::get_texture_coordinate(const unsigned int index) {
|
Mesh::get_texture_coordinate(const unsigned int index) const {
|
||||||
return (vertices_[index])->texture_coordinate;
|
return (vertices_[index])->texture_coordinate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +29,21 @@ 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 &obj_path,
|
const std::vector<Triangle> &Mesh::get_primitives() const {
|
||||||
const std::vector<std::string> texture_path) {
|
return primitives_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Triangle> &Mesh::get_primitives() { return primitives_; };
|
||||||
|
|
||||||
|
std::shared_ptr<PhoneMaterial> &Mesh::get_material() { return material_; }
|
||||||
|
|
||||||
|
const std::shared_ptr<PhoneMaterial> &Mesh::get_material() const {
|
||||||
|
return material_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Mesh> Mesh::load_mesh(
|
||||||
|
const std::string &obj_path,
|
||||||
|
const std::unordered_map<Texture::TextureType, std::string> texture_path) {
|
||||||
const auto &obj_file_path = std::filesystem::path(obj_path);
|
const auto &obj_file_path = std::filesystem::path(obj_path);
|
||||||
const auto &material_directory = obj_file_path.parent_path();
|
const auto &material_directory = obj_file_path.parent_path();
|
||||||
|
|
||||||
|
@ -57,17 +69,18 @@ std::vector<Mesh> Mesh::load_mesh(const std::string &obj_path,
|
||||||
mesh.Indices[i + 1], mesh.Indices[i + 2]));
|
mesh.Indices[i + 1], mesh.Indices[i + 2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::filesystem::path> texture_file_path;
|
std::unordered_map<Texture::TextureType, std::shared_ptr<Texture>> textures;
|
||||||
if (!texture_path.empty()) {
|
if (!texture_path.empty()) {
|
||||||
for (const auto &p : texture_path) {
|
for (const auto &[type, path] : texture_path) {
|
||||||
texture_file_path.push_back(std::filesystem::path(p));
|
textures.insert({type, std::make_shared<Texture>(path)});
|
||||||
}
|
}
|
||||||
} else if (!mesh.MeshMaterial.map_Kd.empty()) {
|
} else if (!mesh.MeshMaterial.map_Kd.empty()) {
|
||||||
texture_file_path.push_back(material_directory /
|
textures.insert({Texture::DiffuseMap,
|
||||||
mesh.MeshMaterial.map_Kd);
|
std::make_shared<Texture>(material_directory /
|
||||||
|
mesh.MeshMaterial.map_Kd)});
|
||||||
}
|
}
|
||||||
auto material =
|
auto material = std::make_shared<PhoneMaterial>(
|
||||||
std::shared_ptr<PhoneMaterial>(new PhoneMaterial(texture_file_path));
|
textures, std::make_shared<Lamp>(Point3d{}, 192.));
|
||||||
|
|
||||||
res.push_back(Mesh(vertices, primitives, material));
|
res.push_back(Mesh(vertices, primitives, material));
|
||||||
}
|
}
|
||||||
|
|
19
src/mesh.h
19
src/mesh.h
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "phone_material.h"
|
#include "phone_material.h"
|
||||||
|
@ -18,22 +18,21 @@ public:
|
||||||
|
|
||||||
static std::vector<Mesh> load_mesh(
|
static std::vector<Mesh> load_mesh(
|
||||||
const std::string &obj_path,
|
const std::string &obj_path,
|
||||||
const std::vector<std::string> texture_path = std::vector<std::string>{});
|
const std::unordered_map<Texture::TextureType, std::string> textures);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::pair<Point2d, double> &
|
std::vector<std::shared_ptr<Vertex>>::size_type get_vertex_size() const;
|
||||||
|
|
||||||
|
const decltype(Vertex::texture_coordinate) &
|
||||||
get_texture_coordinate(const unsigned int index) const;
|
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;
|
||||||
std::vector<Triangle> &get_primitives() { return primitives_; };
|
std::vector<Triangle> &get_primitives();
|
||||||
|
|
||||||
std::shared_ptr<PhoneMaterial> &get_material() { return material_; }
|
std::shared_ptr<PhoneMaterial> &get_material();
|
||||||
const std::shared_ptr<PhoneMaterial> &get_material() const {
|
const std::shared_ptr<PhoneMaterial> &get_material() const;
|
||||||
return material_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<Vertex>> vertices_;
|
std::vector<std::shared_ptr<Vertex>> vertices_;
|
||||||
|
|
|
@ -17,6 +17,12 @@ MinusRenderer::MinusRenderer(float near, float far, float fov,
|
||||||
fov_, aspect_ratio_);
|
fov_, aspect_ratio_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
float MinusRenderer::calculate_height(const float fov, const float near) {
|
||||||
return std::fabs(near) * std::tan(fov * 0.5) * 2;
|
return std::fabs(near) * std::tan(fov * 0.5) * 2;
|
||||||
}
|
}
|
||||||
|
@ -101,9 +107,13 @@ void MinusRenderer::view_transform(const TransformMatrix &mtx) {
|
||||||
cv::Mat MinusRenderer::render(const int resolution_width,
|
cv::Mat MinusRenderer::render(const int resolution_width,
|
||||||
const int resolution_height) {
|
const int resolution_height) {
|
||||||
std::vector<Mesh> meshes = meshes_;
|
std::vector<Mesh> meshes = meshes_;
|
||||||
|
std::vector<std::vector<double>> perspective_coeff(meshes.size());
|
||||||
Rasterizer rasterizer{resolution_width, resolution_height};
|
Rasterizer rasterizer{resolution_width, resolution_height};
|
||||||
for (auto &m : meshes) {
|
for (int mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) {
|
||||||
auto &primitives = m.get_primitives();
|
auto &mesh = meshes[mesh_index];
|
||||||
|
auto &primitives = mesh.get_primitives();
|
||||||
|
perspective_coeff[mesh_index] =
|
||||||
|
std::vector<double>(mesh.get_vertex_size(), 1.);
|
||||||
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();
|
||||||
|
@ -111,8 +121,7 @@ cv::Mat MinusRenderer::render(const int resolution_width,
|
||||||
apply_transform(projection_matrix_, triangle_vertices);
|
apply_transform(projection_matrix_, triangle_vertices);
|
||||||
const auto &indices = t.get_vertex_index();
|
const auto &indices = t.get_vertex_index();
|
||||||
for (int j = 0; j < indices.size(); ++j) {
|
for (int j = 0; j < indices.size(); ++j) {
|
||||||
auto &uv = m.get_texture_coordinate(indices[j]);
|
perspective_coeff[mesh_index][indices[j]] = w[j];
|
||||||
uv.second = w[j];
|
|
||||||
}
|
}
|
||||||
const auto &[res, _] = apply_transform(
|
const auto &[res, _] = apply_transform(
|
||||||
view_port_transform(resolution_width, resolution_height),
|
view_port_transform(resolution_width, resolution_height),
|
||||||
|
@ -140,41 +149,44 @@ cv::Mat MinusRenderer::render(const int resolution_width,
|
||||||
const auto &[alpha, beta, gamma] = calculate_barycentric_coordinate(
|
const auto &[alpha, beta, gamma] = calculate_barycentric_coordinate(
|
||||||
triangle, Point2d{x + 0.5, y + 0.5});
|
triangle, Point2d{x + 0.5, y + 0.5});
|
||||||
|
|
||||||
const auto &triangle_indices = triangle.get_vertex_index();
|
|
||||||
std::vector<Point2d> triangle_uv;
|
std::vector<Point2d> triangle_uv;
|
||||||
|
std::vector<Point3d> triangle_normal;
|
||||||
std::vector<double> w_coeff;
|
std::vector<double> w_coeff;
|
||||||
for (int j = 0; j < triangle_indices.size(); ++j) {
|
for (const auto vertex_index : triangle.get_vertex_index()) {
|
||||||
const auto &[uv, w] =
|
triangle_uv.push_back(mesh.get_texture_coordinate(vertex_index));
|
||||||
mesh.get_texture_coordinate(triangle_indices[j]);
|
triangle_normal.push_back(mesh.get_normal_vector(vertex_index));
|
||||||
triangle_uv.push_back(uv);
|
w_coeff.push_back(
|
||||||
w_coeff.push_back(w);
|
perspective_coeff[p.triangle.mesh_index][vertex_index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(tianlei): 推导一下为什么需要除以下面这个
|
// TODO(tianlei): 推导一下为什么需要除以下面这个
|
||||||
// 在 screen space 对 1/w 的插值就相当于在 world space 对 w 的插值
|
// 在 screen space 对 1/w 的插值就相当于在 world space 对 w 的插值
|
||||||
auto w_reciprocal = alpha * (1. / w_coeff[0]) +
|
auto w_reciprocal = alpha * (1. / w_coeff[0]) +
|
||||||
beta * (1. / w_coeff[1]) +
|
beta * (1. / w_coeff[1]) +
|
||||||
gamma * (1. / w_coeff[2]);
|
gamma * (1. / w_coeff[2]);
|
||||||
Point2d pixel_uv{(alpha * (triangle_uv[0].x() / w_coeff[0]) +
|
Point2d uv{(alpha * (triangle_uv[0].x() / w_coeff[0]) +
|
||||||
beta * (triangle_uv[1].x() / w_coeff[1]) +
|
beta * (triangle_uv[1].x() / w_coeff[1]) +
|
||||||
gamma * (triangle_uv[2].x() / w_coeff[2])) /
|
gamma * (triangle_uv[2].x() / w_coeff[2])) /
|
||||||
w_reciprocal,
|
w_reciprocal,
|
||||||
(alpha * (triangle_uv[0].y() / w_coeff[0]) +
|
(alpha * (triangle_uv[0].y() / w_coeff[0]) +
|
||||||
beta * (triangle_uv[1].y() / w_coeff[1]) +
|
beta * (triangle_uv[1].y() / w_coeff[1]) +
|
||||||
gamma * (triangle_uv[2].y() / w_coeff[2])) /
|
gamma * (triangle_uv[2].y() / w_coeff[2])) /
|
||||||
w_reciprocal};
|
w_reciprocal};
|
||||||
auto &material = mesh.get_material();
|
|
||||||
|
|
||||||
pixel_color = material->sample_texture(0, pixel_uv);
|
Point3d normal{
|
||||||
|
alpha * triangle_normal[0].x() + beta * triangle_normal[1].x() +
|
||||||
|
gamma * triangle_normal[2].x(),
|
||||||
|
alpha * triangle_normal[0].y() + beta * triangle_normal[1].y() +
|
||||||
|
gamma * triangle_normal[2].y(),
|
||||||
|
alpha * triangle_normal[0].z() + beta * triangle_normal[1].z() +
|
||||||
|
gamma * triangle_normal[2].z()};
|
||||||
|
|
||||||
|
auto &material = mesh.get_material();
|
||||||
|
pixel_color = material->shade(
|
||||||
|
Vertex{Point3d{x, y, p.depth}, normal, uv}, Point3d{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return color_image;
|
return color_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
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 &obj_path,
|
|
||||||
const std::vector<std::string> texture_path);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
void set_meshes(const std::vector<Mesh> &meshes);
|
||||||
|
|
||||||
cv::Mat render(const int resolution_width, const int resolution_height);
|
cv::Mat render(const int resolution_width, const int resolution_height);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -2,48 +2,58 @@
|
||||||
// Author: tianlei.richard@qq.com (tianlei.richard)
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
#include "phone_material.h"
|
#include "phone_material.h"
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "util/interpolation.h"
|
|
||||||
#include <opencv2/highgui/highgui.hpp>
|
#include <opencv2/highgui/highgui.hpp>
|
||||||
|
|
||||||
PhoneMaterial::PhoneMaterial(
|
PhoneMaterial::PhoneMaterial(
|
||||||
const std::vector<std::filesystem::path> &texture_path) {
|
const std::unordered_map<Texture::TextureType, std::shared_ptr<Texture>>
|
||||||
for (const auto &path : texture_path) {
|
&textures,
|
||||||
if (!std::filesystem::exists(path)) {
|
const std::shared_ptr<Lamp> &lamp)
|
||||||
spdlog::warn("Texture {} is not existed!", path.c_str());
|
: point_lamp_(lamp), textures_(textures), Ka(cv::Vec3b{16, 16, 16}),
|
||||||
continue;
|
Ks(cv::Vec3b(64, 64, 64)),
|
||||||
}
|
specular_attenuation_factor_(defaultAttenuationFactor) {
|
||||||
const auto &img(cv::imread(path));
|
spdlog::info("Specular attenuation factor: {}", specular_attenuation_factor_);
|
||||||
spdlog::debug("Texture at {}, shape: ({},{})", path.c_str(), img.cols,
|
|
||||||
img.rows);
|
|
||||||
textures_.push_back(img);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Vec3b PhoneMaterial::sample_texture(const TextureId texture_id,
|
cv::Vec3b PhoneMaterial::shade(const Vertex &shading_point,
|
||||||
const Point2d &texture_coordinate) {
|
const Point3d &view_point) {
|
||||||
cv::Vec3b res{0, 0, 0};
|
cv::Vec3b shading_res{0, 0, 0};
|
||||||
if (texture_id >= 0 && texture_id < textures_.size()) {
|
|
||||||
const auto &texture = textures_[texture_id];
|
|
||||||
const double x = texture_coordinate.x() * texture.cols - 0.5;
|
|
||||||
const double y = texture_coordinate.y() * texture.rows - 0.5;
|
|
||||||
|
|
||||||
const int x_f = x;
|
// Diffuse
|
||||||
const int x_c = x + 1.;
|
assert(textures_.find(Texture::DiffuseMap) != textures_.end());
|
||||||
const float t_x = x - x_f;
|
const auto &diffuse_map = textures_[Texture::DiffuseMap];
|
||||||
const int y_f = y;
|
const auto &Kd = diffuse_map->sample(shading_point.texture_coordinate);
|
||||||
const int y_c = y + 1.;
|
|
||||||
const float t_y = y - y_f;
|
|
||||||
|
|
||||||
const cv::Vec3b &a = texture.at<cv::Vec3b>(x_f, y_f);
|
const auto &point2light_vector =
|
||||||
const cv::Vec3b &b = texture.at<cv::Vec3b>(x_f, y_c);
|
(point_lamp_->position - shading_point.position).normalized();
|
||||||
const cv::Vec3b &c = texture.at<cv::Vec3b>(x_c, y_f);
|
const auto &point2light_distance =
|
||||||
const cv::Vec3b &d = texture.at<cv::Vec3b>(x_c, y_c);
|
(shading_point.position - point_lamp_->position).norm();
|
||||||
|
const auto ¤t_intensity =
|
||||||
|
point_lamp_->intensity / (point2light_distance * point2light_distance);
|
||||||
|
|
||||||
res = bilerp(t_x, t_y, a, b, c, d);
|
const auto diffuse_coeff =
|
||||||
} else {
|
current_intensity *
|
||||||
spdlog::warn("Sample texture {} at ({}, {}) failed!", texture_id,
|
std::max(shading_point.normal.dot(point2light_vector), 0.);
|
||||||
texture_coordinate.x(), texture_coordinate.y());
|
cv::Vec3b diffuse_color = Kd * diffuse_coeff;
|
||||||
}
|
shading_res += diffuse_color;
|
||||||
return res;
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
return shading_res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,20 +8,29 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "texture.h"
|
||||||
|
|
||||||
// Bling-Phone Matrerial
|
// Bling-Phone Matrerial
|
||||||
class PhoneMaterial {
|
class PhoneMaterial {
|
||||||
public:
|
public:
|
||||||
using TextureId = uint32_t;
|
PhoneMaterial(const std::unordered_map<Texture::TextureType,
|
||||||
|
std::shared_ptr<Texture>> &textures,
|
||||||
|
const std::shared_ptr<Lamp> &lamp);
|
||||||
|
|
||||||
PhoneMaterial(const std::vector<std::filesystem::path> &texture_path);
|
cv::Vec3b shade(const Vertex &shading_point, const Point3d &view_point);
|
||||||
|
|
||||||
public:
|
|
||||||
cv::Vec3b sample_texture(const TextureId texture_id,
|
|
||||||
const Point2d &texture_coordinate);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<cv::Mat> textures_;
|
static constexpr int defaultAttenuationFactor = 16;
|
||||||
|
|
||||||
uint32_t illumination_intensity_;
|
private:
|
||||||
|
std::shared_ptr<Lamp> point_lamp_;
|
||||||
|
|
||||||
|
std::unordered_map<Texture::TextureType, std::shared_ptr<Texture>> textures_;
|
||||||
|
|
||||||
|
// Ambient Related
|
||||||
|
cv::Vec3b Ka;
|
||||||
|
|
||||||
|
// Specular Related
|
||||||
|
cv::Vec3b Ks;
|
||||||
|
int specular_attenuation_factor_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2024 SquareBlock Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
|
#include "texture.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "util/interpolation.h"
|
||||||
|
#include <opencv2/highgui/highgui.hpp>
|
||||||
|
|
||||||
|
Texture::Texture(const std::filesystem::path &texture_path) {
|
||||||
|
if (!std::filesystem::exists(texture_path)) {
|
||||||
|
spdlog::warn("Texture {} is not existed!", texture_path.c_str());
|
||||||
|
throw std::filesystem::filesystem_error("File not found",
|
||||||
|
std::error_code{});
|
||||||
|
}
|
||||||
|
texture_ = (cv::imread(texture_path));
|
||||||
|
spdlog::debug("Texture at {}, shape: ({},{})", texture_path.c_str(),
|
||||||
|
texture_.cols, texture_.rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Vec3b Texture::sample(const Point2d &texture_coordinate) {
|
||||||
|
cv::Vec3b res{0, 0, 0};
|
||||||
|
|
||||||
|
const double x = texture_coordinate.x() * texture_.cols - 0.5;
|
||||||
|
const double y = texture_coordinate.y() * texture_.rows - 0.5;
|
||||||
|
|
||||||
|
const int x_f = x;
|
||||||
|
const int x_c = x + 1.;
|
||||||
|
const float t_x = x - x_f;
|
||||||
|
const int y_f = y;
|
||||||
|
const int y_c = y + 1.;
|
||||||
|
const float t_y = y - y_f;
|
||||||
|
|
||||||
|
const cv::Vec3b &a = texture_.at<cv::Vec3b>(x_f, y_f);
|
||||||
|
const cv::Vec3b &b = texture_.at<cv::Vec3b>(x_f, y_c);
|
||||||
|
const cv::Vec3b &c = texture_.at<cv::Vec3b>(x_c, y_f);
|
||||||
|
const cv::Vec3b &d = texture_.at<cv::Vec3b>(x_c, y_c);
|
||||||
|
|
||||||
|
res = bilerp(t_x, t_y, a, b, c, d);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2024 SquareBlock Inc. All Rights Reserved.
|
||||||
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <opencv2/core/core.hpp>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class Texture {
|
||||||
|
public:
|
||||||
|
typedef enum TextureType {
|
||||||
|
DiffuseMap,
|
||||||
|
AmbientMap,
|
||||||
|
} TextureType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Texture(const std::filesystem::path &texture_path);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cv::Vec3b sample(const Point2d &texture_coordinate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
cv::Mat texture_;
|
||||||
|
};
|
Loading…
Reference in New Issue