games101作业1

1.任务

本次作业的任务是完成旋转矩阵和透视投影矩阵(投影转换中的透视矩阵Perspective projection)。

2.渲染前的步骤及细节

我们在此次作业模拟了将三维空间图形经过模型变换、相机变换(View/Camera transformation  )、透视变换、视口变换、最终将变换后的图形打到屏幕上。下面将依次介绍这些变换,以及具体的变换方法和代码展示。

2.1模型变换:

想象我们在拍一张照片。

那么,找到一个好的拍摄地点和安排被拍摄的对象(arrange people)是我们最开始要做的,模型变换指的就是这个。

具体一点:模型变换表现为被投影的物体在世界坐标系下(XYZ)的平移和旋转变换,它变现为我们最初给定的位置,经历了怎样的运动:

matrix*物体原始坐标-------->>>运动后的新坐标

从代码角度实现,本题让我们实现get_model_matrix()函数,逻辑是每次返回饶Z轴旋转的旋转矩阵。旋转矩阵是这样的:
games101作业1_第1张图片

. 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.

2.2视图(相机)变换

Find a good “angle” to put the camera (view transformation),找到一个好角度去放置相机。

想象一下,放置相机时,如何可以确定拍照角度?首先需要确定的是相机的位置(position),再一个是确定相机的朝向(gaze direction),还有一个比较关键,是要确定相机的向上方向。因为相机如果位置定了,朝向定了,如果相机选择了那么照出来的结果也会不同。所以我们需要就上一个向上方向的向量(up direction)。

games101作业1_第2张图片

 可以发现的,如果相机和物体一起移动,就像物理中的相对运动一样,他们的相对运动是不变的。

为了方便之后投影变换,我们需要将此gte左边转换为空间直角坐标,将position移动到坐标系的源点(origin),那么g需要旋转到-z轴,t旋转到Y轴,剩下的g x t旋转到X轴。

games101作业1_第3张图片

 那么用数学公式如何转换?
games101作业1_第4张图片

 Tview 的平移矩阵:如果将目标坐标移动到源点,当然需要移动负的x,y,z了。

其次,做Rview,将坐标轴依次转换成xyz空间直角坐标轴。

games101作业1_第5张图片

 如果我们先从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;

看没有轴旋转的显示结果:
games101作业1_第6张图片

 那么到底该不该在这里进行轴的旋转矩阵操作呢?

2.3投影变换

分为正交投影和透视投影perspective projection

games101作业1_第7张图片

 正交投影可以看做将视点看成在无限远处,拍摄照片似右图效果。

如何做出透视投影?

1.首先将截锥压成长方体(n -> n, f -> f) (M persp->ortho)

2. 做正交投影Mortho

games101作业1_第8张图片

推导“挤压”时用到的矩阵:
 

games101作业1_第9张图片

 首先先要找到被转换的点与源点之间的关系

俯视图可能不够直观,来个立体的:

所以:

 其次坐标表示:

 games101作业1_第10张图片

 games101作业1_第11张图片

 通过上述式子可求矩阵的几个位置的值:

         games101作业1_第12张图片

 再次:?号都是什么?

从截锥体挤压到长方体的矩阵需要推导求得,我们通过两个特殊的地方:

1.任何近平面上的点都不会改变
2.远平面上的任何点z都不会改变

通过推导可得:
games101作业1_第13张图片

 f:远平面在Z轴上的位置

做完了投影变换,接下来就是做正交变换,将矩形体变换为【-1,1】的标准立方体。

games101作业1_第14张图片

 通常做法先将“挤压”好的立方体中心置于坐标源点,再缩放使得其大小为【-1,1】^3。

正交矩阵为:

games101作业1_第15张图片

 先看右侧的平移矩阵,每个点都移动到了-r+l/2的位置,将立方体中心和源点重合;

再看左侧的缩放变换:

首先先求r和l,t和b,n和f

nf值是函数给定的形参,分别赋值即可;

r,l是代表可视角度/2 = 高度一半/n,已知可视角度和n,所以可求t;

t可求,r=t*aspect  所以r可求。也就是高宽比的宽度;接着l和b都可求。

games101作业1_第16张图片

games101作业1_第17张图片

最后附上代码:(矩阵中带入的值进行了简化)
 

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

 做完了几种变换,后面就是光栅化了,之后再谈~

你可能感兴趣的:(Games,矩阵,线性代数)