深度探讨透视投坐标系.docx

上传人:b****6 文档编号:7617874 上传时间:2023-01-25 格式:DOCX 页数:7 大小:62.40KB
下载 相关 举报
深度探讨透视投坐标系.docx_第1页
第1页 / 共7页
深度探讨透视投坐标系.docx_第2页
第2页 / 共7页
深度探讨透视投坐标系.docx_第3页
第3页 / 共7页
深度探讨透视投坐标系.docx_第4页
第4页 / 共7页
深度探讨透视投坐标系.docx_第5页
第5页 / 共7页
点击查看更多>>
下载资源
资源描述

深度探讨透视投坐标系.docx

《深度探讨透视投坐标系.docx》由会员分享,可在线阅读,更多相关《深度探讨透视投坐标系.docx(7页珍藏版)》请在冰豆网上搜索。

深度探讨透视投坐标系.docx

深度探讨透视投坐标系

深度探讨透视投坐标系

 

 

————————————————————————————————作者:

————————————————————————————————日期:

 

3d图形程序,就一定会做坐标变换。

而谈到坐标变换,就不得不提起投影变换,因为它是所有变换中最不容易弄懂的。

但有趣的是,各种关于透视变换的文档却依然是简之又简,甚至还有前后矛盾的地方。

看来如此这般光景,想要弄清楚它,非得自己动手不可了。

所以在下面的文章里,作者尝试推导一遍这个难缠的透视变换,然后把它套用到DX和PS2lib的实例中去。

1.一般概念

所谓透视投影变换,就是view空间到project空间的带透视性质的坐标变换步骤(这两

个空间的定义可以参考其他文档和书籍)。

我们首先来考虑它应该具有那些变换性质。

很显然,它至少要保证我们在view空间中所有处于可视范围内的点通过变换之后,统统落在project空间的可视区域内。

好极了,我们就从这里着手——先来看看两个空间的可视区域。

由于是透视变换,view空间中的可见范围既是常说的视平截体(viewfrustum)。

如图,

(图1)

它就是由前后两个截面截成的这个棱台。

从view空间的x正半轴看过去是下图这个样子。

(图2)

接下来是project空间的可视范围。

这个空间应当是处于你所见到的屏幕上。

实际上将屏幕表面视作project空间的xoy平面,再加一条垂直屏幕向里(或向外)的z轴(这取决于你的坐标系是左手系还是右手系),这样就构成了我们想要的坐标系。

好了,现在我们可以用视口(viewport)的大小来描述这个可视范围了。

比如说全屏幕640*480的分辨率,原点在屏幕中心,那我们得到的可视区域为一个长方体,它如下图(a)所示。

(图3)

但是,这样会带来一些设备相关性而分散我们的注意力,所以不妨先向DirectX文档学学,将project空间的可视范围定义为x∈[-1,1],y∈[-1,1],z∈[0,1]的一个立方体(上图b)。

这实际上可看作一个中间坐标系,从这个坐标系到上面我们由视口得出的坐标系,只需要对三个轴向做一些放缩和平移操作即可。

另外,这个project坐标系对clip操作来说,也是比较方便的。

2.推导过程

先从project空间的x正半轴看看我们的变换目标。

(图4)

这个区域的上下边界为y’=±1,而图2中的上下边界为y=±z*tan(fov/2),要实现图

2到图4的变换,我们有y’=y*cot(fov/2)/z。

这下完了,这是一个非线性变换,怎么用矩阵计算来完成呢?

还好我们有w这个分量。

注意到我们在做投影变换之前所进行的两次坐标变换——world变换和view变换,他们只是一系列旋转平移和缩放变换的叠加。

仔细观察这些变换矩阵,你会发现它们其实不会影响向量的w分量。

换句话说,只要不是故意,一个w分量等于1的向量,再来到投影变换之前他的w分量仍旧等于1。

好的,接下来我们让w’=w*z,新的w就记录下了view空间中的z值。

