参考文章:
GAMES101作业(更新中)
GAMES101】作业1(提高)与框架理解
GAMES101 学习笔记 Lecture 1~6
闫令琪:Games101 现代计算机图形学-作业Assignment02解析
文章目录
关于变换的流程
a.旋转矩阵:作业02中省略
b.平移矩阵:把相机的位置平移到原点,物体也会被平移
c.仿射变换:把透视相机变成平行相机
d.平移矩阵:把变成平行相机的相机空间移到原点
e.缩放矩阵:把相机空间缩放到[-1,1]
f.把齐次坐标的第四分量也就是w分量变为1
g.把[-1,1]的坐标空间映射到屏幕空间,也就是映射到有多少分辨
这才算是全部的变换完成,然后接下来才是光栅化。
完成变化之后,便是光栅化
三部分:
MVP的过程

Model Transformation(绕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.
//角度转弧度,便于计算
rotation_angle = rotation_angle / 180.0f * MY_PI;
//模型旋转矩阵(绕z轴)
Eigen::Matrix4f translate;
translate << cos(rotation_angle), -sin(rotation_angle), 0, 0,
sin(rotation_angle), cos(rotation_angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = translate * model;
return model;
}
View Transformation(代码中已经写好)

Tview已经写好,而Rview没用上
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
//从原来位置移动到原点位置
Eigen::Matrix4f translate;
translate << 1, 0, 0, -eye_pos[0],
0, 1, 0, -eye_pos[1],
0, 0, 1, -eye_pos[2],
0, 0, 0, 1;
view = translate * view;
return view;
}
透视投影矩阵(Projection Transformation)


步骤1:挤压

步骤2:正交投影

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.
eye_fov = eye_fov / 180.0f * MY_PI;
//求出所需参数
float height = tan(eye_fov / 2.0f) * abs(zNear) * 2.0f;
float width = height * aspect_ratio;
float length = zNear - zFar;
//透视投影中将frustum给挤压成一个长方体
Eigen::Matrix4f p2o;
p2o << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;
Eigen::Matrix4f orthoT;
//以下两个为正交投影
orthoT << 1.0f, 0, 0, 0,
0, 1.0f, 0, 0,
0, 0, 1.0f, -(zNear + zFar) / 2.0f,
0, 0, 0, 1.0f;
Eigen::Matrix4f orthoS;
//在观测投影时xoy平面视角默认为中心,所以不需要平移x和y了
orthoS << 2.0f / width, 0, 0, 0,
0, 2.0f / height, 0, 0,
0, 0, 2.0f / length, 0,
0, 0, 0, 1;
//1.先挤压 2.正交投影
projection = orthoS * orthoT * p2o * projection;
return projection;
}
到绕任意过原点的轴的旋转变换矩阵

