GAMES101作业1
作业目标:
get_model_matrix(float rotation_angle)
:
逐个元素地构建模型变换矩阵并返回该矩阵。在此函数中,你只需要实现三维中绕 z 轴旋转的变换矩阵,而不用处理平移与缩放
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
:
使用给定的参数逐个元素地构建透视投影矩阵并返回该矩阵。
实现:
get_model_matrix(float rotation_angle)
三维空间,绕Z轴旋转,由一个角度,得到一个旋转矩阵
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
float theta = (rotation_angle / 180.0) * MY_PI;//角度转弧度
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();//新建一个4x4的矩阵
model << std::cos(theta), -std::sin(theta), 0, 0,
std::sin(theta), std::cos(theta), 0, 0,
0, 0, 1.0, 0,
0, 0, 0, 1.0;
//PS:绕X、Z轴旋转的旋转矩阵同理,绕Y轴的旋转矩阵略有不同,sin和-sin的位置会互换
return model;
}
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
摄像机上方是y正半轴,右边是x正半轴、看向z负半轴
现在假设我们站在x正半轴 看向x负半轴
eye_fov:视角,摄像机最上方能看到的角度到最下方能看到的角度
aspect_ratio:矩形宽高比
zNear、zFar:近处面的z值,远处面的z值,注意,朝向z负半轴,即两者均为负值
目标得到投影矩阵,先将透视图转化为正交图,然后再进行缩放,使得x、y、z三维方向的值的范围均在[-1,1],最后将其平移到视口中心
视锥->立方体
透视图->正交图
根据zFar、zNear、eye_fov、aspect_ratio可以算出宽、高。
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
//Eigen::Matrix4f::Identity() 初始化为单位矩阵
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
//透视图,近大远小,是个视锥 此矩阵是一个公式
Eigen::Matrix4f P2O = Eigen::Matrix4f::Identity();//将透视矩阵挤压成正交矩阵
P2O << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, - zFar* zNear,
0, 0, 1.0, 0;
float halfEyeAngelRadian = (eye_fov / 2.0 / 180.0) * MY_PI; //视角的一半
float y_top = -zNear * std::tan(halfEyeAngelRadian);//y轴正方向值 = 显示视口的一半高度 zNear是负值!
float x_left = -y_top * aspect_ratio;//x轴负方向值 = 显示视口的一半宽度
float y_down = -y_top;
float x_right = -x_left;
//构造缩放矩阵,使视口大小等同窗口大小
Eigen::Matrix4f scaleMat = Eigen::Matrix4f::Identity();
scaleMat << 2 / (x_right - x_left), 0, 0, 0, //将中心视为原点,则窗口的三维方向值域均为[-1,1]
0, 2 / (y_top - y_down), 0, 0, //缩放的倍数为 期望值/当前值
0, 0, 2 / (zNear - zFar), 0, //所以缩放的倍数为 (1+1)/某一维度的当前值
0, 0, 0, 1;
//构造平移矩阵,将视口左下角移动到原点
Eigen::Matrix4f translateMat = Eigen::Matrix4f::Identity();
//左下角的点原本为 (x_left,y_down,zNear)
//注意!此时已经经过了缩放,所以左下角的点的位置已经变化
//左下角的点现在为 (-1,-1,zNear)
//即其实可以不用管x和y轴,比较尺寸已经和窗口匹配了
//但网上其他人却还是右下方注释的那样写的,左侧+右侧或者上侧+下侧,结果不都是0么?
translateMat << 1, 0, 0, 0, //-(x_left+x_right)/2
0, 1, 0, 0, //-(y_top+y_down)/2
0, 0, 1, -(zNear+zFar)/2,
0, 0, 0, 1;
//注意!此处矩阵相乘,右结合率,必须先压缩矩阵,再缩放,最后平移,顺序一定不可以改!
projection = translateMat * scaleMat * P2O;
return projection;
}