实时渲染基础(1)变换(Transformation)


模型变换(Model Transformation)


模型变换:模型空间坐标可经过模型变换得到世界空间坐标。模型变换可理解为设置场景中模型的位置(包括角度、大小)。在进行模型变换时,大多数应用(符合人的直觉)都会按照以下顺序进行变换:伸缩、旋转、位移

\(M_{model} = T_{model}R_{model}S_{model}\)

为什么需要按照伸缩、旋转、位移的顺序进行变换?
比方说,通过变换希望获得的结果可能是:将一个放在原点的可乐罐移动到(30,50),让它自身倾斜45度,再放大2倍。 而不希望的结果是:

  • 当缩放在旋转之后时,会发生和本地坐标轴成角度的缩放(会导致扭曲,像踩扁的可乐罐)。
  • 当缩放和旋转在*移之后时,会发生进行绕自己几何中心以外位置的原点的旋转(地球公转式)和缩放。

变换常用有三种:伸缩【线性变换】、旋转【线性变换】、位移【*移变换】。

而我们常听到的仿射(affline)变换,实际上就是线性变换与*移变换相乘后的变换结果。

实际变换还有不常用的扭曲和镜像,但它们不是仿射变换

伸缩(Scale)

\(S(s_x,s_y,s_z) = \begin{pmatrix} s_x&0&0&0\\ 0&s_y&0&0\\ 0&0&s_z&0\\ 0&0&0&1\\ \end{pmatrix}\)

旋转(Rotation)

绕向量n旋转\(\alpha\)弧度角,最后得到3X3矩阵还要扩展成4X4矩阵

\(R(n,\alpha) = cos(\alpha)E+(1-cos(\alpha))nn^T+sin(\alpha)\begin{pmatrix} 0&-n_z&n_y\\ n_z&0&-n_x\\ -n_y&n_x&0\\ \end{pmatrix}\)

任何旋转必定是正交变换(理由可以自己去查),而正交矩阵的好处就是它的逆矩阵必定是转置矩阵,这样求逆的时候就不必通过复杂的求逆方法(例如用克莱姆法则)

位移(Translation)

\(T(t_x,t_y,t_z) = \begin{pmatrix} 1&0&0&t_x\\ 0&1&0&t_y\\ 0&0&1&t_z\\ 0&0&0&1\\ \end{pmatrix}\)

齐次坐标表示法

齐次坐标系是与笛卡尔坐标不同的坐标表示法,相当于笛卡尔坐标再增广一个w维度,如笛卡尔坐标\((3,4,0)\)可扩展成齐次坐标\((3,4,0,1)\)

  • \(3D point = (x,y,z,1)^T\) :齐次坐标的w分量为1时,该向量视为点\((x,y,z)\)
  • \(3D vector = (x,y,z,0)^T\) :齐次坐标的w分量为0时,该向量视为向量\((x,y,z)\)
  • \(3D point = (x,y,z,w)^T\) :齐次坐标的w分量不为0时,该向量视为齐次坐标\((\frac{x}{w},\frac{y}{w},\frac{z}{w},1)\)
    ,即对应笛卡尔点\((\frac{x}{w},\frac{y}{w},\frac{z}{w})\)

规定点w分量为1、向量w分量为0是有根据的,可以自己推理一下以下向量减法的结果(计算w分量的值)是不是符合常识:

  • point - point = vector
  • point + vector = point
  • vector + vector = vector

图形学常用齐次坐标,是因为三维空间下的位移只可用三维矩阵加法,而线性变换却都是用三维矩阵乘法。为了统一这些变换的表示,引入了齐次坐标,通过增加一个维度便可以让矩阵乘法完美支持三维空间下的位移。

这样一来,我们不仅可以统一所有变换都使用乘法,还可以用4X4矩阵表示一个将若干个变换(无论是线性还是*移)压缩后的变换,从而节省内存:

\(A_{compose} = A_1A_2A_3...\)

因此,三维空间下的变换往往用4X4矩阵表示,而点/向量则用4个分量的向量表示。

视图变换/观察变换(View/Camera Transformation)


首先,在产生视图之前,我们需要定义摄像机(位置、朝前方向、朝上方向),也就是我们人眼在哪个地方朝某个方向看。同时我们还注意到,摄像机在不同的位置朝不同的方向看,在有些情况下应该看到的画面是等价的:

