投影变换完成的是如何将三维模型显示到二维视口上,这是一个三维到二维的过程。你可以将投影变换看作是调整照相机的焦距,它模拟了为照相机选择镜头的过程。投影变换是所有变换中最复杂的一个。
[视锥体]
视锥体是一个三维体,他的位置和摄像机相关,视锥体的形状决定了模型如何从camera space投影到屏幕上。最常见的投影类型-透视投影,使得离摄像机近的物体投影后较大,而离摄像机较远的物体投影后较小。透视投影使用棱锥作为视锥体,摄像机位于棱锥的椎顶。该棱锥被前后两个平面截断,形成一个棱台,叫做View Frustum,只有位于Frustum内部的模型才是可见的。
[透视投影的目的]
透视投影的目的就是将上面的棱台转换为一个立方体(cuboid),转换后,棱台的前剪裁平面的右上角点变为立方体的前平面的中心(下图中弧线所示)。由图可知,这个变换的过程是将棱台较小的部分放大,较大的部分缩小,以形成最终的立方体。这就是投影变换会产生近大远小的效果的原因。变换后的x坐标范围是[-1, 1],y坐标范围是[-1, 1],z坐标范围是[0, 1](OpenGL略有不同,z值范围是[-1, 1])。
[透视投影矩阵推导]
下面来推导一下透视投影矩阵,这样我们就可以自己设置投影矩阵了,就可以模拟神奇的D3DXMatrixPerspectiveLH函数的功能了。那么透视投影到底做了什么工作呢?这一部分算是个难点,无论是DX SDK的帮助文档,还是大多数图形学书籍,对此都是一带而过,很少有详细讨论的,早期的DX SDK文档还讨论的稍微多一些,而新近的文档则完全取消了投影矩阵的推导过程。
我们可以将整个投影过程分为两个部分,第一部分是从Frustum内一点投影到近剪裁平面的过程,第二部分是由近剪裁平面缩放的过程。假设Frustum内一点P(x,y,z)在近剪裁平面上的投影是P'(x',y',z'),而P'经过缩放后的最终坐标设为P''(x",y",z")。假设所求的投影矩阵为M,那么根据矩阵乘法可知,如下等式成立。
PM=P'',即
先看第一部分,为了简化问题,我们考虑YOZ平面上的投影情况,见下图。设P(x, y, z)是Frustum内一点,它在近剪裁平面上的投影是P'(x', y', z')。(注意:D3D以近剪裁平面作为投影平面),设视锥体在Y方向的夹角为Θ。
由上图可知,三角形OP'Q'与三角形OPQ相似,于是有如下等式成立。
在看第二部分,将P'缩放的过程,假设投影平面的高度为H,由于转换后cuboid的高度为2。所以有
又因为投影平面的纵横比为Aspect,所以
最后看z'',当Frustum内的点投影到近剪裁平面的时候,实际上这个z'值已经没有意义了,因为所有位于近剪裁平面上的点,其z'值都是n,看起来我们甚至可以抛弃这个z'值,可以么?当然不行!别忘了后面还有深度测试呢。由第一幅图可知,所有位于线段p'p上的点,最终都会投影到p'点,那么如果这条线段上真的有多个点,如何确定最终保留哪一个呢?当然是离观察这最近的这个了,也就是深度值(z值)最小的。所以z'坐标可以直接保存p点的z值。因为在光栅化之前,我们需要对z坐标的倒数进行插值(原因请参见Mathematics
for 3D Game Programming and Computer Grahpics 3rd section 5.4),所以可以将z''写成z的一次表达式形式,如下
在映射前,z的范围是[n,f],这里n和f分别是近远两个剪裁平面到原点的距离,在映射后,z''的范围是[0,1],将数据代入上面的一次式,可得下面的方程组
解这个方程组得到
所以
整理一下得
将X'',y'',z''代入最开始的矩阵乘法等式中得
由上式可见,x'',y'',z''都除以了Pz,于是我们将他们再乘以Pz(这并不该变齐次坐标的大小),得到如下等式。
注意这里,x即Px,y即Py,z即Pz,解矩阵的每一列得到
于是所求矩阵为
代码
一般来说,在程序中我们通常给定四个参数来求透视投影矩阵,分别是y方向的视角,纵横比,近剪裁平面到原点的距离及远剪裁平面到原点的距离,通过这四个参数即可求出上面的矩阵,代码如下。
D3DXMATRIX BuildProjectionMatrix(float fov, float aspect, float zn, float zf)
{
D3DXMATRIX proj;
ZeroMemory(&proj, sizeof(proj));
proj.m[0][0] = 1 / (tan(fov * 0.5f) *aspect) ;
proj.m[1][1] = 1 / tan(fov * 0.5f) ;
proj.m[2][2] = zf / (zf - zn) ;
proj.m[2][3] = 1.0f;
proj.m[3][2] = (zn * zf) / (zn - zf);
return proj ;
}
矩阵求解完毕,现在可以用如下代码试试效果,这和使用D3D函数D3DXMatrixPerspectiveFovLH所得效果是一致的。
D3DXMATRIX proj = BuildProjectionMatrix(D3DX_PI / 4, 1.0f, 1.0f, 1000);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &proj) ;
Happy Coding!!!
相关推荐
1.在世界坐标系中定义一个立方体(由6个面组成),并给定观察点在世界坐标系中的位置(a,b,c)以及观察坐标系的方位角θ,俯仰角φ和姿态角α,另外再给定投影面离观察点的距离D,在屏幕上画出立方体的透视投影图形。...
c语言,利用opengl中用自带函数实现平行,透视投影
图形学实验-立方体线框模型透视投影图形学实验-立方体线框模型透视投影图形学实验-立方体线框模型透视投影
3. 投影平面设为n=0,视点为(0,0,-d)(用户坐标系),编写程序实现物体的一点透视投影,并画出在uov面上的1点透视图。 4. 投影平面为与x轴、z轴有交,视点为(x,0,d)(用户坐标系) ,编写程序实现物体的二点透视投影,...
1.在世界坐标系中定义一个立方体(由6个面组成),并给定观察点在世界坐标系中的位置(a,b,c)以及观察坐标系的方位角θ,俯仰角φ和姿态角α,另外再给定投影面离观察点的距离D,在屏幕上画出立方体的透视投影图形。...
球面透视投影模型校正鱼眼图像畸变
(1) 在屏幕中心建立三维坐标系Oxyz,x轴水平向右,y轴铅直向上,z轴 垂直于屏幕指向观察者 (2) 建立以三维...的顶点坐标 (4) 屏幕上绘制立方体线框模型的二维正交投影图 (5) 屏幕上绘制立方体线框模型的二维透视投影图
PointCloud透视投影,OpenGL实现读取点云数据并显示,透视投影。
计算机图形学实验 立方体线框模型透视投影 的可执行文件,亲测可运行,若需报告可以联系我,期待和各位交流
透视投影例子程序
C++实现的三维透视投影,一个立方体,可用鼠标拖动来移动或旋转,包含源代码和可执行文件,VS2008的工程。
齐次坐标概念&&透视投影变换推导 透视投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(frustum)变换到规则观察体(Canonical View Volume)中,待裁剪完毕后进行透视除法的行为。在算法中它是通过透视...
3D透视投影
WebGL/ThreeJS正投影和透视投影相机对比demo
正交投影与透视投影的源码; 该源码是 OpenGL 示例源码集合. OpenGL 需要依赖 v7 包
MFC图形学透视投影,图形是房子,包括一点透视,二点透视,三点透视打开后需要rebuild
深入探讨透视投影坐标变换,directx坐标变换,游戏开发基础知识
透视投影是一种中心投影法,在日常生活中,我们观察外界的景物时,常会看到一些明显的透视现象。
【OpenGL】正交投影和透视投影矩阵(二) —— 结合OpenGL代码验证
利用matlab实现图形变换,图像旋转,透视投影和平行投影