作业内容
本次作业的任务是填写一个旋转矩阵和一个透视投影矩阵。给定三维下三个 点 v0(2.0, 0.0, −2.0), v1(0.0, 2.0, −2.0), v2(−2.0, 0.0, −2.0), 你需要将这三个点的坐标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形 (在代码框架中,我们已经提供了 draw_triangle 函数,所以你只需要去构建变换矩阵即可)。简而言之,我们需要进行模型、视图、投影、视口等变换来将三角形显示在屏幕上。在提供的代码框架中,我们留下了模型变换和投影变换的部分给你去完成。以下是你需要在 main.cpp 中修改的函数(请不要修改任何的函数名和其他已经填写好的函数,并保证提交的代码是已经完成且能运行的):
• get_model_matrix(float rotation_angle): 逐个元素地构建模型变换矩 阵并返回该矩阵。在此函数中,你只需要实现三维中绕 z 轴旋转的变换矩阵, 而不用处理平移与缩放。
• get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar): 使用给定的参数逐个元素地构建透视投影矩阵并返回该矩阵。
• [Optional] main(): 自行补充你所需的其他操作。当你在上述函数中正确地构建了模型与投影矩阵,光栅化器会创建一个窗口显示出线框三角形。由于光栅化器是逐帧渲染与绘制的,所以你可以使用 A 和 D 键去将该三角形绕 z 轴旋转 (此处有一项提高作业,将三角形绕任意过原点的轴旋转)。当你按下 Esc 键时,窗口会关闭且程序终止。
另外,你也可以从命令行中运行该程序。你可以使用以下命令来运行和传递 旋转角给程序,在这样的运行方式下,是不会生成任何的窗口,输出的结果图像会被存储在给定的文件中 (若未指定文件名,则默认存储在 output.png 中)。图 像的存储位置在可执行文件旁,所以如果你的可执行文件是在 build 文件夹中, 那么图像也会在该文件夹内。
在本次作业中,因为你并不需要去使用三角形类,所以你需要理解与修改的文件为:rasterizer.hpp 和 main.cpp。其中 rasterizer.hpp 文件作用是生成渲染器界面与绘制。
作业评分机制:
• [5 分] 正确构建模型矩阵。
• [5 分] 正确构建透视投影矩阵。
• [10 分] 你的代码可以在现有框架下正确运行,并能看到变换后的三角形。
• [10 分] 当按 A 键与 D 键时,三角形能正确旋转。或者正确使用命令行得 到旋转结果图像。
• [提高项 5 分] 在 main.cpp 中构造一个函数,该函数的作用是得到绕任意过原点的轴的旋转变换矩阵。 Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
作业思路
分别将main.cpp中用三个函数返回的m,v,p三个矩阵加载到光栅化器(rasterizer)中的model, view, projection这三个矩阵中去。
Eigen::Matrix4f mvp = projection * view * model;
这三个变换矩阵分别为模型变换矩阵,视图变换矩阵,投影变换矩阵。其中视图变换矩阵已经在框架中写出,无需补充,本次作业的任务是补充模型矩阵和透视投影矩阵。
1. 构造模型矩阵
// 构造模型矩阵
// 输入:旋转角度 rotation_angle(以度为单位)
// 输出:绕 Z 轴旋转的模型矩阵
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the model matrix for rotating the triangle around the Z axis.
// Then return it.
Eigen::Matrix4f rotation_matrix;
// 角度转弧度
float radians = rotation_angle * MY_PI / 180.0;
//旋转矩阵:绕Z轴旋转
rotation_matrix << cos(radians), -sin(radians), 0, 0,
sin(radians), cos(radians), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = rotation_matrix * model;
return model;
}
2. 构造透视投影矩阵
// 构造透视投影矩阵
// 输入:视野角 eye_fov(以度为单位)、宽高比 aspect_ratio、近平面 zNear、远平面 zFar
// 输出:将三维坐标投影到二维屏幕的矩阵
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
// Students will implement this function
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the projection matrix for the given parameters.
// Then return it.
//挤压矩阵
Eigen::Matrix4f p2o_Matrix=Eigen::Matrix4f::Zero();
p2o_Matrix << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;
// 将视野角eye_fov度数转弧度
float fov_rad = eye_fov * MY_PI / 180.0;
// 计算近平面的上下左右范围
float t = tan(fov_rad / 2) * abs(zNear); // top
float r = t * aspect_ratio; // right
float l = -r; // left
float b = -t; // bottom
// 正交投影的缩放矩阵(将范围缩放到 [-1, 1])
Eigen::Matrix4f ortho_scale_Matrix=Eigen::Matrix4f::Identity();
ortho_scale_Matrix << 2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (Zfar - zNear), 0,
0, 0, 0, 1;
// 正交投影的平移矩阵(将中心点移到原点)
Eigen::Matrix4f ortho_translate_Matrix=Eigen::Matrix4f::Identity();
ortho_translate_Matrix << 1, 0, 0, -(r + l) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(zFar + zNear) / 2,
0, 0, 0, 1;
// 计算完整正交投影矩阵
Eigen::Matrix4f ortho_Matrix = ortho_scale_Matrix * ortho_translate_Matrix;
//组合透视投影矩阵:挤压矩阵 + 正交投影矩阵
projection=ortho_Matrix * p2o_Matrix;
return projection;
}
3. 构造绕任意轴旋转矩阵(附加题)
这里会用到Rodrigues‘ Rotation Formula 罗德里格斯旋转公式。
Eigen::Matrix4f get_rotation(Eigen::Vector3f axis,float angle)
{
//初始化一个单位矩阵
Eigen::Matrix4f rotation = Eigen::Matrix4f::Identity();
//将角度转换为弧度
float radians=angle*MY_PI/180.0;
// 对旋转轴向量进行归一化(确保其长度为1)
axis.normalize();
//单位矩阵I(3x3)
Eigen::Matrix3f I = Eigen::Matrix3f::Identity();
// N 矩阵(反对称矩阵),用于 Rodrigues 公式计算
// N 的定义是基于轴向量的分量,按照反对称矩阵的结构:
// | 0 -z y |
// | z 0 -x |
// | -y x 0 |
Eigen::Matrix3f N;
N << 0, -axis.z(), axis.y(),
axis.z(), 0, -axis.x(),
-axis.y(), axis.x(), 0;
// 使用 Rodrigues’ Rotation Formula 罗德里格斯旋转公式计算旋转矩阵 R:
// R = cos(θ) * I + (1 - cos(θ)) * (axis * axis^T) + sin(θ) * N
Eigen::Matrix3f R= cos(radians)*I+(1-cos(radians))*(axis*axis.transpose())+sin(radians)*N;
//将计算得到的 3x3 旋转矩阵嵌入到 4x4 齐次矩阵中,同时保持其余元素(如平移部分)为单位值。
rotation.block<3, 3>(0, 0) = R;
// 返回完整的 4x4 旋转矩阵
return rotation;
}
4. 按 R 键,设置任意轴旋转
int main(int argc, const char **argv)
{
// 初始化旋转角度为 0
float angle = 0;
// 标志变量:是否为命令行模式
bool command_line = false;
// 默认输出文件名
std::string filename = "output.png";
// 默认旋转轴:X 轴
Eigen::Vector3f axis = {1, 0, 0};
// 处理命令行参数
if (argc >= 3) // 如果提供了足够的参数
{
command_line = true; // 设置为命令行模式
angle = std::stof(argv[2]); // 从参数中读取旋转角度
if (argc == 4)
{
// 从参数中读取输出文件名
filename = std::string(argv[3]);
}
else
{
// 如果参数不足,则退出程序
return 0;
}
}
// 初始化光栅化器(分辨率为 700x700 像素)
rst::rasterizer r(700, 700);
// 设置观察点位置
Eigen::Vector3f eye_pos = {0, 0, 5};
// 定义三角形的顶点坐标
std::vector<Eigen::Vector3f> pos{{2, 0, -2}, {0, 2, -2}, {-2, 0, -2}};
// 定义三角形的顶点索引(表示顶点的连接关系)
std::vector<Eigen::Vector3i> ind{{0, 1, 2}};
// 将顶点位置和索引加载到光栅化器中
auto pos_id = r.load_positions(pos);
auto ind_id = r.load_indices(ind);
// 按键捕获变量
int key = 0;
// 帧计数器,用于记录当前渲染的帧数
int frame_count = 0;
// 如果是命令行模式
if (command_line)
{
// 清除颜色和深度缓冲区
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
// 设置模型矩阵、视图矩阵和投影矩阵
r.set_model(get_model_matrix(angle));
r.set_view(get_view_matrix(eye_pos));
r.set_projection(get_projection_matrix(45, 1, 0.1, 50));
// 绘制三角形
r.draw(pos_id, ind_id, rst::Primitive::Triangle);
// 获取帧缓冲数据并转换为 OpenCV 图像格式
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
image.convertTo(image, CV_8UC3, 1.0f);
// 将图像保存到指定文件
cv::imwrite(filename, image);
// 退出程序
return 0;
}
// 实时渲染模式
while (key != 27) // 按下 ESC 键退出循环
{
// 清除颜色和深度缓冲区
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
// 设置模型矩阵、视图矩阵和投影矩阵
r.set_model(get_model_matrix(angle));
r.set_view(get_view_matrix(eye_pos));
r.set_projection(get_projection_matrix(45, 1, 0.1, 50));
// 绘制三角形
r.draw(pos_id, ind_id, rst::Primitive::Triangle);
// 获取帧缓冲数据并显示在窗口中
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
image.convertTo(image, CV_8UC3, 1.0f);
cv::imshow("image", image);
key = cv::waitKey(10); // 捕获键盘输入
// 输出当前帧数到控制台
std::cout << "frame count: " << frame_count++ << '\n';
// 根据键盘输入执行不同操作
if (key == 'a') // 按 A 键,绕 Z 轴逆时针旋转
{
angle += 10; // 增加旋转角度
}
else if (key == 'd') // 按 D 键,绕 Z 轴顺时针旋转
{
angle -= 10; // 减少旋转角度
}
else if (key == 'r') // 按 R 键,设置任意轴旋转
{
std::cout << "Please enter rotation axis (x, y, z): ";
// 用户输入旋转轴向量
std::cin >> axis[0] >> axis[1] >> axis[2];
std::cout << "Please enter rotation angle: ";
// 用户输入旋转角度
std::cin >> angle;
// 根据用户输入的旋转轴和旋转角度计算旋转矩阵
Eigen::Matrix4f rotation_Matrix = get_rotation(axis, angle);
// 使用新的旋转矩阵替换当前模型矩阵
r.set_model(rotation_Matrix);
}
}
return 0;
}
5. 运行结果
最初的三角形:
通过按 A 和 D 键控制绕z轴旋转:
通过按 R 键,绕任意轴旋转任意角度:
框架文件关系及注释
main.cpp 是程序入口,调用了 rasterizer.hpp 和 Triangle.hpp 定义的功能。
rasterizer.hpp 定义了光栅化器的功能接口,rasterizer.cpp 实现了这些功能。
Triangle.hpp 定义了三角形类接口,Triangle.cpp 实现了顶点和颜色的设置逻辑。
CMakeLists.txt 配置了整个项目的构建流程及依赖关系。
CMakeLists.txt
CMakeLists.txt 是构建系统的配置文件,用于配置项目依赖、编译选项及生成目标。
cmake_minimum_required(VERSION 3.10) # 设置最低 CMake 版本
project(Rasterizer) # 定义项目名称find_package(OpenCV REQUIRED) # 查找 OpenCV 库
set(CMAKE_CXX_STANDARD 17) # 设置 C++ 标准为 C++17
include_directories(/usr/local/include) # 添加头文件搜索路径
add_executable(Rasterizer main.cpp rasterizer.hpp rasterizer.cpp Triangle.hpp Triangle.cpp)
# 定义可执行目标 "Rasterizer",包括源文件和头文件target_link_libraries(Rasterizer ${OpenCV_LIBRARIES})
# 链接 OpenCV 库到目标
main.cpp
文件功能
main.cpp 是程序的入口,负责初始化光栅化器、定义场景内容(顶点、索引等),以及实现交互控制逻辑。
1. 包含了 Triangle.hpp 和 rasterizer.hpp,调用了它们的功能。
2. 使用了 Eigen 库和 OpenCV 库。
3. 实现了透视投影矩阵和模型矩阵构建逻辑。
#include "Triangle.hpp" // 引入三角形相关操作
#include "rasterizer.hpp" // 引入光栅化器功能
#include <eigen3/Eigen/Eigen> // 引入 Eigen 库,用于矩阵运算
#include <opencv2/opencv.hpp> // 引入 OpenCV 库,用于图像操作
#include <iostream> // 输入输出库
constexpr double MY_PI = 3.1415926; // 定义圆周率常量
// 构建视图矩阵,负责将观察点平移到原点
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos);
// 构建模型矩阵,绕 Z 轴旋转
Eigen::Matrix4f get_model_matrix(float rotation_angle);
// 构建透视投影矩阵,将三维坐标投影到二维屏幕
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar);
// 计算任意轴旋转矩阵
Eigen::Matrix4f get_rotation(Eigen::Vector3f axis, float angle);
// 主函数:处理命令行参数、初始化光栅化器、实现实时渲染逻辑
int main(int argc, const char **argv)
{
// 初始化光栅化器和观察点,定义场景顶点和索引
// 支持交互式操作(按键控制旋转)
// 通过命令行模式输出渲染结果到文件
}
rasterizer.cpp
文件功能
rasterizer.cpp 是 rasterizer.hpp 的实现文件,负责定义光栅化器的核心功能,包括:
1. 加载缓冲区数据:加载顶点和索引数据,管理缓冲区。
2. 矩阵变换:设置和应用模型、视图和投影矩阵。
3. 光栅化操作:对三角形和线段进行逐像素处理,将顶点数据绘制到帧缓冲中。
4. 帧缓冲管理:设置像素颜色、清空缓冲区。
文件关系
1. 依赖关系:
依赖 rasterizer.hpp:实现 rasterizer.hpp 声明的所有功能。
依赖 Triangle.hpp:调用Triangle 类获取三角形顶点数据。
2. 被依赖关系:
被 main.cpp 使用:main.cpp 调用 rasterizer.hpp 的接口,这些接口在 rasterizer.cpp 中实现。
#include "rasterizer.hpp" // 包含光栅化器头文件
#include <algorithm> // 通用算法库,用于查找最大最小值
#include <cmath> // 数学库,用于计算
#include <stdexcept> // 异常处理库
namespace rst
{
// 构造函数:初始化帧缓冲大小和深度缓冲
rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
frame_buf.resize(w * h); // 初始化帧缓冲区大小
depth_buf.resize(w * h); // 初始化深度缓冲区大小
}
// 加载顶点缓冲区
pos_buf_id rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
{
int id = next_id++; // 分配新的缓冲区 ID
pos_buf[id] = positions; // 将顶点数据存入缓冲区
return {id}; // 返回分配的缓冲区 ID
}
// 加载索引缓冲区
ind_buf_id rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
{
int id = next_id++; // 分配新的缓冲区 ID
ind_buf[id] = indices; // 将索引数据存入缓冲区
return {id}; // 返回分配的缓冲区 ID
}
// 设置模型矩阵
void rasterizer::set_model(const Eigen::Matrix4f &m)
{
model = m; // 设置当前使用的模型矩阵
}
// 设置视图矩阵
void rasterizer::set_view(const Eigen::Matrix4f &v)
{
view = v; // 设置当前使用的视图矩阵
}
// 设置投影矩阵
void rasterizer::set_projection(const Eigen::Matrix4f &p)
{
projection = p; // 设置当前使用的投影矩阵
}
// 清除缓冲区(支持颜色和深度缓冲)
void rasterizer::clear(Buffers buff)
{
if ((buff & Buffers::Color) == Buffers::Color)
{
// 将颜色缓冲区清空为黑色 (0, 0, 0)
std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
}
if ((buff & Buffers::Depth) == Buffers::Depth)
{
// 将深度缓冲区清空为无穷大
std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
}
}
// 绘制函数:支持线段和三角形
void rasterizer::draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, Primitive type)
{
if (type != Primitive::Triangle) // 暂时只实现三角形绘制
{
throw std::runtime_error("Only triangle drawing is implemented.");
}
// 获取顶点缓冲区和索引缓冲区的数据
auto &buf = pos_buf[pos_buffer.pos_id];
auto &ind = ind_buf[ind_buffer.ind_id];
// 计算 MVP 矩阵
Eigen::Matrix4f mvp = projection * view * model;
for (auto &i : ind)
{
Triangle t;
// 将三角形的每个顶点从模型空间变换到裁剪空间
Eigen::Vector4f v[] = {
mvp * to_vec4(buf[i[0]], 1.0f),
mvp * to_vec4(buf[i[1]], 1.0f),
mvp * to_vec4(buf[i[2]], 1.0f)};
// 执行透视除法,将裁剪坐标归一化
for (auto &vec : v)
{
vec /= vec.w();
}
// 将顶点从裁剪坐标转换为屏幕坐标
for (auto &vert : v)
{
vert.x() = 0.5 * width * (vert.x() + 1.0);
vert.y() = 0.5 * height * (vert.y() + 1.0);
vert.z() = vert.z();
}
// 设置三角形的顶点
for (int i = 0; i < 3; ++i)
{
t.setVertex(i, v[i].head<3>());
}
// 设置三角形颜色
t.setColor(0, 255.0, 0.0, 0.0);
t.setColor(1, 0.0, 255.0, 0.0);
t.setColor(2, 0.0, 0.0, 255.0);
// 线框光栅化三角形
rasterize_wireframe(t);
}
}
// 获取帧缓冲的引用
std::vector<Eigen::Vector3f> &rasterizer::frame_buffer()
{
return frame_buf;
}
// 获取像素在帧缓冲中的索引
int rasterizer::get_index(int x, int y)
{
return (height - 1 - y) * width + x; // OpenCV 坐标系与帧缓冲坐标系的转换
}
// 设置像素颜色
void rasterizer::set_pixel(const Eigen::Vector3f &point, const Eigen::Vector3f &color)
{
int ind = get_index(point.x(), point.y()); // 获取像素索引
frame_buf[ind] = color; // 设置帧缓冲中的颜色
}
// 光栅化三角形的线框
void rasterizer::rasterize_wireframe(const Triangle &t)
{
draw_line(t.a(), t.b()); // 绘制边 a->b
draw_line(t.b(), t.c()); // 绘制边 b->c
draw_line(t.c(), t.a()); // 绘制边 c->a
}
// 绘制线段(Bresenham 算法)
void rasterizer::draw_line(Eigen::Vector3f begin, Eigen::Vector3f end)
{
// 实现线段的逐像素绘制
// 使用 Bresenham 算法处理整数像素点
}
} // namespace rst
rasterizer.hpp
文件功能
rasterizer.hpp 声明了光栅化器的类 rasterizer,是实现三角形渲染的核心组件。
1. 缓冲区管理:加载顶点、索引数据,并管理帧缓冲区。
2. 矩阵设置:设置模型矩阵、视图矩阵和投影矩阵。
3. 光栅化操作:将三角形转换为像素数据,执行线框绘制。
4. 帧缓冲管理:设置像素颜色、清空缓冲区等。
文件关系
1. 依赖关系:
依赖 Triangle.hpp:使用 Triangle 类管理三角形数据。
2. 被依赖关系:
被 main.cpp 使用:main.cpp 调用 rasterizer 类加载顶点和索引,设置矩阵并执行绘制。
被 rasterizer.cpp 实现:rasterizer.cpp 实现了该头文件声明的所有功能。
#include "Triangle.hpp" // 包含三角形头文件
#include <algorithm> // 通用算法库
#include <stdexcept> // 异常处理库
// 三角形类的构造函数,初始化顶点和颜色
Triangle::Triangle();
// 设置三角形顶点
void Triangle::setVertex(int ind, Eigen::Vector3f ver);
// 设置三角形法向量
void Triangle::setNormal(int ind, Eigen::Vector3f n);
// 设置三角形颜色
void Triangle::setColor(int ind, float r, float g, float b);
// 设置三角形纹理坐标
void Triangle::setTexCoord(int ind, float s, float t);
// 转换顶点为齐次坐标
std::array<Vector4f, 3> Triangle::toVector4() const;
Triangle.cpp
文件功能
Triangle.cpp 是 Triangle.hpp 的实现文件,负责定义 Triangle 类中声明的所有成员函数的具体逻辑。
1. 设置三角形的顶点、法向量、颜色、纹理坐标。
2. 转换三角形的顶点为齐次坐标(用于后续变换和光栅化操作)。
3. 获取三角形的相关属性(如顶点位置和法向量)。
文件关系
1. 依赖关系:
依赖 Triangle.hpp:Triangle.cpp 实现了 Triangle.hpp 中声明的所有功能。
2. 被依赖关系:
被 rasterizer.cpp 使用:rasterizer.cpp 调用 Triangle 类的接口(如设置顶点、颜色等)来设置三角形属性,用于光栅化。
被 main.cpp 使用:main.cpp 使用 Triangle 类创建三角形对象,并设置顶点和颜色。
3. 与 rasterizer.hpp 的关系:
虽然未直接依赖,但 rasterizer.hpp 的绘制逻辑需要调用 Triangle 的功能获取顶点和颜色数据。
#include "Triangle.hpp" // 包含 Triangle 类的声明
#include <stdexcept> // 异常处理
#include <array> // 标准库数组
#include <eigen3/Eigen/Eigen> // 引入 Eigen 库,用于矩阵与向量计算
// 构造函数:初始化三角形的顶点、颜色、法向量和纹理坐标
Triangle::Triangle()
{
// 初始化顶点为零向量
v[0] = Eigen::Vector3f(0, 0, 0);
v[1] = Eigen::Vector3f(0, 0, 0);
v[2] = Eigen::Vector3f(0, 0, 0);
// 初始化颜色为白色(255, 255, 255)
color[0] = Eigen::Vector3f(255, 255, 255);
color[1] = Eigen::Vector3f(255, 255, 255);
color[2] = Eigen::Vector3f(255, 255, 255);
// 初始化法向量为零向量
normal[0] = Eigen::Vector3f(0, 0, 0);
normal[1] = Eigen::Vector3f(0, 0, 0);
normal[2] = Eigen::Vector3f(0, 0, 0);
// 初始化纹理坐标为零向量
tex_coords = {Eigen::Vector2f(0, 0), Eigen::Vector2f(0, 0), Eigen::Vector2f(0, 0)};
}
// 设置三角形的顶点位置
void Triangle::setVertex(int ind, Eigen::Vector3f ver)
{
if (ind < 0 || ind > 2) // 检查索引是否有效
{
throw std::invalid_argument("Vertex index must be between 0 and 2");
}
v[ind] = ver; // 设置指定索引的顶点位置
}
// 设置三角形的法向量
void Triangle::setNormal(int ind, Eigen::Vector3f n)
{
if (ind < 0 || ind > 2) // 检查索引是否有效
{
throw std::invalid_argument("Normal index must be between 0 and 2");
}
normal[ind] = n; // 设置指定索引的法向量
}
// 设置三角形的颜色
void Triangle::setColor(int ind, float r, float g, float b)
{
if (ind < 0 || ind > 2) // 检查索引是否有效
{
throw std::invalid_argument("Color index must be between 0 and 2");
}
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) // 检查颜色范围
{
throw std::invalid_argument("Color components must be in range [0, 255]");
}
color[ind] = Eigen::Vector3f(r, g, b); // 设置指定索引的颜色
}
// 设置三角形的纹理坐标
void Triangle::setTexCoord(int ind, float s, float t)
{
if (ind < 0 || ind > 2) // 检查索引是否有效
{
throw std::invalid_argument("Texture coordinate index must be between 0 and 2");
}
tex_coords[ind] = Eigen::Vector2f(s, t); // 设置指定索引的纹理坐标
}
// 获取指定索引的顶点位置
Eigen::Vector3f Triangle::getVertex(int ind) const
{
if (ind < 0 || ind > 2) // 检查索引是否有效
{
throw std::invalid_argument("Vertex index must be between 0 and 2");
}
return v[ind]; // 返回顶点位置
}
// 获取指定索引的法向量
Eigen::Vector3f Triangle::getNormal(int ind) const
{
if (ind < 0 || ind > 2) // 检查索引是否有效
{
throw std::invalid_argument("Normal index must be between 0 and 2");
}
return normal[ind]; // 返回法向量
}
// 将顶点转换为齐次坐标(4 维向量)
std::array<Eigen::Vector4f, 3> Triangle::toVector4() const
{
std::array<Eigen::Vector4f, 3> result; // 创建一个数组存储结果
for (int i = 0; i < 3; ++i)
{
// 转换为齐次坐标形式
result[i] = Eigen::Vector4f(v[i].x(), v[i].y(), v[i].z(), 1.0f);
}
return result; // 返回转换结果
}
Triangle.hpp
文件功能
Triangle.hpp 定义了三角形的类(Triangle),用于管理三角形的顶点、颜色、法向量和纹理坐标。
主要功能包括:
1. 存储三角形的顶点数据:包括位置、法向量、颜色、纹理坐标等。
2. 提供设置和获取三角形属性的接口:如设置顶点位置、颜色、法向量等。
3. 将三角形顶点转换为齐次坐标:便于后续进行矩阵变换。
文件关系
1. Triangle.hpp 被以下文件使用:
main.cpp:调用 Triangle 类创建三角形实例,并设置顶点和颜色,用于渲染。
rasterizer.cpp:通过调用 Triangle 的接口,获取三角形顶点数据进行光栅化操作。
2. 被 Triangle.cpp 实现:
Triangle.cpp 实现了 Triangle.hpp 声明的所有功能。
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <eigen3/Eigen/Eigen> // 引入 Eigen 库,用于矩阵与向量计算
#include <array> // 标准库数组,存储顶点和相关属性
class Triangle
{
public:
// 构造函数:初始化三角形
Triangle();
// 设置顶点位置
// ind:顶点索引(0, 1, 2)
// ver:顶点坐标(Eigen::Vector3f 类型)
void setVertex(int ind, Eigen::Vector3f ver);
// 设置顶点法向量
// ind:顶点索引(0, 1, 2)
// n:法向量(Eigen::Vector3f 类型)
void setNormal(int ind, Eigen::Vector3f n);
// 设置顶点颜色
// ind:顶点索引(0, 1, 2)
// r, g, b:颜色分量(float 类型,范围 [0, 255])
void setColor(int ind, float r, float g, float b);
// 设置纹理坐标
// ind:顶点索引(0, 1, 2)
// s, t:纹理坐标(float 类型)
void setTexCoord(int ind, float s, float t);
// 获取指定顶点位置
// 返回:顶点位置向量(Eigen::Vector3f 类型)
Eigen::Vector3f getVertex(int ind) const;
// 获取指定顶点法向量
// 返回:法向量(Eigen::Vector3f 类型)
Eigen::Vector3f getNormal(int ind) const;
// 转换顶点为齐次坐标
// 返回:三角形的三个顶点,齐次坐标形式(std::array<Eigen::Vector4f, 3> 类型)
std::array<Eigen::Vector4f, 3> toVector4() const;
private:
// 三角形的三个顶点位置(Eigen::Vector3f 类型)
Eigen::Vector3f v[3];
// 三角形的三个法向量
Eigen::Vector3f normal[3];
// 三角形的三个颜色值(RGB,每个分量为 float 类型)
Eigen::Vector3f color[3];
// 三角形的三个纹理坐标
std::array<Eigen::Vector2f, 3> tex_coords;
};
#endif // TRIANGLE_H