Perspective Matrix的另一种推导

本文通过线性代数方法详细推导OpenGL投影矩阵,解释第三行系数的选择原因,并介绍了射影矩阵的概念及其在图形学中的应用。

OpenGL投影矩阵的常见推导在OpenGL Projection Matrix中已经详细地给出了,但是作者似乎没有给出第三行的系数的由来。这里我再给出一种使用线性代数方法的推导,从中读者可以明白为第三行系数是那样选取的。

这里的推导参考了《Computer Graphics - Principle and Practice》3rd的第13章(在扩展材料中给出了详细的推导,不过需要注意的是,扩展材料中的推导是从standard view volume到canonical view volume,与OpenGL或DX中的投影矩阵稍有不同。),以及《Multi View Geometry》中p34的一个例子。

投影矩阵是一种射影矩阵(Projective Matrix),射影矩阵根据《Multi View Geometry》[1]中的定义,是一个可逆矩阵,并且在变换过程中保留共线性质,而OpenGL的投影矩阵也是一种射影矩阵,我们可以通过线性代数的方法来求得对应的投影变换。

对于P3中的射影变换对应的矩阵,总共有16个元素,由于P3中的点是齐次坐标,所以总共有15个自由度(即15个变量),所以我们只需要15个方程就能够求得对应的矩阵。这里是15个自由度而不是16的原因是,对于点p和q,我们称它们是等价的,当q=kp(k != 0)。于是对于任意射影变换M,有

等价变换

也就是说M的任意倍数与M也是等价的。

我们只需要选取5个对应的点,就可以得到15个方程,因为每个R3中的点有3个分量。这里选取对应点时需要注意,需要使得他们在general position,也就是任意4个点不能是共面的,或者说,点P不能表示为其他3个点仿射组合,否则我们在求解矩阵M时不能得到唯一的解(这里的唯一解是关于某个倍数的,因为有16个未知数,而我们只有15个方程,显然对应的线性方程组系数矩阵不是满秩的,于是不能有一般意义上的唯一解,但是因为只有一个自由变量,其他基本变量都是这个自由变量的倍数,所以我们依然可以得到一个唯一的射影矩阵)。

在frustum中选取如下5个点以及变化后在cvv上的对应点(这里是R3中的点,对应的P3中的点可以通过添加分量w=1得到):

Corresponding points

这里n、f分别是near和far平面的距离,b为aspect ratio,a为tan(fov/2)。

假设射影矩阵为

projective matrix

对于P3中任意点p=(x,y,z,1),经过M变换后的点为q’(x1,y1,z1,w1),我们可以得到q’对应的R3中的点q = q’/w1,所以有

transformation

equation

根据前面找到的5个对应点,我们可以得到15个形似上面的方程,最终可以求得矩阵M为(这里我使用Maple进行求解,相关的计算代码,见maple):

result

这里的矩阵是 a 32 a_{32} a32的倍数,我们需要选取 a 32 = − 1 a_{32}=-1 a32=1得到与OpenGL中一致的结果。如果选取 a 32 = 1 a_{32}=1 a32=1,会造成错误的裁剪(比如一个三角面,三个顶点的 w w w都小于0,此时就不会显示),因为OpenGL裁剪时要求

− w ≤ x ≤ w − w ≤ y ≤ w − w ≤ z ≤ w -w \le x \le w \\ -w \le y \le w \\ -w \le z \le w \\ wxwwywwzw

选取 a 32 = 1 a_{32}=1 a32=1会使得 w &lt; 0 w&lt;0 w<0,导致无法满足上述不等式,虽然我们知道相差一个符号的两个齐次坐标代表的是同一个点。一个点在变换后 w w w会小于0的例子是进行平面投影(用于Projection Shadow)且投影矩阵射影矩阵的形式。

这种方法的好处在于,我们可以方便地推导出斜透视矩阵等其他非标准的透视矩阵。

[1] 本书中对于齐次坐标,射影空间等概念的描述比较简单,推荐阅读。

