2024-03-22 20:12:09 +08:00
|
|
|
// Copyright 2024 SquareBlock Inc. All Rights Reserved.
|
2024-03-01 21:31:47 +08:00
|
|
|
// Author: tianlei.richard@qq.com (tianlei.richard)
|
|
|
|
|
2024-03-06 22:23:48 +08:00
|
|
|
#include <cmath>
|
|
|
|
|
2024-03-08 11:54:37 +08:00
|
|
|
#include "spdlog/spdlog.h"
|
2024-03-22 20:12:09 +08:00
|
|
|
#include <opencv2/highgui/highgui.hpp>
|
2024-03-08 11:54:37 +08:00
|
|
|
|
2024-03-06 17:21:53 +08:00
|
|
|
#include "minus_renderer.h"
|
2024-03-01 21:31:47 +08:00
|
|
|
#include "rasterizer.h"
|
2024-03-01 16:00:08 +08:00
|
|
|
|
2024-03-06 17:21:53 +08:00
|
|
|
MinusRenderer::MinusRenderer(float near, float far, float fov,
|
2024-03-08 11:54:37 +08:00
|
|
|
float aspect_ratio)
|
2024-03-06 17:21:53 +08:00
|
|
|
: near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio),
|
2024-03-21 21:34:29 +08:00
|
|
|
projection_matrix_(orthographic_transform() * squish_transform()) {
|
2024-03-08 11:54:37 +08:00
|
|
|
spdlog::info("near: {}, far: {}, fov: {}, aspect ratio: {}", near_, far_,
|
|
|
|
fov_, aspect_ratio_);
|
2024-03-06 22:23:48 +08:00
|
|
|
}
|
2024-03-01 21:31:47 +08:00
|
|
|
|
2024-03-27 18:31:10 +08:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2024-03-06 22:23:48 +08:00
|
|
|
float MinusRenderer::calculate_height(const float fov, const float near) {
|
2024-03-06 17:21:53 +08:00
|
|
|
return std::fabs(near) * std::tan(fov * 0.5) * 2;
|
|
|
|
}
|
2024-03-01 21:31:47 +08:00
|
|
|
|
2024-03-06 22:23:48 +08:00
|
|
|
float MinusRenderer::calculate_width(const float height, const float ratio) {
|
2024-03-06 17:21:53 +08:00
|
|
|
return height * ratio;
|
|
|
|
}
|
2024-03-01 16:00:08 +08:00
|
|
|
|
2024-03-21 21:34:29 +08:00
|
|
|
TransformMatrix MinusRenderer::squish_transform() {
|
2024-03-06 22:23:48 +08:00
|
|
|
// Frustum to Cuboid
|
|
|
|
return TransformMatrix{{near_, 0, 0, 0},
|
|
|
|
{0, near_, 0, 0},
|
|
|
|
{0, 0, near_ + far_, -near_ * far_},
|
|
|
|
{0, 0, 1, 0}};
|
|
|
|
}
|
|
|
|
|
2024-03-21 21:34:29 +08:00
|
|
|
TransformMatrix MinusRenderer::orthographic_transform() {
|
2024-03-07 14:29:24 +08:00
|
|
|
const float height = calculate_height(fov_, near_);
|
|
|
|
const float width = calculate_width(height, aspect_ratio_);
|
|
|
|
const float right = width * 0.5;
|
|
|
|
const float left = -right;
|
|
|
|
const float top = height * 0.5;
|
|
|
|
const float bottom = -top;
|
2024-03-06 22:23:48 +08:00
|
|
|
|
|
|
|
TransformMatrix m{{1, 0, 0, -0.5 * (right - left)},
|
|
|
|
{0, 1, 0, -0.5 * (top - bottom)},
|
|
|
|
{0, 0, 1, -0.5 * (far_ - near_)},
|
|
|
|
{0, 0, 0, 1}};
|
|
|
|
|
|
|
|
m = TransformMatrix{{2 / (right - left), 0, 0, 0},
|
|
|
|
{0, 2 / (top - bottom), 0, 0},
|
|
|
|
{0, 0, 2 / std::fabs(far_ - near_), 0},
|
|
|
|
{0, 0, 0, 1}} *
|
|
|
|
m;
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2024-03-07 11:11:04 +08:00
|
|
|
TransformMatrix MinusRenderer::view_port_transform(const float width,
|
|
|
|
const float height) {
|
|
|
|
return TransformMatrix{{width * 0.5, 0, 0, width * 0.5},
|
|
|
|
{0, height * 0.5, 0, height * 0.5},
|
2024-03-06 22:23:48 +08:00
|
|
|
{0, 0, 1, 0},
|
|
|
|
{0, 0, 0, 1}};
|
|
|
|
}
|
|
|
|
|
2024-03-22 20:12:09 +08:00
|
|
|
std::tuple<double, double, double>
|
|
|
|
MinusRenderer::calculate_barycentric_coordinate(const Triangle &t,
|
|
|
|
const Point2d &p) {
|
2024-03-21 21:34:29 +08:00
|
|
|
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 =
|
2024-03-22 20:12:09 +08:00
|
|
|
(-(p.x() - B.x()) * (C.y() - B.y()) + (p.y() - B.y()) * (C.x() - B.x())) /
|
2024-03-21 21:34:29 +08:00
|
|
|
(-(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;
|
2024-03-22 20:12:09 +08:00
|
|
|
return {alpha, beta, gamma};
|
2024-03-21 21:34:29 +08:00
|
|
|
}
|
|
|
|
|
2024-03-08 11:54:37 +08:00
|
|
|
void MinusRenderer::model_transform(const TransformMatrix &mtx) {
|
|
|
|
for (auto &m : meshes_) {
|
2024-03-21 21:34:29 +08:00
|
|
|
for (auto &t : m.get_primitives()) {
|
2024-03-26 21:34:06 +08:00
|
|
|
const auto &[res, _] = apply_transform(mtx, t.get_vertex_position());
|
|
|
|
t.set_points(res);
|
2024-03-08 11:54:37 +08:00
|
|
|
}
|
2024-03-06 17:21:53 +08:00
|
|
|
}
|
|
|
|
}
|
2024-03-01 16:00:08 +08:00
|
|
|
|
2024-03-08 11:54:37 +08:00
|
|
|
void MinusRenderer::view_transform(const TransformMatrix &mtx) {
|
|
|
|
for (auto &m : meshes_) {
|
2024-03-21 21:34:29 +08:00
|
|
|
for (auto &t : m.get_primitives()) {
|
2024-03-26 21:34:06 +08:00
|
|
|
const auto &[res, _] = apply_transform(mtx, t.get_vertex_position());
|
|
|
|
t.set_points(res);
|
2024-03-08 11:54:37 +08:00
|
|
|
}
|
2024-03-07 14:29:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-06 17:21:53 +08:00
|
|
|
cv::Mat MinusRenderer::render(const int resolution_width,
|
|
|
|
const int resolution_height) {
|
2024-03-08 11:54:37 +08:00
|
|
|
std::vector<Mesh> meshes = meshes_;
|
2024-03-27 18:31:10 +08:00
|
|
|
std::vector<std::vector<double>> perspective_coeff(meshes.size());
|
2024-03-06 17:21:53 +08:00
|
|
|
Rasterizer rasterizer{resolution_width, resolution_height};
|
2024-03-27 18:31:10 +08:00
|
|
|
for (int mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) {
|
|
|
|
auto &mesh = meshes[mesh_index];
|
|
|
|
auto &primitives = mesh.get_primitives();
|
|
|
|
perspective_coeff[mesh_index] =
|
|
|
|
std::vector<double>(mesh.get_vertex_size(), 1.);
|
2024-03-08 11:54:37 +08:00
|
|
|
for (int i = 0; i < primitives.size(); ++i) {
|
|
|
|
auto &t = primitives[i];
|
2024-03-21 21:34:29 +08:00
|
|
|
const auto &triangle_vertices = t.get_vertex_position();
|
2024-03-26 21:34:06 +08:00
|
|
|
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) {
|
2024-03-27 18:31:10 +08:00
|
|
|
perspective_coeff[mesh_index][indices[j]] = w[j];
|
2024-03-26 21:34:06 +08:00
|
|
|
}
|
|
|
|
const auto &[res, _] = apply_transform(
|
|
|
|
view_port_transform(resolution_width, resolution_height),
|
|
|
|
projective_res);
|
|
|
|
t.set_points(res);
|
2024-03-08 11:54:37 +08:00
|
|
|
}
|
|
|
|
}
|
2024-03-22 20:12:09 +08:00
|
|
|
const auto &shading_points = rasterizer.rasterize(meshes);
|
2024-03-21 21:34:29 +08:00
|
|
|
assert(!shading_points.empty());
|
2024-03-22 20:12:09 +08:00
|
|
|
cv::Mat color_image(shading_points.size(), (shading_points[0]).size(),
|
|
|
|
CV_8UC3);
|
|
|
|
for (int y = 0; y < shading_points.size(); ++y) {
|
|
|
|
for (int x = 0; x < shading_points[y].size(); ++x) {
|
|
|
|
auto &pixel_color =
|
|
|
|
color_image.at<cv::Vec3b>(shading_points.size() - y - 1, x);
|
|
|
|
pixel_color = cv::Vec3b(0, 0, 0);
|
|
|
|
|
|
|
|
const auto &p = shading_points[y][x];
|
|
|
|
if (p.triangle.mesh_index >= 0 && p.triangle.mesh_index < meshes.size()) {
|
|
|
|
const auto &mesh = meshes[p.triangle.mesh_index];
|
|
|
|
const auto &primitives = mesh.get_primitives();
|
|
|
|
if (p.triangle.triangle_index >= 0 &&
|
|
|
|
p.triangle.triangle_index < primitives.size()) {
|
|
|
|
const auto &triangle = primitives[p.triangle.triangle_index];
|
|
|
|
const auto &[alpha, beta, gamma] = calculate_barycentric_coordinate(
|
|
|
|
triangle, Point2d{x + 0.5, y + 0.5});
|
|
|
|
|
2024-03-26 21:34:06 +08:00
|
|
|
std::vector<Point2d> triangle_uv;
|
2024-03-27 18:31:10 +08:00
|
|
|
std::vector<Point3d> triangle_normal;
|
2024-03-26 21:34:06 +08:00
|
|
|
std::vector<double> w_coeff;
|
2024-03-27 18:31:10 +08:00
|
|
|
for (const auto vertex_index : triangle.get_vertex_index()) {
|
|
|
|
triangle_uv.push_back(mesh.get_texture_coordinate(vertex_index));
|
|
|
|
triangle_normal.push_back(mesh.get_normal_vector(vertex_index));
|
|
|
|
w_coeff.push_back(
|
|
|
|
perspective_coeff[p.triangle.mesh_index][vertex_index]);
|
2024-03-22 20:12:09 +08:00
|
|
|
}
|
2024-03-27 18:31:10 +08:00
|
|
|
|
2024-03-22 20:12:09 +08:00
|
|
|
// TODO(tianlei): 推导一下为什么需要除以下面这个
|
2024-03-26 21:34:06 +08:00
|
|
|
// 在 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]);
|
2024-03-27 18:31:10 +08:00
|
|
|
Point2d 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 * (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};
|
|
|
|
|
|
|
|
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()};
|
2024-03-22 20:12:09 +08:00
|
|
|
|
2024-03-27 18:31:10 +08:00
|
|
|
auto &material = mesh.get_material();
|
|
|
|
pixel_color = material->shade(
|
|
|
|
Vertex{Point3d{x, y, p.depth}, normal, uv}, Point3d{});
|
2024-03-22 20:12:09 +08:00
|
|
|
}
|
2024-03-08 11:54:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-22 20:12:09 +08:00
|
|
|
return color_image;
|
2024-03-08 11:54:37 +08:00
|
|
|
}
|