(三维空间)核心变换:MVP
图形渲染的两个重要过程是变换和着色。
整个过程可以理解为画一张肖像画,变换 = 模特摆pose +画家调整远近和观测角度,着色 = 在(大大小小不同的)画纸上勾勒出轮廓 + 上颜色。
可以发现,前面部分变换总是处理三维空间相关的问题,后面部分着色则总是处理二维平面相关的问题。两者衔接的过程,需要把三维的数据映射到二维屏幕,叫做视口变换。
虽然叫“视口变换”,但处理的是屏幕相关的问题,跟物体平移、旋转、缩放这些核心的变换没有关系。核心的变换包括模型变换M、视图变换V、投影变换P,统称MVP。也叫观测变换Vewing。
模型变换,即物体本身的平移、旋转、缩放。
视图变换,即转动摄像机观测物体。
投影变换,即展现物体的方式,有两种。第一种:人眼看物体,离得越远显得越小,最后会交汇与远处的一个消失点,叫透视投影;第二种,设计图上画的物体远近都是一样大的,叫正交投影。
我们知道变换的数学方法是矩阵乘法,平移、旋转、缩放的矩阵分别长啥样请看:《几种基本矩阵变换的推导过程》
我们发现,在三维空间中,平移和缩放两种矩可以叠在一起,并不冲突
而旋转矩阵则被抛弃了,要单独凉一边。
我们可以把三维空间中的任何旋转分解成各自3个平面上的旋转,分别是绕x轴旋转、绕y轴旋转、绕z轴旋转,而这三兄弟的矩阵居然不是完全一样的:
可以看到,在右手定则中,绕x轴或z轴旋转,矩阵都是(cosα -sinα,sinα cosα),绕y轴旋转却是(cosα sinα,-sinα cosα)。
究其原因,是因为右手定则的z方向,是由x向y指向得到的,即x-y->z,y-z->x,z-x->y。
而从图中可以看到,绕y轴旋转,实际上是x-z,跟z-x相反,所以表示方法跟其他两兄弟不一样。
那么,有没有一个公式是给出任意角度,直接应用就可以得到新的向量/点?
有的,叫“罗格里格斯旋转公式” ,可以计算出,绕任意方向的轴n旋转α度后的结果(轴n过原点):
注意了,旋转一定是绕着坐标轴原点(0, 0, 0)的,如果要做绕着物体中心的旋转变换,要拆解成3步:把物体中点平移到原点,做旋转变换,再逆向移回原位置。
现在我们来看视图变换的整个过程。
首先,先“摆正”摄像机的位置,这样就能减少一个变量因素。摆正的过程,是把摄像机从任意位置任意朝向->>>>放到原点位置 + 顶朝+y方向 + 看向-z方向
如图所示,摆好摄像机 = e点平移到原点 + g方向旋转到-z方向 + t方向旋转到y方向 + (g * t )旋转到x方向。
e点平移到原点,这个很好写,只要平移(-ex, -ey, -ez)即可。
而后面的三个旋转,则非常不好写,如果按常规的操作,每一个旋转都要先找到旋转轴,再应用一次上面提到的“罗格里格斯旋转公式”,复杂程度可想而知。
不过我们只要逆向思维,问题就迎刃而解:任意轴旋转到标准轴,其实就是标准轴旋转到任意轴的逆变换,也就是说,我们只要求出标准轴旋转到任意轴的变换矩阵,求它的逆,就可以得到任意轴旋转到标准轴的变换矩阵。而从标准轴旋转到任意轴,不就是分别乘3次(cosα -sinα,sinα cosα)嘛!同时,对于所有的正交矩阵,逆变换 = 转置。旋转矩阵是正交矩阵,求它的逆变换就是只需要转置一下它即可。
至此,相机已经摆正,可以进行下一步。
我们知道,在实际生活中,一个物体同样的大小同样的位置,我们用不同的方式去描述它,就会呈现不同的结果,比如设计图和素描图。
不管是平行投影还是透视投影,最终都是要显示在屏幕上的,因此我们必须要做的一个步骤,是把摄像机所看到的范围映射到屏幕上。
既然屏幕是方的,而且每种屏幕的大小各不一样,于是我们这么做:
1.虚拟建立一个2*2*2的规范化正方体;
2.虚拟建立一个长方体,以表示摄像机覆盖的范围;
3.把长方体里模型的点均匀挤压到规范正方体内;
4.最后,把规范化正方体里的点映射到不同大小的屏幕上。
正交投影
人的眼睛看向远方,看到远平面的物体和近平面的一样大,并没有缩小,即正交投影。
正交投影“把长方体里模型的点均匀挤压到规范正方体内”的方法很简单,想象一个任意大小的长方体压成2*2*2的正方体,就是把长方体的长宽高分别缩成2,即
x` = x / w *2 = (2 / (r - l)) * x , l表示left,r表示right
y` = y / h * 2 = (2 / (t - b)) * y , t表示top,b表示bottom
z` = z / d * 2 = (2 / (f - n)) * z , f表示far,n表示near
注意,如果这个长方体的中心不是在原点,需要先平移到原点,再做缩放。
透视投影
人的眼睛看向远方,远平面的物体缩小到近平面里,即透视投影。
透视投影变换要做2步:
1.把透视空间中的锥体Frustum压成正交空间的长方体Cuboid;
2.做一次正交投影。
第2步我们已经会了,现在我们来求锥体Frustum压成长方体Cuboid的矩阵。
我们称它为矩阵M。锥体Frustum上任意平面上的任意点,乘M后都会得到,对应在长方体Cuboid上的一个点。
求解的过程分几步走:
【第一步】我们通过观察发现,在所有平面,x和y值都跟z有直接关系,可以用相似三角形求出,x` = (n/z)x, y` = (n/z)y
由此可知,经过变换后的点为
在其次坐标中,我们可以把变换后的点的每一个值都乘以z,表示的是同一个点:
根据矩阵乘法,知道M一定长这样:
【第二步】我们知道,在近平面上,所有的点变换以后都等于它本身,我们可以利用这个特殊情况,把n代入z,并再次利用齐次坐标的特性,让变换后的点的每一项都乘以n,可得:
现在我们只关注红色框的部分:n通过矩阵乘法得到n²,即 ?x + ?y + ?n + ?1 = n²,可知前面2个?都一定等于0
就剩 A 和 B 的值未知了。
【第三步】
对于近平面上的任何点,可推出:
现在再找一个特殊点,即远平面的中心点,它的x和y都等于0,z等于f,可推出:
根据红框中的两个公式,可求出
最终,我们求出了这个“挤压”的矩阵
用它乘正交投影矩阵,即得到透视投影的矩阵变换。
注意!!!在整个求解过程中,前面所说可用相似三角形求出不同平面的x和y值,并没有说可以用同样的方法求出z值。
近平面上点的z值等于n,远平面上点的z值等于f,但其他平面上的点的z值跟z并不是直接的相似关系!
也就是说,近平面和远平面正中间的平面,挤压以后,点的z值并不是等于(zf + nf) / 2
这又是一个跟直观上有出入的结论。而结果等于多少,用“挤压”公式算一遍就知道了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律