同时在y分量上我们退而求其次,只要做到y’=y*cot(fov/2)。

那么,在做完线性变换之后,我们再用向量的y除以w,就得到了我们想要的最终的y值。

x分量的变换可以如法炮制,只是fov要换一换。

事实上,很多用以生成投影变换矩阵的函数都使用了aspect这个参数。

这个参数给出了视平截体截面的纵横比(这个比值应与viewport的纵横比相等,否则变换结果会失真)。

如果我们按照惯例,定义aspect=sizeofX/sizeofY。

那么我们就可以继续使用同一个fov而给出x分量的变换规则:

x’=x*cot(fov/2)/aspect。

现在只剩下z分量了。

我们所渴望的变换应将z=Znear变换到z=0,将z=Zfar变换到z=1。

这个很简单,但是等等,x,y最后还要除以w,你z怎能例外。

既然也要除,那么z=Zfar就不能映射到z=1了。

唔,先映射到z=Zfar试试。

于是,有z’=Zfar*(z-Znear)/(Zfar–Znear)。

接下来,看看z’/z的性质。

令f(z)=z’/z=Zfar*(z-Znear)/(z*(Zfar–Znear))。

则f’(z)=Zfar*Znear/(z^2*(Zfar–Znear)),显而易见f’(z)>0。

所以除了z=0是一个奇点,函数f(z)是一个单调增的函数。

因此,当Znear≤z≤Zfar时,f(Znear)≤f(z)≤f(Zfar),

即0≤f(z)≤1。

至此,我们可以给出投影变换的表达式了。

x’=x*cot(fov/2)/aspect

y’=y*cot(fov/2)

z’=z*Zfar/(Zfar–Znear)–Zfar*Znear/(Zfar–Znear)

w’=z

以矩阵表示,则得到变换矩阵如下,

cot(fov/2)/aspect000

0cot(fov/2)00

00Zfar/(Zfar-Znear)1

00-Zfar*Znear/(Zfar-Znear)0。

做完线性变换之后,再进行所谓的“归一化”,即用w分量去除结果向量。

  现在我们考虑一下这个变换对全view空间的点的作用。

首先是x和y分量,明了地,当z>0时,一切都如我们所愿;当z<0时,x和y的符号在变换前后发生了变化,从图象上来说,view空间中处于camera后面的图形经过变换之后上下颠倒,左右交换;当z=0时,我们得到的结果是无穷大。

这个结果在实际中是没有意义的,以后我们得想办法弄掉它。

再来看z,

仍旧拿我们上面定义的f(z)函数来看,我们已经知道当z≥Zfar时,f(z)≥1;同时当z→+∞,f(z)→Zfar/(Zfar-Znear);当z→+0时,f(z)→-∞;z→-0时,f(z)→+∞;z→∞时,f(z)→Zfar/(Zfar-Znear).由此我们画出f(z)的图像。

(图5)

由此图可以看出当z≤0时,如果我们仍旧使用f(z)进行绘制会产生错误。

所以我们会想需要clip操作——只要这个三角形有任意一个顶点经过变换后z值落在[Zfar/(Zfar-Znear),+∞]区间中,我们就毫不怜悯地抛弃她——因为无论如何,这个结果是错的。

那么万一有三角形在view空间内横跨了Znear到0的范围,按我们想应该是画不出来了。

但是回想一下我们所看见过的DirectX程序,似乎从未看到过这种情况。

有点奇怪,但是不得不先放放,稍后再说。

3.到DirectX中求证

在DirectX中拿一个用fov生成投影矩阵的函数来看。

D3DXMATRIX*D3DXMatrixPerspectiveFovLH(D3DXMATRIX*pOut,FLOATfovy,FLOATAspect,

FLOATzn,FLOATzf)

这个函数恰好使用了我们刚才推导所使用的几个参数,经过一些数据的代入计算之后,我们就会发现它所产生的矩阵就是我们计算出来的。

看来,DirectX的思路和我们是一致的。

好的,一个问题解决了,但一个新的问题接着产生——DirectX是怎么做clip的?

