从顶点坐标到我们最终看到的效果,中间要经历多个坐标系的转换:
对于我们来讲,只需要关注三个矩阵即可:Vclip=Mprojection⋅Mview⋅Mmodel⋅Vlocal
Mmodel
:将局部坐标转为世界坐标的矩阵,注意此时的平移是以物体本身的坐标系为基准的Mview
:将世界坐标以摄像机的视角进行转换,比如向右平移,实际上视图会向左平移Mprojection
:对观察坐标进行投影
Camera/View space
通过view matrix
,将世界坐标系相对摄像机的位置和方向进行转换,就可以得到view space
。摄像机在世界坐标系的位置定义如下:
注意摄像机计算 Right
向量时采用的是右手系,如果 cross
相反时左右移动也会相反。
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);
有了位置信息之后,可以计算出LookAt matrix
:
利用glm
可以更便捷的构造出该矩阵:
glm::mat4 view;
// Position Target Up
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
Walk around
移动视图其实是改变摄像机的位置,具体代码可以参照 -> 👉
Look around
Euler angles
俯仰角(pitch)
是描述我们如何往上或往下看的角,可以在第一张图中看到。第二张图展示了偏航角(yaw)
,表示我们往左和往右看的程度。滚转角(roll)
代表我们如何翻滚摄像机,通常在太空飞船的摄像机中使用。
鼠标左右移动改变了yaw
,上下移动改变了pitch
,变换的坐标可以计算为:
glm::vec3 direction;
// yaw 在xz平面, pitch 在yz平面
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
direction.y = sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
front
向量就是摄像头看向的方向。这与现实中的摄像头的镜头相当,决定了摄像头的视线方向。
偏航角和俯仰角是用来控制视觉的方向的。偏航角决定了摄像机向左或向右转,就像控制方向盘一样;俯仰角决定了摄像机向上或向下旋转。
使用三角函数cos
和sin
将这两个角度转换为一个三维向量,这就是所说的计算出的坐标向量。这个向量指向的方向就是摄像头的前方。
摄像机的 viewMatrix
也可以用(float4x4(translation: target) * float4x4(rotationYXZ: rotation)).inverse
。
为了理解为什么视图矩阵是这样计算的,我们首先需要理解在3D中,摄像机其实并没有移动和旋转:世界中所有的物体都围绕摄像机旋转和移动,而摄像机总是在坐标原点并朝向-z方向。也就是说,所有的物体都要应用一个变换,这个变换产生的效果看起来就像是摄像机在移动和旋转。
假设我们将摄像机放在世界坐标中的target
位置,并绕Y轴
和X轴
按rotation
旋转,那么物体要经历的变换就是先绕Y轴
和X轴
旋转-rotation
,然后在世界中平移-target
。在矩阵变换中,我们要做的是将摄像机的旋转作用在物体上,然后将摄像机的平移作用在物体上,这就需要首先用rotation
计算旋转矩阵,然后用target
计算平移矩阵,最后得到的矩阵其实就是物体需要的变换。
然而,在3D中,我们通常对物体应用的变换都是模型矩阵,视图矩阵,以及透视矩阵。为了遵循这个习惯,我们需要做的就是求出float4x4(translation: target) * float4x4(rotationYXZ: rotation)
的逆矩阵,这个逆矩阵就是视图矩阵。
Zoom
进行透射投影即可起到放大缩小的效果,具体代码可以参照 -> 👉