介绍
----
本文叙述了如何用portal(入口)技术来渲染三维多边形。并比较了几种可选的技术。portal技术主要用于室内引擎。室外引擎需要其他技术,例如voxels(三维象素)。本文后段部分还介绍了几种处理portal引擎的技巧(例如怎样比较容易的把一个portal引擎和其他的室外引擎集成起来)
问题定义
--------
本文中,我们将多边形(polygons)限制为凸多边形。大体上你可以改造portal引擎使之也能够处理非凸多边形,但是这样会使剪切处理变得复杂。如果你要渲染一个3维多边形构成的场景(scene)你就要知道这些多边形显示的顺序。例如你的算法可能会以从远到近的方式来画多边形。但是你以怎样的顺序来画呢?一般来说没有什么比较容易的方法。甚至在某些情况下,根本没有办法来将它们简单地排序。见下图:(互相覆盖的多边形――原谅我的ASCII艺术,呵呵)
这是一个在3维空间中以某个角度看到的3个3维多边形。它们互相两两覆盖,没有什么方法可以将它们以单独的顺序排列起来。这样的问题不能用portal技术来解决,你需要其他的一些技巧(例如Z缓冲等)。
入口(portal)
-----------
因此,portal不能解决一些根本的问题。portal技术需要约束我们世界中的多边形的摆放方式。这点限制了portal技术的使用范围,也是portal引擎不能渲染室外场景的主要原因。portal技术的主要优点是快速并且易于实现。
定义世界
-------
整个世界由Sector(这里我实在是想不出用什么中文词来翻译,部分?扇区?地区?)组成。一个Sector是一系列闭合的凸的多边形。凸表示如果你在sector内的任意两点间画一条线段,这条线段不会穿透Sector的任何一个多边形。闭合表示在Sector内发出一条光线,如果要让光线射到Sector外,则此光线必须要穿透一个Sector的多边形。可以形象地把Sector看成是一个房间,Sector的多边形构成了房间的墙壁、天花板、和地板等。
两个邻近的Sector通过portal连接。一个portal是一个特殊种类的多边形。普通的多边形只是利用诸如纹理映射之类的技术来渲染,而当渲染一个portal时我们会进入到另一个Sector(和这个Sector相连接的Sector)。
利用这种方法你可以刻画任何室内场景(当然在很多情况下,你要很多的portal)。如果要描述一个凹的房间,你只需要将这个房间划分成若干个凸的Sector,然后用portal将它们连接起来。见下图中的2维示意图:
渲染
----
portal技术的主要威力是易于渲染。例如在上图中,假设你在Sector A有一个照相机,再假设所有多边形的顶点(vertices)已经从世界空间正确地转化成了照相机空间(换句话说,就是照相机成为坐标的原点)。因为所有的sector是凸的,所以渲染Sector内的所有多边形没有什么问题。它们不可能互相覆盖,因此没有任何的剪切操作(当然还是有显示区域产生的剪切处理),它们可以以任何顺序来画,没有排序的必要。下面是基本的算法:
render(Sector* sector, Camera* camera, View* view)
for sector中的所有多边形 do
if 多边形不是portal then
画经过view剪切处理后的多边形
else
利用portal和当前的view产生一个新的view
这个递归的渲染算法非常简单。但这里有一点我们还没有讨论,就是在算法中我们引进了视图(view)的概念。视图规定了如何画多边形。
利用2维多边形视图进行剪切
-------------------------
第一种方式是利用2维多边形剪切。初始视图是一个屏幕尺寸大小的2维矩形。每次你进入一个人口(portal)(即每次你递归地产生一个新的view)时,你将经过透视矫正处理后的2维portal多边形(这是一个经过包括透视矫正的屏幕空间转换之后的2维多边形)和初始视图进行相交处理,他们的交(intersection)构成了新的视图。每当你画一个常规的多边形时,它首先(在2维意义上)被当前的视图剪切。这就好象是你从纸上的一个空里看纸后的世界,每当你通过一个portal,你眼前的空就更小。让我用丑陋的ASCII图画来尽量描述这种技术。假设在sector A中有一个照相机,面对一个有两个portal的墙。一个通往sector B一个通往sector C。最先,你渲染一个如下的场景。
初始视图是最外面的最大的一个和屏幕尺寸(或窗口尺寸)一致的矩形。为了清晰起见假设portal多边形是最后渲染的(这一点算法本身并没有要求),现在要渲染sector B。那么我们要将portal B和当前视图做相交运算以得到一个新的视图,得到如下的渲染结果。
现在我们渲染了sector B并到达了通往sector D的人口。我们再一次将当前视图(注意现在的当前试图是左边的第二大的矩形)和porta D做相交,得到最终如下的结果。
在现在的分支里已经没有了portal,所以递归结束,我们返回到Sector A并继续以同样的方式渲染sector C。
计算两个2维多边形的开销是很低的。在多数情况下两个多边形的相交等于其中的一个多边形(因为大多数视图比我们要画的多边形大),这点很容易判断并可以实现相应的特殊情况下的剪切处理代码。
用这种技术,你在画多边形时就可以不关心剪切处理了。初始视图等于屏幕,你知道你要画的多边形不可能超出屏幕(每个多边形在渲染前已经被view剪切过了),这种渲染方式是没有重画情况发生的。
你唯一要考虑的是在有些情况下一个多边形可能被渲染两次。在上例中,sector D的墙可能被渲染两次(一次是从sector B进入时渲染的,一次是从sector C进入时渲染的)。但是,因为多边形是被不同的视图剪切过的,两个部分不会重叠,所以也不会发生重画的结果。
利用视图平截面来进行剪切
----------------------
另一种常用的技巧是用3维的平截来剪切视图。这种方法利用一系列的3维的平面而不是用2维的多边形。使用2维的剪切技术。这种方法最大的优点是在剪切之前你不必对你的多边形进行透视矫正,但是缺点是3维剪切相对2维剪切更困难效率也更低些。
portal技术的优点
----------------
1. 快速渲染。
2. 此技术可以动态地创建世界。入口可以被打开或关闭,你可以容易地移动顶点,因此改变sector的大小(当然,要维持sector的凸性)。如果你要增加一个新的sector并将其连入已经存在的sector中并不需要太多的计算。
3. 能够比较容易地定义世界。
4. 你可以对portal做一些很不错的改进(见后文)。
5. 利用portal你能够很容易的 秩境【安⑶颐挥兄 画情况,这带来了很客观的渲染速度。
6. portal技术只处理可视多边形,因而效率非常高。没有必要使用分离的PVS集(potentially visible set),这样portal技术对非常大的世界很合适(对照相机不能直接看到的很远的部分没有额外的计算开销)
7. 其他3维效果(例如灯光、碰撞判断等)利用portal技术也比较容易实现。
portal技术的缺点
----------------
1. portal技术不是3维引擎普遍适用的技术,portal不适合室外渲染,就是室内世界也需要使用特殊的方式。
2. 必须用凸sector和连接sector的portal来定义世界,但是一个好的编辑器可以改变这点(自动地产生portal等)
其他可选技术
------------
画家算法
常规的画图算法,太慢,并不适用于实时3维引擎。
Z Buffering
这是一个较容易实现的算法。你可以用你愿意的任何次序画多边形。每次画多边形时,每个象素依照Z缓冲进行检查。如果新象素的Z值比缓冲里的Z值更近,则缓冲里相应的象素被更新。
优点:
1. 容易实现
2. 这种方法很普遍。能够处理任何情况下的3维多边形。甚至可以处理除多边形以外的几何模型(例如球、弯曲表面等)。
缺点:
1. 这种方法比较慢。除非你有特殊的Z buffering硬件加速设备。你还可以用一些特殊的Z缓冲技术(例如S-Buffering)来改进。但总得来说这是一种慢速的算法。
BSP树
-----
利用BSP树,多边形构成的世界首先被BSP产生器分析处理,把世界分成凸的子sector。为实现这点,它会把多边形分离,最终形成一个可递归遍历的树。因为你能3维空间中的任意给定点开始递归遍历整个树,因此你可以得到有后往前或由前往后的多边形集。
优点:
1. 这是一种快速的算法。一旦你建立好BSP树,你可以按你想要的顺序快速地遍历从而得到所有的多边形。然后,你可以利用画家算法由后往前画出多边形。
缺点:
1. BSP树仅仅适合静态的世界。虽然你可以往世界中添加或删除多边形,但这样你必须重新计算BSP树。利用局部BSP树可以优化这点,但总得来说,BSP更适合描述静态的世界。
2. BSP算法没有提供避免重画的机制。你必须添加跨度缓冲(span-buffering)或其他技术来做到避免重画。
3. 为了充分利用BSP树的效率,你还需要使用PVS或其他类似的技术。不然你会为过多的多边形而顾虑(尤其是在比