Eigen::Matrix4f get_rotation(Vector3f axis, float angle) {//任意轴旋转矩阵(罗德里格斯旋转公式,默认轴过原点)
double fangle = angle / 180 * MY_PI;
Eigen::Matrix4f I, N, Rod;
Eigen::Vector4f axi;
Eigen::RowVector4f taxi;
axi << axis.x(), axis.y(), axis.z(), 0;
I.Identity();
N << 0, -axis.z(), axis.y(), 0,
axis.z(), 0, -axis.x(), 0,
-axis.y(), axis.x(), 0, 0,
0, 0, 0, 1;
Rod = cos(fangle) * I + (1 - cos(fangle)) * axi * axi.transpose() + sin(fangle) * N;
Rod(3, 3) = 1;//这里要注意,非齐次坐标的公式应用在齐次坐标上时记得运算完成后把矩阵的右下角改为1,否则会导致图形比例错误
return Rod;
}
总代码
main.cpp
#include "Triangle.hpp"
#include "rasterizer.hpp"
#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace Eigen;
constexpr double MY_PI = 3.1415926;
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
//从原来位置移动到原点位置
Eigen::Matrix4f translate;
translate << 1, 0, 0, -eye_pos[0],
0, 1, 0, -eye_pos[1],
0, 0, 1, -eye_pos[2],
0, 0, 0, 1;
view = translate * view;
return view;
}
Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
{
// 罗德里格旋转公式
Eigen::Matrix4f rot = Eigen::Matrix4f::Identity();
angle = angle / 180.0f * MY_PI;
Eigen::Matrix3f N;
N << 0, -axis[2], axis[1],
axis[2], 0, -axis[0],
-axis[1], axis[0], 0,
0, 0, 0;
Eigen::Matrix3f translate3f;
translate3f << cos(angle) * Eigen::Matrix3f::Identity()
+ (1 - cos(angle)) * (axis * axis.transpose())
+ sin(angle) * N;
Eigen::Matrix4f translate4f;
translate4f.block<3, 3>(0, 0) = translate3f;
translate4f(0, 3) = 0.0f;
translate4f(1, 3) = 0.0f;
translate4f(2, 3) = 0.0f;
translate4f(3, 3) = 1.0f;
rot = translate4f * rot;
return rot;
}
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.
//角度转弧度,便于计算
rotation_angle = rotation_angle / 180.0f * MY_PI;
//模型旋转矩阵(绕z轴)
Eigen::Matrix4f translate;
translate << cos(rotation_angle), -sin(rotation_angle), 0, 0,
sin(rotation_angle), cos(rotation_angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = translate * model;
return model;
}
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.
eye_fov = eye_fov / 180.0f * MY_PI;
//求出所需参数
float height = tan(eye_fov / 2.0f) * abs(zNear) * 2.0f;
float width = height * aspect_ratio;
float length = zNear - zFar;
//透视投影中将frustum给挤压成一个长方体
Eigen::Matrix4f p2o;
p2o << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;
Eigen::Matrix4f orthoT;
//以下两个为正交投影
orthoT << 1.0f, 0, 0, 0,
0, 1.0f, 0, 0,
0, 0, 1.0f, -(zNear + zFar) / 2.0f,
0, 0, 0, 1.0f;
Eigen::Matrix4f orthoS;
//在观测投影时xoy平面视角默认为中心,所以不需要平移x和y了
orthoS << 2.0f / width, 0, 0, 0,
0, 2.0f / height, 0, 0,
0, 0, 2.0f / length, 0,
0, 0, 0, 1;
//1.先挤压 2.正交投影
projection = orthoS * orthoT * p2o * projection;
return projection;
}
int main(int argc, const char** argv)
{
float angle = 0;//定义角度
bool command_line = false;//定义命令行开关标志,默认为关
std::string filename = "output.png";//定义文件名,默认为output.png"
Eigen::Vector3f raxis(0, 0, 1);
double rangle = 0, ra;
if (argc >= 3) {//接收到的参数大于三个,即检测到通过命令行传入参数时
cout<<"argc[1]="<<argv[1]<<endl;
cout<<"argc[2]="<<argv[2]<<endl;
cout<<"argc[3]="<<argv[3]<<endl;
command_line = true;//设命令行开关标志为开
angle = std::stof(argv[2]); //从命令行获取角度参数
if (argc == 4) {//接收到的参数为四个,那么说明命令行输入了文件名参数
filename = std::string(argv[3]);//从命令行获取文件名
}
}
rst::rasterizer r(700, 700);//设定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) {//如果命令行开关标志为开(这一段if代码是为了应用命令行传入的参数,比如初始角度和文件名)
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));//向光栅器传入MVP矩阵
r.set_rodrigues(get_rotation(raxis, rangle));
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::imwrite(filename, image);//写入文件名
return 0;
}
bool rflag = false;
std::cout << "Please enter the axis and angle:" << std::endl;
std::cin >> raxis.x() >> raxis.y() >> raxis.z() >> ra;//定义罗德里格斯旋转轴和角
while (key != 27) {//只要没有检测到按下ESC就循环(ESC的ASCII码是27)
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));
if (rflag) //如果按下r了,就开始绕给定任意轴旋转
r.set_rodrigues(get_rotation(raxis, rangle));
else
r.set_rodrigues(get_rotation({ 0,0,1 }, 0));
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);//等待10号码接收键盘输入,没有输入就为空,图像不做调整,保持原状
std::cout << "frame count: " << frame_count++ << '\n';//显示当前是第几帧画面
if (key == 'a') {//按下a,逆时针旋转10°
angle += 10;
}
else if (key == 'd') {//按下d,顺时针旋转10°
angle -= 10;
}
else if (key == 'r') {//按下r,绕给定任意轴旋转
rflag = true;
rangle += ra;
}
}
return 0;
}

本文详细介绍了在GAMES101课程中如何构建和应用绕z轴旋转的模型矩阵、视图矩阵以及透视和正交投影矩阵,展示了如何通过MVP过程实现三维图形的变换和渲染,包括使用罗德里格斯旋转公式进行任意轴旋转。
799

被折叠的 条评论
为什么被折叠?