### 仿射变换与单应变换的区别与联系 #### 定义比较 仿射变换是一种保持共线性和比例关系的几何变换,适用于二维空间中的旋转、缩放、剪切和平移操作[^1]。而单应变换(Homography),也称为透视变换或投影变换,在三维空间中描述了一个平面到一个平面之间的映射关系[^4]。 两者的主要区别在于适用场景的不同: - **仿射变换**通常用于近似平行于相机传感器的目标物体建模,假设不存在显著的视角变化。 - **单应变换**则可以处理更加复杂的情况,比如当目标表面倾斜或者存在明显透视效应时。 #### 数学表达形式对比 ##### 仿射变换矩阵 对于任意一点 $(x, y)$ 经过仿射变换后的坐标可表示为: $$ \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} t_x \\ t_y \end{bmatrix}, $$ 其中 $[a,b,c,d]$ 表示线性部分,$[t_x,t_y]^T$ 则代表平移向量[^3]。 为了简化计算并统一各种基本变换的操作符定义方式,引入齐次坐标的概念后得到如下扩展版本: $$ \begin{bmatrix} x' \\ y' \\ w \end{bmatrix} = \begin{bmatrix} m_{00} & m_{01} & t_x \\ m_{10} & m_{11} & t_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}. $$ 最终实际使用的像素位置需除以其权重因子 w 来标准化结果: $$ (x', y') = (\frac{x'}{w}, \frac{y'}{w}), $$ 这里一般情况下 w 应该等于 1. ##### 单应变换矩阵 (HomoMatrix) 相比之下,单应变换涉及到了更多自由度参数,并且能够很好地应对由于摄像机视场角引起的变形效果等问题。其通用方程式形如下面这样: $$ \lambda \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}= \begin{bmatrix} h_{11}& h_{12}& h_{13}\\ h_{21}& h_{22}& h_{23}\\ h_{31}& h_{32}& h_{33} \end{bmatrix} \begin{bmatrix} X_w\\ Y_w \\ Z_w \end{bmatrix}, $$ 其中 $\lambda$ 是尺度因子,$(u,v)^T$ 对应的是图像上的点位,而 $(X_w,Y_w,Z_w)^T$ 描述世界坐标系下的对应实体所在之处的位置矢量[^4]. 值得注意的一点就是说如果 z 轴方向的距离趋于无穷大,则此时 H 就退化成为了前面提到过的那种标准型式的 Affine Transformation Matrix 形态了。 --- ### 放射矩阵推导过程 放射矩阵实际上指的是上面所提及的 Homography 或者 Perspective Transform 的具体实现细节。以下是基于两组已知对应点对之间建立模型的一个典型算法流程概述: 给定 n>=4 不共面也不全重合的特征匹配点集合 P={p_i,q_i|i=1..n} ,我们希望求解满足条件 q~i~=HP_i 的未知变量 H=[h_ij], i,j ∈ {1,...9}. 利用最小二乘法原理构建误差函数 E(H)=Σ||q'_i - HP_i ||² , 并通过优化手段寻找最优解使得整体偏差达到全局极小值状态即可完成整个任务链路闭环设计思路阐述完毕[^5]. 下面是 Python 实现代码片段展示如何借助 NumPy 工具库快速搭建原型环境测试验证理论正确性的可能性路径之一: ```python import numpy as np def compute_homography(src_points, dst_points): """ Computes homography matrix from source to destination points. Args: src_points: A Nx2 array of N source points (x,y). dst_points: A Nx2 array of N destination points (x,y). Returns: The computed 3x3 homography matrix. """ assert len(src_points) >= 4 and len(dst_points) >= 4 num_points = len(src_points) # Constructing the linear system Ax = 0 where x represents flattened homography elements A_rows = [] for p_src, p_dst in zip(src_points, dst_points): u, v = p_dst[:]; x, y = p_src[:] row1 = [-x,-y,-1, 0, 0, 0, u*x,u*y,u ] row2 = [ 0, 0, 0,-x,-y,-1, v*x,v*y,v ] A_rows.extend([row1,row2]) A_matrix = np.array(A_rows).reshape(-1,9) _,_,V_T=np.linalg.svd(A_matrix) H_flat = V_T[-1] return H_flat.reshape((3,3)) if __name__ == "__main__": pts_src = [[78, 56],[144,102],[210,168]] pts_dst = [[114,18 ],[150,150],[186,282]] H = compute_homography(np.float32(pts_src),np.float32(pts_dst)) print("Computed Homography:\n",H / H[2][2]) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值