理论回顾
Model transformation
解决两个问题:1、将相对坐标转换为绝对坐标;2、对模型进行位移、旋转和缩放;
目的可以概括为在”虚拟世界中“放置一个模型。
Unity中的Transform就是干这件事情,如图:
View(Camera) transformation
将世界坐标变换到摄像机坐标。先将相机平移到坐标原点,然后将相机的视角方向变换到(0,0,-1),视角变换的原理是向量空间的基底变换。
Projection transformation
首先澄清一个误区:这里的投影变换不是指的把3D空间的向量投影到平面上,而是一种对三维空间顶点的线性变换,类似于向量a在向量b上的投影,结果为一个与向量b同方向向量。投影变换指的是将视景体(view volume)里的顶点变换到一个规则观察体(canonical view volume)
-
正交投影(cuboid to “canonical” cube
)
将视觉立方体里面的物体投影到单位立方里里去(下面左图到右图)
-
立方体通常这样表示:[l,r]X[b,t]X[f,n],即xyz表示的是:左右、上下、远近。
-
通常这几个值是用户设定的,xy取决于视角和渲染比例;z中的远平面f大小取决于渲染距离,很多游戏提供了这个选项给用户调整,来改变画面的精细程度。
然后是translate和scale操作:
这样操作完,渲染区域内的物体就被放在了CVV里面。
-
透视投影(frustum to “canonical” cube)
透视投影与正交投影最大的区别是有“近大远小”的视觉效果,物理会随着离摄像机的距离产生大小变化。
首先将视椎体(frustum)挤压成视觉立方体,这一步的操作可以用如下P矩阵表示:
推导过程略,用的是待定系数法,需要找两个在挤压过程中不变的点。这里的“挤压”操作实际上是对距离(f)不同的物体,施加不同的伸缩变换,根据距离调整大小。最终的效果是将视椎变成了orthography view volume。
然后按照正交投影变换为规则观察体:
这样就把视椎里的物体变换到了单位立方体内。
viewport transformation
视口变换,又称为窗口变换。将规则观察体内的单位图像矩阵映射到屏幕空间的像素矩阵。 它仅取决于输出图像的大小和位置。
实验代码
模型变换
按照要求实现绕z轴旋转,需要现将角度转换为弧度,然后在矩阵中填入正确的数值。
Eigen::Matrix4f get_model_matrix(float rotation_angle){
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
rotation_angle = Degree(rotation_angle);
model<<cos(rotation_angle), -sin(rotation_angle), 0, 0,
sin(rotation_angle), cos(rotation_angle), 0, 0,
0,0,1,0,
0,0,0,1;
return model;
}
投影变换
要实现透视投影
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar){
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
float n=-zNear;
float f=-zFar;
float t=zNear*tan(Degree(eye_fov/2.0f));
float r=aspect_ratio*t;
projection<< n/r, 0, 0, 0,
0, n/t, 0, 0,
0, 0, (n+f)/(n-f), -2*f*n/(n-f),
0, 0, 1, 0;
return projection;
}
这里要先用视角角度计算view volume的宽高,然后代入正交投影矩阵,程序中先根据几何关系计算出t和r。需要注意的是view volume的中心点在z轴上,所以l+r=0,且t+b=0,只需要考虑边长即可。
需要注意的是输入进来的zNear和zFar都是正的,上面理论部分的推导是基于zFar<zNear<0的,所以要先取反,但这样计算出的三角形和标准答案也不太一样(具体表现在旋转了90度)。
但这样也不影响,因为图形学的实验注重所实现的效果,在这里已经实现了模型变换和投影变换,而结果与标准答案有区别并不重要。
参考资料:
[1] Fundamentals of Computer Graphics 5th Edition.
[2] games101 Lecture