MinusculeRender/src/minus_renderer.cc

218 lines
8.9 KiB
C++
Raw Normal View History

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>
#include "spdlog/spdlog.h"
#include <iostream>
2024-03-22 20:12:09 +08:00
#include <opencv2/highgui/highgui.hpp>
#include "minus_renderer.h"
2024-03-01 21:31:47 +08:00
#include "rasterizer.h"
2024-03-01 16:00:08 +08:00
MinusRenderer::MinusRenderer(float near, float far, float fov,
float aspect_ratio)
: near_(near), far_(far), fov_(fov), aspect_ratio_(aspect_ratio) {
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) {
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) {
return height * ratio;
}
2024-03-01 16:00:08 +08:00
std::pair<TransformMatrix, TransformMatrix>
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_}};
// Orthographic transform
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
const auto &ortho_transform =
TransformMatrix{{2 / (right - left), 0, 0, 0},
2024-03-06 22:23:48 +08:00
{0, 2 / (top - bottom), 0, 0},
{0, 0, 2 / std::fabs(far_ - near_), 0},
{0, 0, 0, 1}} *
TransformMatrix{{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}};
const auto &inv_ortho_transform = TransformMatrix{
{(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 =
TransformMatrix{{2. / resolution_width, 0, 0, 0},
{0, 2. / resolution_height, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}} *
TransformMatrix{{1, 0, 0, -resolution_width * 0.5},
{0, 1, 0, -resolution_height * 0.5},
{0, 0, 1, 0},
{0, 0, 0, 1}};
const auto &ss_transform =
view_port_transform * ortho_transform * squish_transform;
const auto &inv_ss_transform =
inv_squish_transform * inv_ortho_transform * inv_view_port_transform;
return {ss_transform, inv_ss_transform};
2024-03-06 22:23:48 +08:00
}
2024-03-22 20:12:09 +08:00
std::tuple<double, double, double>
MinusRenderer::calculate_barycentric_coordinate(const Triangle &t,
const Point2d &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 =
2024-03-22 20:12:09 +08:00
(-(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;
2024-03-22 20:12:09 +08:00
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);
}
}
}
2024-03-01 16:00:08 +08:00
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);
}
}
}
cv::Mat MinusRenderer::render(const int resolution_width,
const int resolution_height) {
// 构造 camera space -> screen space 的变换矩阵
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};
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();
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);
}
}
2024-03-22 20:12:09 +08:00
const auto &shading_points = rasterizer.rasterize(meshes);
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 &fragment = shading_points[y][x];
if (fragment.triangle.mesh_index >= 0 &&
fragment.triangle.mesh_index < meshes_.size()) {
const auto &mesh = meshes_[fragment.triangle.mesh_index];
2024-03-22 20:12:09 +08:00
const auto &primitives = mesh.get_primitives();
if (fragment.triangle.triangle_index >= 0 &&
fragment.triangle.triangle_index < primitives.size()) {
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 &[alpha, beta, gamma] =
calculate_barycentric_coordinate(triangle, position.head(2));
spdlog::trace("barycentric coordinate: ({}, {}, {})", alpha, beta,
gamma);
2024-03-22 20:12:09 +08:00
std::vector<Point2d> triangle_uv;
2024-03-27 18:31:10 +08:00
std::vector<Point3d> triangle_normal;
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));
2024-03-22 20:12:09 +08:00
}
2024-03-27 18:31:10 +08:00
Point2d uv{alpha * triangle_uv[0].x() + beta * triangle_uv[1].x() +
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() +
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()}
.normalized();
spdlog::trace("Normal: ({}, {}, {})", normal.x(), normal.y(),
normal.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{position, normal, uv}, Point3d{});
2024-03-22 20:12:09 +08:00
}
}
}
}
2024-03-22 20:12:09 +08:00
return color_image;
}