本次作业的任务是完成旋转矩阵和透视投影矩阵(投影转换中的透视矩阵Perspective projection)。
我们在此次作业模拟了将三维空间图形经过模型变换、相机变换(View/Camera transformation )、透视变换、视口变换、最终将变换后的图形打到屏幕上。下面将依次介绍这些变换,以及具体的变换方法和代码展示。
想象我们在拍一张照片。
那么,找到一个好的拍摄地点和安排被拍摄的对象(arrange people)是我们最开始要做的,模型变换指的就是这个。
具体一点:模型变换表现为被投影的物体在世界坐标系下(XYZ)的平移和旋转变换,它变现为我们最初给定的位置,经历了怎样的运动:
matrix*物体原始坐标-------->>>运动后的新坐标
从代码角度实现,本题让我们实现get_model_matrix()函数,逻辑是每次返回饶Z轴旋转的旋转矩阵。旋转矩阵是这样的:
. get_model_matrix(float rotation_angle):逐个元素地构建模型变换矩阵并返回该矩阵。在此函数中,你只需要实现三维中绕z轴旋转的变换矩阵,而不用处理平移与缩放。
旋转矩阵转为 代码如下:
Eigen :: Matrix4f get_model_matrix(float rotation_angle){
Eigen :: Matrix4f model = Eigen ::Matrix4f ::Identity();
model << std ::cos (rotation_angle/180*acos ( -1)),
std ::sin((rotation_angle/180*acos ( -1))*( -1),0 ,0
,std ::sin(rotation_angle/180*acos ( -1)),
std ::cos (rotation_angle/180*acos ( -1)),0,0,
0,0,1,0,
0,0,0 ,l;
return model;
}
acos(-1)就是圆周率,sin函数需要角度转弧度,所以需要*π/180.
Find a good “angle” to put the camera (view transformation),找到一个好角度去放置相机。
想象一下,放置相机时,如何可以确定拍照角度?首先需要确定的是相机的位置(position),再一个是确定相机的朝向(gaze direction),还有一个比较关键,是要确定相机的向上方向。因为相机如果位置定了,朝向定了,如果相机选择了那么照出来的结果也会不同。所以我们需要就上一个向上方向的向量(up direction)。
可以发现的,如果相机和物体一起移动,就像物理中的相对运动一样,他们的相对运动是不变的。
为了方便之后投影变换,我们需要将此gte左边转换为空间直角坐标,将position移动到坐标系的源点(origin),那么g需要旋转到-z轴,t旋转到Y轴,剩下的g x t旋转到X轴。
Tview 的平移矩阵:如果将目标坐标移动到源点,当然需要移动负的x,y,z了。
其次,做Rview,将坐标轴依次转换成xyz空间直角坐标轴。
如果我们先从xyz转换到gte是比较好转换的,
可以测试矩阵的正确性,如带入1,0,0,0,乘以Rview-1 是可以得到X轴的坐标值 g*t的
(Xg*t )x (1)+(Xt)x(0)+(X-g)x(0) = g*t;
如果被转换positon坐标为(0,0,5),如何求出Rview中各值呢?
第一行的值代表X轴,也是g*t 求得,第二行代表up direction,也就是Y轴,xyz坐标系下的UP direction 方向应该是(0,0,1),第三行代表-Z轴,由-g坐标求得。
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos){
Eigen :: Matrix4f view = Eigen :: Matrix4f :: Identity();
Eigen ::Vector3f g = eye_pos;
Eigen ::Vector3f t = {0,1,0};Eigen ::Vector3f e = g.cross(t);
view << e.x( ),e.y () ,e.z(),0,
t.x() ,t.y() ,t.z(),0,
-g.x(),-g.y(),-g.z(),0
,0,0,0,l;
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,l;
view = translate * view;
return view;
}
作业中给出的get_view_matrix 函数只给出了把目标位置移动到源点(Tview) ,并没有进行Rview.
目标坐标输入为(0,0,5)。在后面的实现中,如果不在此函数中实现坐标轴的旋转,显示在屏幕的三角形是从Z轴的平行视角看的(沿着Z轴)。而在此函数中实现了坐标轴的旋转,我们看向三角形的角度与Z轴行成了一定的角度。但在屏幕中不能完全看到三角形的全貌,但是如果下面的g轴如果为负,结果貌似还正常一点:
Eigen ::Vector3f g = - eye_pos;
那么到底该不该在这里进行轴的旋转矩阵操作呢?
分为正交投影和透视投影perspective projection
正交投影可以看做将视点看成在无限远处,拍摄照片似右图效果。
如何做出透视投影?
1.首先将截锥压成长方体(n -> n, f -> f) (M persp->ortho)
2. 做正交投影Mortho
推导“挤压”时用到的矩阵:
首先先要找到被转换的点与源点之间的关系
俯视图可能不够直观,来个立体的:
所以:
其次坐标表示:
通过上述式子可求矩阵的几个位置的值:
再次:?号都是什么?
从截锥体挤压到长方体的矩阵需要推导求得,我们通过两个特殊的地方:
1.任何近平面上的点都不会改变
2.远平面上的任何点z都不会改变
f:远平面在Z轴上的位置
做完了投影变换,接下来就是做正交变换,将矩形体变换为【-1,1】的标准立方体。
通常做法先将“挤压”好的立方体中心置于坐标源点,再缩放使得其大小为【-1,1】^3。
正交矩阵为:
先看右侧的平移矩阵,每个点都移动到了-r+l/2的位置,将立方体中心和源点重合;
再看左侧的缩放变换:
首先先求r和l,t和b,n和f
nf值是函数给定的形参,分别赋值即可;
r,l是代表可视角度/2 = 高度一半/n,已知可视角度和n,所以可求t;
t可求,r=t*aspect 所以r可求。也就是高宽比的宽度;接着l和b都可求。
最后附上代码:(矩阵中带入的值进行了简化)
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f Mpersp, Mscale, Mtrans, MP2O;
float fovY = eye_fov*PI/180.0;
float t = abs(zNear)*tanf(fovY/2);
float r = t*aspect_ratio;
Mscale << 1/r, 0, 0, 0,
0, 1/t, 0, 0,
0, 0, 2/(zNear-zFar), 0,
0, 0, 0, 1;
Mtrans << 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -(zNear+zFar)/2,
0, 0, 0, 1;
MP2O << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear+zFar, -zNear*zFar,
0, 0, 1, 0;
Mpersp = Mscale*Mtrans*MP2O;
return Mpersp;
}
————————————————
版权声明:本文为CSDN博主「感天动地大白狗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41265365/article/details/124229095