因此为了方便计算,我们可以以摄像机的位置视为原点,以朝前方向为Z轴、朝上方向为Y坐标轴建立一个坐标系(摄像机坐标系)

视图变换/观察变换:世界空间坐标可经过视图变换得到观察空间坐标。视图变换可理解为将物体的世界坐标系位置(包括角度)转换成在摄像机坐标系的位置(包括角度),换句话说就是求物体相对于摄像机的位置(包括角度)。

注:视图变换不会改变大小

摄像机的世界坐标是 \(M_{modelC} = T_{modelC}R_{modelC}\)

但想象一下,现在我们要将摄像机的世界坐标经过一定变换,变换成在原点的位置且轴向与朝向一致,这样的话求物体相对于摄像机的位置实际上就是让物体也经过相同的变换后得到的位置,因此有了下面视图变换的公式:

\(M_{view} = R_{view}T_{view}\)

其中 \(R_{view}\)\(T_{view}\) 分别为 \(R_{modelC}^{}\)\(T_{modelC}\) 的逆矩阵。

正好由于旋转矩阵\(R\)是正交矩阵,其求逆只需直接转置矩阵,然后*移矩阵\(T\)求逆也只需将简单的将\(t_x\)\(t_y\)\(t_z\)都变一下正负号。

投影变换(Projection Transformation)


投影变换:观察空间的坐标通过投影变换得到裁剪空间(Clip Space)的坐标。投影变化则可理解成将3D空间中的坐标转换成在投影面上的坐标,即将三维空间上的东西投影在我们最终呈现画面的投影面。

为此我们需要先定义视景体:

  • *剪裁*面(Near Clip Plane):最终投影呈现的*面
  • 远剪裁*面(Far Clip Plane):视景体可见的最远*面

这样,我们认为摄像机可见范围便是这两个*面之间的空间范围,不过投影又分为透视投影正交投影两种投影方式,因此前者的空间是一个*截锥体(Frustum)而后者则是一个长方体(Cuboid):

为了方便计算成像,我们规定将可视的范围空间映射成 规范立方体(canoical cube),这个立方体实际上就是所谓的裁剪空间,且立方体长宽高范围均设定为[-1,1]。

这个投影出来的空间之所以叫裁剪空间,是因为通过规范范围,可将[-1,1]以外的坐标(这意味着不在可视范围内)很方便地裁剪掉。

正交投影(Orthographic Projection)

可以理解成*行投射(例如太阳照射,可*似看成无数条*行线投射过来)。物体无论放多远,最终在投影面看到的大小都不会变,常用于2D游戏的投影方式。

坐标映射成规范立方体里的坐标很简单,只需要先位移至规范立方体的中心点,后伸缩成[-1,1]的范围即可,因此正交投影变换的公式为:

\(M_{ortho} = \begin{pmatrix} \frac{2}{r-l}&0&0&0\\ 0&\frac{2}{t-b}&0&0\\ 0&0&\frac{2}{n-f}&0\\ 0&0&0&1\\ \end{pmatrix} \begin{pmatrix} 1&0&0&-\frac{r+l}{2}\\ 0&1&0&-\frac{t+b}{2}\\ 0&0&1&-\frac{n+f}{2}\\ 0&0&0&1\\ \end{pmatrix}\)

其中l、r为视景体在x轴上的最大值、最小值,t、b为在y轴上的最大值、最小值,f、n为在z轴上的最大最小值。

透视投影(Perspective Projection)

符合现实人眼的投影方式,典型特征就是远小*大,大部分3D游戏使用的都是透视投影方式。

将截锥体映射成规范立方体的方式则可以先把截锥体映射成长方体,再沿用正交投影变换映射成规范立方体。

截锥体映射成长方体:可以想象成把截锥体挤压成长方体,然后保持**面不发生改变,位于中心线的点位置不改变,其它的点则被挤压往中心线靠*。

我们可以根据**面离视点的距离(z轴值)和**面的高度(y轴值)或宽度(x轴值),利用相似三角形计算出对应物体映射在长方体的新x值和y值:

