透视矩阵的推导(最直观、最深入、最还原,看完请点赞。)
2015-07-23 17:29
239 查看
由参数l,r,b,t,n,f定义的透视投影矩阵的推导困惑了我差不多一个多礼拜,这几天几乎是天天都在思考这个问题,昨天晚上3点多钟我突然醒了,然后我又开始想这个问题,结果终于让我给想通了,于是我赶紧起床把这个思路记在了草稿纸上,还专门照了张照片作证。
为了解决这个问题,前几天我专门发了两篇帖子求答案,结果知网上的竟然沉掉了。而在csdn上也没有得到答案(质疑有关透视投影矩阵的推导)。幸亏我自己还是解决了这个问题。
下面推导的是OpenGL中的透视投影矩阵。
已经知道由参数fovy,aspect,n,f定义的透视投影矩阵为:(有关这块的推导可见《3d graphics for game programming》 2.4.3 derivation of projection matrix,讲得非常详细)
POpenGL=
P_{OpenGL} =
⎛⎝⎜⎜⎜⎜⎜⎜⎜cot(fovy2)aspect0000cot(fovy2)0000−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟⎟
\begin{pmatrix}
\frac{cot(\frac{fovy}{2})}{aspect} & 0 & 0 & 0 \\
0 & cot(\frac{fovy}{2}) & 0 & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
那么再看另一种投影参数l,r,b,t,n,f,上面矩阵中:
cot(fovy2)aspect=cot(fovx2)=2nr−l,
\frac{cot(\frac{fovy}{2})}{aspect} = cot(\frac{fovx}{2}) = \frac{2n}{r-l},
cot(fovy2)=2nt−b.
cot(\frac{fovy}{2}) = \frac{2n}{t-b}.
因为参数l,r是对应视见空间中x轴的坐标,b,t对应视见空间中y轴的坐标,如果l=-r并且b=-t,那么由这种参数定义的透视投影矩阵就是:
P′OpenGL=
P’_{OpenGL} =
⎛⎝⎜⎜⎜⎜⎜2nr−l00002nt−b0000−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟
\begin{pmatrix}
\frac{2n}{r-l} & 0 & 0 & 0 \\
0 & \frac{2n}{t-b} & 0 & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
而官方的透视投影矩阵是:
POpenGL=
P_{OpenGL} =
⎛⎝⎜⎜⎜⎜⎜⎜2nr−l00002nt−b00r+lr−lt+bt−b−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟
\begin{pmatrix}
\frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\
0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
实际上这个矩阵是由两步转换完成的,第一步是进行矩阵P′OpenGLP'_{OpenGL}变换,然后进行了平移操作,如下:
POpenGL=T×P′OpenGL=⎛⎝⎜⎜⎜⎜⎜⎜⎜100001000010−r+lr−l−t+bt−b01⎞⎠⎟⎟⎟⎟⎟⎟⎟×⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜2nr−l00002nt−b0000−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜2nr−l00002nt−b00r+lr−lt+bt−b−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟\begin{align}
P_{OpenGL} & = T×P'_{OpenGL} \\
& = \begin{pmatrix}
1 & 0 & 0 & -\frac{r+l}{r-l} \\
0 & 1 & 0 & -\frac{t+b}{t-b} \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1 \\
\end{pmatrix} ×
\begin{pmatrix}
\frac{2n}{r-l} & 0 & 0 & 0 \\
0 & \frac{2n}{t-b} & 0 & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix} \\
& = \begin{pmatrix}
\frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\
0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
\end{align}
那些说这个透视投影没有做平移这一步操作,我可以100%跟你说你是错的!
下面我来解释为什么进行了平移操作。
来张示意图:
这个是视见空间中的示意图,因为存在l≠-r或b≠-t的情况,所以这里我故意没把近裁剪平面中心画在z轴上。示意图上由红线绘制的立方体才是由l,r,b,t,n,f参数定义的视锥体,其中远裁剪平面上的四个点分别对应原点eye经过近裁剪平面上四个点的延长线与z=-f平面的交点,注意这个视锥体是不规则的!然后图中的标注l’, r’是近裁剪平面点(l,b,-n)和点(r,b,-n)x轴上的分量在z=−cot(fovx2)z=-cot(\frac{fovx}{2})上的投影,l′=2lr−ll'=\frac{2l}{r-l},r′=2rr−lr'=\frac{2r}{r-l},l’和r’的中心值是r+lr−l\frac{r+l}{r-l},同理近裁剪平面上点(r,b,-n)和点(r,t,-n)y轴上的分量在z=−cot(fovy2)z=-cot(\frac{fovy}{2})上的投影,b′=2bt−bb'=\frac{2b}{t-b},t′=2tt−bt'=\frac{2t}{t-b},b’和t’的中心值是t+bt−b\frac{t+b}{t-b}。图中的视锥体通过P′OpenGLP'_{OpenGL}透视变换之后得到的是规则的正方体(左下角顶点(2lr−l,2bt−b,1)(\frac{2l}{r-l},\frac{2b}{t-b},1),右上角顶点(2rr−l,2tt−b,−1))(\frac{2r}{r-l},\frac{2t}{t-b},-1)),最终要把它转换成cvv(canonical view volume,正规可视化空间),则需要进行T平移转换,也就是平移(−r+lr−l,−t+bt−b,0)(-\frac{r+l}{r-l}, -\frac{t+b}{t-b}, 0)。得证。
为了解决这个问题,前几天我专门发了两篇帖子求答案,结果知网上的竟然沉掉了。而在csdn上也没有得到答案(质疑有关透视投影矩阵的推导)。幸亏我自己还是解决了这个问题。
下面推导的是OpenGL中的透视投影矩阵。
已经知道由参数fovy,aspect,n,f定义的透视投影矩阵为:(有关这块的推导可见《3d graphics for game programming》 2.4.3 derivation of projection matrix,讲得非常详细)
POpenGL=
P_{OpenGL} =
⎛⎝⎜⎜⎜⎜⎜⎜⎜cot(fovy2)aspect0000cot(fovy2)0000−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟⎟
\begin{pmatrix}
\frac{cot(\frac{fovy}{2})}{aspect} & 0 & 0 & 0 \\
0 & cot(\frac{fovy}{2}) & 0 & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
那么再看另一种投影参数l,r,b,t,n,f,上面矩阵中:
cot(fovy2)aspect=cot(fovx2)=2nr−l,
\frac{cot(\frac{fovy}{2})}{aspect} = cot(\frac{fovx}{2}) = \frac{2n}{r-l},
cot(fovy2)=2nt−b.
cot(\frac{fovy}{2}) = \frac{2n}{t-b}.
因为参数l,r是对应视见空间中x轴的坐标,b,t对应视见空间中y轴的坐标,如果l=-r并且b=-t,那么由这种参数定义的透视投影矩阵就是:
P′OpenGL=
P’_{OpenGL} =
⎛⎝⎜⎜⎜⎜⎜2nr−l00002nt−b0000−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟
\begin{pmatrix}
\frac{2n}{r-l} & 0 & 0 & 0 \\
0 & \frac{2n}{t-b} & 0 & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
而官方的透视投影矩阵是:
POpenGL=
P_{OpenGL} =
⎛⎝⎜⎜⎜⎜⎜⎜2nr−l00002nt−b00r+lr−lt+bt−b−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟
\begin{pmatrix}
\frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\
0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
实际上这个矩阵是由两步转换完成的,第一步是进行矩阵P′OpenGLP'_{OpenGL}变换,然后进行了平移操作,如下:
POpenGL=T×P′OpenGL=⎛⎝⎜⎜⎜⎜⎜⎜⎜100001000010−r+lr−l−t+bt−b01⎞⎠⎟⎟⎟⎟⎟⎟⎟×⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜2nr−l00002nt−b0000−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜2nr−l00002nt−b00r+lr−lt+bt−b−f+nf−n−100−2nff−n0⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟\begin{align}
P_{OpenGL} & = T×P'_{OpenGL} \\
& = \begin{pmatrix}
1 & 0 & 0 & -\frac{r+l}{r-l} \\
0 & 1 & 0 & -\frac{t+b}{t-b} \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1 \\
\end{pmatrix} ×
\begin{pmatrix}
\frac{2n}{r-l} & 0 & 0 & 0 \\
0 & \frac{2n}{t-b} & 0 & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix} \\
& = \begin{pmatrix}
\frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\
0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\
0 & 0 & -1 & 0 \\
\end{pmatrix}
\end{align}
那些说这个透视投影没有做平移这一步操作,我可以100%跟你说你是错的!
下面我来解释为什么进行了平移操作。
来张示意图:
这个是视见空间中的示意图,因为存在l≠-r或b≠-t的情况,所以这里我故意没把近裁剪平面中心画在z轴上。示意图上由红线绘制的立方体才是由l,r,b,t,n,f参数定义的视锥体,其中远裁剪平面上的四个点分别对应原点eye经过近裁剪平面上四个点的延长线与z=-f平面的交点,注意这个视锥体是不规则的!然后图中的标注l’, r’是近裁剪平面点(l,b,-n)和点(r,b,-n)x轴上的分量在z=−cot(fovx2)z=-cot(\frac{fovx}{2})上的投影,l′=2lr−ll'=\frac{2l}{r-l},r′=2rr−lr'=\frac{2r}{r-l},l’和r’的中心值是r+lr−l\frac{r+l}{r-l},同理近裁剪平面上点(r,b,-n)和点(r,t,-n)y轴上的分量在z=−cot(fovy2)z=-cot(\frac{fovy}{2})上的投影,b′=2bt−bb'=\frac{2b}{t-b},t′=2tt−bt'=\frac{2t}{t-b},b’和t’的中心值是t+bt−b\frac{t+b}{t-b}。图中的视锥体通过P′OpenGLP'_{OpenGL}透视变换之后得到的是规则的正方体(左下角顶点(2lr−l,2bt−b,1)(\frac{2l}{r-l},\frac{2b}{t-b},1),右上角顶点(2rr−l,2tt−b,−1))(\frac{2r}{r-l},\frac{2t}{t-b},-1)),最终要把它转换成cvv(canonical view volume,正规可视化空间),则需要进行T平移转换,也就是平移(−r+lr−l,−t+bt−b,0)(-\frac{r+l}{r-l}, -\frac{t+b}{t-b}, 0)。得证。
相关文章推荐
- Spring task quartz 定时任务的几种实现
- 各种排序算法的实现,总结与比较
- Dialog自定义布局
- unity3d打包和包的使用
- mac 免密码登陆服务器
- NSString什么时候用copy,什么时候用strong
- 分布式锁实现
- 学习文章连载一
- 树莓派实现用pi用户自动登录
- windows下nginx安装、配置与使用
- Android的API版本和名称对应关系
- UNITY3D学习笔记4
- qml:1807: Error: Insufficient arguments
- Spring MVC下跳转问题的总结
- 在android使用lambda表达式
- maven学习之一本地(windows)安装
- SecureCRT超级终端使用说明
- HDU2032
- [Java]初识AtomicInteger
- javascript如何定义全局变量