我不知道,而且看样子现在也知道不了,只能期待牛人相助或者是碰到一本好书了。

4.研究ps2lib的投影变换

  其实投影变换都是一回事,但是PS2lib的函数怎么有点不一样呢?

仔细看看,原来我们的思路是先做“归一化”,然后再做viewport的放缩和平移,而PS2不是这样——它把“归一化”放在最后。

接下来,我们就按这个顺序试试。

先看缩放操作,把它和除z交换顺序很方便,直接换便是了。

于是我们记viewport的宽度为Vw,高度为Vh,Z缓存的最大值为Zmax,最小值为Zmin则有

x’=x*cot(fov/2)/aspect*(Vw/2)

y’=y*cot(fov/2)*(Vh/2)

z’=Zfar(z-Znear)/(Zfar-Znear)*(Zmax-Zmin);

w’=z

再看平移部分,既然是要平移后再除,则必须平移原来的z倍,于是我们又记viewport中心坐标为(Cx,Cy),就有

x’’=x’+z*Cx

y’’=y’+z*Cy

z’’=z’+z*Zmin

w’’=w

好的,我们看看cot(fov/2)等于什么,从图2看,实际上它就是D/(Vh/2),那么cot(fov/2)/aspect实际上就是D/(Vw/2)。

但是,ps2在这上面耍了个小花招,它在view空间中的viewport和project空间的viewport可以不相等。

最明显的一点是,它在view空间中的viewport的高度为480,但实际上它的输出的y向分辨率只有224。

也就是说,ps2想要输出纵横比等于电视机的图像,就必须在y向上再加一个缩放。

这个缩放在我们的变换中体现在哪呢?

就在y’=D/(Vh/2)*(Vhscr/2)中,注意到两个Vh不相等(project空间中的Vh记成Vhscr),两个值一运算就得到x’=D*(224/480)=0.466667D。

这个0.4666667就是ps2lib函数参数ay的由来。

同理,我们亦可得知ax一般应取值为1。

那么,实际上ps2lib函数的scrz,ax,ay三个参数的作用等同于DirectX的象形函数的fov和aspect,在确定的规则下,他们可以相互转换,得到性质完全相同的透视变换。

至于这个规则,这里就不给出了。

转回正题,有了上面的讨论,我们就可以展开我们的变换表达式如下,

x’’=x*scrz*ax+z*Cx

y’’=x*scrz*ay+z*Cy

z’’=z*(Zfar*Zmax–Znear*Zmin)/(Zfar–Znear)

–Zfar*Znear*(Zmax-Zmin)/(Zfar-Znear)

w’’=z

z分量好像还有点不一样,注意到一般ps2程序在zbuffer的操作为greater&equal,而DirectX的操作为less&equal,就是说,z方向得做些变动——得把z=Znear映射到z’’=Zmax,z=Zfar映射到z’’=Zmin。

说变就变,我们马上有

z’=Zfar(z-Znear)/(Zfar-Znear)*(Zmin-Zmax)

z’’=z’+Zmax

再次展开,得到z’’=z*(Zfar*Zmin–Znear*Zmax)/(Zfar–Znear)

+Zfar*Znear*(Zmax-Zmin)/(Zfar-Znear)

好了,用矩阵把这个变换写出来,

scrz*ax000

0scrz*ay00

CxCy(Zfar*Zmin–Znear*Zmax)/(Zfar–Znear)1

00Zfar*Znear*(Zmax-Zmin)/(Zfar-Znear)0,

这下就完全一样了。

下面的任务就是看看这个变换的性质。

因为最后同样要除以z,所以x,y分量上的情形的和原来我们推导的DirectX的投影变换是一样的,区别在z分量上。

来看新的f(z)函数,它的图像为

(图6)

5.结论

至此,我们已经完成了预定的目标。

但是,将坐标变换完全掌握之后,为了做一个像样的图形程序,我们还有更多事情要做——至少在PS2上是这样。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1