\(y^{'}=\frac{n}{z}y\)

\(x^{'}=\frac{n}{z}x\)

不过在齐次坐标表示中,如果w不为0,实际等价于表示 \((\frac{x}{w},\frac{y}{w},\frac{z}{w},1)\)

\( \begin{pmatrix} n&0&0&0\\ 0&n&0&0\\ 0&0&A&B\\ 0&0&1&0\\ \end{pmatrix} \begin{pmatrix} x\\ y\\ z\\ 1\\ \end{pmatrix}= \begin{pmatrix} nx\\ ny\\ zz^{'}\\ z\\ \end{pmatrix}=> \begin{pmatrix} nx/z\\ ny/z\\ z^{'}\\ 1\\ \end{pmatrix} \)

此时A、B仍然未知(因为挤压后的\(z^{'}\)是不确定的,但是**面的z和远*面的z必须和原来一致),我们可以通过解方程求得A、B,假设一个点\((x1,y1,n)\)在**面,另一个点\((x2,y2,f)\)在远*面,我们期望最终经过映射后的z轴维度分别为\(n^2\),\(f^2\),则有:\(An+B = n^2\)\(Af+B = f^2\)

从而解得:\(A=n+f\)\(B=-nf\)

因此我们可以得出以下公式:

\( M_{persp->ortho} = \begin{pmatrix} n&0&0&0\\ 0&n&0&0\\ 0&0&n+f&-nf\\ 0&0&1&0\\ \end{pmatrix} \)

\(M_{persep} = M_{ortho}M_{persp->ortho}\)

“道理我都懂,可是鸽子为什么这么大”

裁剪(Clipping)

当摄像机视野范围覆盖不了所有场景物体的时候,可以用对摄像机视野范围外的物体裁剪掉,从而减少不必要的进一步计算。

由于通过MVP变换,我们得到的顶点坐标各分量均是[-1,1]范围(即视野范围内),那么只要简单的把这个范围外的顶点剔除即可。但是仍有一些图元的部分顶点在视野范围内,另一些则在视野范围内,则需要“裁剪”掉视野外的部分,并在视野边界生成新的顶点。

与视锥体剔除不同,视锥体剔除往往是在CPU判断整个物体对象是否在摄像机视野内,要不剔除整个对象,要不什么也不剔除。而裁剪则是在GPU判断顶点是否在摄像机视野内,对某个图元允许仅裁剪一部分。

fovY、Aspect Ratio

虽然有l,r,b,t表示**面的左右上下最大值,但是在通常的应用中会使用更加直观的两个参数:
fovY(vertical field-of-view)Aspect Ratio,可理解为仰角范围和宽高比

\(tan\frac{fovY}{2}=\frac{t}{|n|}\)

\(aspect=\frac{r}{t}\)

视口变换(Viewport Transformation)


视口变换:负责把裁剪空间上的坐标(范围[-1,1],范围[-1,1])映射到屏幕坐标(范围[0,width],范围[0,height]),这就需要先定义屏幕分辨率的大小(例如:width=1200,height=1080)

裁剪空间坐标的x和y也被称为 标准化设备坐标(NDC),这是因为其值范围均为[-1,1],可以消除对屏幕纵横比的依赖,之后只需要通过视口变换便可以拉伸到合适的屏幕分辨率。

\(M_{viewport} = \begin{pmatrix} \frac{width}{2}&0&0&\frac{width}{2}\\ 0&\frac{height}{2}&0&\frac{height}{2}\\ 0&0&1&0\\ 0&0&0&1\\ \end{pmatrix}\)

总结


  1. MVP变换:
    • 模型变换(Model Transformation):模型空间坐标->世界空间坐标
    • 视图变换(View Transformation):世界空间坐标->观察空间坐标
    • 投影变换(Projection Transformation):观察空间坐标->裁剪空间坐标
  2. 视口变换(Viewport Transformation):裁剪空间坐标->屏幕空间坐标

\(V^{'} = M_{viewport}M_{persepctive}M_{view}M_{model} V\)

有了以上变换之后,我们可以任意将一个3D模型坐标映射在最终呈现画面的屏幕上,即得到屏幕空间坐标。

参考


posted @ 2021-06-15 13:07  KillerAery  阅读(2472)  评论(0)    收藏  举报