// Copyright 2024 SquareBlock Inc. All Rights Reserved. // Author: tianlei.richard@qq.com (tianlei.richard) #include #include "spdlog/spdlog.h" #include #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) { spdlog::info("near: {}, far: {}, fov: {}, aspect ratio: {}", near_, far_, fov_, aspect_ratio_); } void MinusRenderer::set_meshes(const std::vector &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) { return std::fabs(near) * std::tan(fov * 0.5) * 2; } float MinusRenderer::calculate_width(const float height, const float ratio) { return height * ratio; } 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_}}; // 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; const auto &ortho_transform = 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}} * 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}; } std::tuple 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 = (-(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; 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); } } } 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 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); } } const auto &shading_points = rasterizer.rasterize(meshes); assert(!shading_points.empty()); 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(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]; 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); std::vector triangle_uv; std::vector 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)); } 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()); auto &material = mesh.get_material(); pixel_color = material->shade(Vertex{position, normal, uv}, Point3d{}); } } } } return color_image; }