空间排序

                      空间排序(SPATIAL SORTING

    第四章讨论了渲染一个层次化结构的场景,场景中的物体绘制的顺序是由深度优先遍历所决定的,几乎所有的情况下,渲染的场景都以这种方式不正确的绘制着。例如,如果沿眼睛视线的两个不相交的物体要被绘制,那么更远的那个物体将会被首先绘制。在深度遍历中如果在最近的物体后面还有物体,则场景的绘制方式是不正确的。因此,一个场景如果要被正确的绘制,唯一的方式就是通过排序。上面给出的例子仅仅说明了排序的必要性。在真正的游戏中,在场景建模的时候就可以简化这种排序,例如在一个包含大量建筑的城镇模型里。真正的排序机制也许更复杂,特别是当物体之间并不相交,但是有一定缠绕的的时候。实际上,如果一个场景包括一个透明的物体,判定一个正确的绘制顺序是非常难的,甚至需要对物体进行分割。比如像室外场景中的树,他们每一个都是通过相交的a混合的多边形来实现。

空间排序的的基本前提是避免在屏幕上多次的绘制一个像素,术语深度复杂性(depth complexity)意味着一个像素被绘制多少次,因为整个屏幕每一帧都要绘制,所以理想的深度复杂性是1;也就是说:每个象素只被绘制一次。深度复杂性越高、屏幕刷新率就越低。

现在典型的排序方法是深度缓存,在第三章中已经讨论过了。这个方法是以像素为单位判定的,在最远和最近切割片之间的的深度值存储在深度缓存里。每个像素的颜色值存储在帧缓存里,假设深度缓存启动了测试和写的功能,一个像素的颜色若要绘制进帧缓存中,则它的深度值必须小于目前那个像素颜色的深度值,这个过程对于程序事先的渲染器来说是很慢的,但是有了硬件加速的支持,深度缓存是一个很好的通用的深度排序解决方案。

深度缓存需要一个三角形来处理包围住的像素是不被绘制的,但是当三角形不会被绘制的时候,就不要把它们送给渲染器去判定了,以物体为单位的判定(物空间算法)要胜过以像素为单位的判定(象空间算法)。本章将要讨论比较高级的空间排序算法.12.1节是一个对四叉树和八叉树的概要介绍,这两种树性结构提供了一种对场景的规则划分方案。四叉树是用来对平面的的矩形作划分的,八叉树是对长方体的作划分的。然而许多游戏所需要的排序方案是与游戏场景数据相关的。对一个室内场景来说,自然使用一种与视口(protal)相关的自然排序方式(12.2的主题),对一个室外场景和需要正确绘制包含a混合多边形的场景来讲:二叉空间分割树非常有用,12.3节将描述这种树,其中包括如何构建这种树以及它如何用于隐藏面切除、可见性判定、选择、和碰撞监测。

12.1 四叉树和八叉树

场景图提供了一种对物体选择的基本机制,节点绑定空间对于视锥体的比较对照可以极大地减少被送到渲染器的节点数量。如果绑定空间与视锥体相交,那它的以它的绑定节点为根结点的子树才会被进一步处理,但是这一种处理仅仅基于绑定空间的信息。系统中还有关于场景结构得更高级的信息可以被利用。例如在一个地形系统中,可以建立一个可见性图来帮助减少在当前视点位置看不见的地形片。特别是一个地形中有高山隐藏了山后情景的时候,所以那些隐藏片就没有必要被处理了,尽管他们的绑定空间信息与视锥体相交。

四叉树和八叉树都是用来将场景空间剖分为小单元的。可见性图也是以单元为基础的。当视点被放置到某一个单元的时候,与这个单元有关的一个潜在可视单元列表就会被产生,如果场景结构被精心设计来支持来这种策略的话,这个方案是非常有效的。

这种以单元为单位的可视性判定场景图的构造有两种方案,第一种:以平面位置为基础来构建,在这种情况下这个平面被剖分为一个四叉树;第二种方案是以空间位置为基础,这种情况下整个空间被剖分为八叉树。场景图中的一个节点代表一个具体的四叉树块或者是一个八叉树块。如果一个节点代表一个节点代表一个四叉树块,则它有四个子节点。如果它代表一个八叉树块,则有八个子节点。另外子节点都被用来在这些单元里的实际物体。如果物体随时间移动,则场景图需要不断地通过把物体连到节点或取消联系来重新设置。然而,基本的四叉树或八叉树结构在整个程序运行周期是一致的。

下面给出一个四叉树或八叉树场景图处理的伪代码。四叉树块的可见性列表存储一些指针,这些指针指向根据目前所在块判定的所有潜在可视块的节点。

   cameraBlock=GetBlockOf(render.camera);

   visibleList=GetVisibleCellsFrom(cameraBlock);

   for(each node in visibleList)

       renderer.Draw(node);

就像第四章提到的,Draw函数递归地遍历某一个确定的子树并且努力在绘制之前基于绑定空间进行选择。基于四叉树结构地子树划分在基于绑定空间比照的选择中被去除。

当然这个过程的难点是建立可见性列表。一个优秀的可见性判定的参考是Seth Teller 1992年的博士论文,而Hanan Samet1989/1990年出版的两个小册子提供了所有关于四叉树和八叉树的一切。

12.2入口(portal)

四叉树和八叉树空间排序就努力在程序关于场景结构和物体的元信息的基础上建立一个可见性图。程序制作者有责任手工或通过某种自动方法建立可见性图。一个需要比较少的交互的方法是基于入口的系统。在这个系统中与其建立一个复杂的可见性图,倒不如游戏程序员增加附加的切割面把视锥体裁减城更小的。典型的情况是视点位置在一个屋子外面,但是却向房子里面看。那个门口就是一个使你看到屋内东西的入口(portal)。但是门口附近的墙会封闭很多屋内内容的视图。所以在绘制这个屋子的时候,被墙所所遮挡的屋内屋体可以被去除,甚至,如果物体是潜在隐藏的,通过门口框架组成的面和视点位置一起可以被用来建立切割面,这些切割面可以被用来裁减,而这些裁减可以作为消隐处理的补充。入口技术在室内风格的游戏中是非常有用的,因为在那种情况下有很多的墙和其他阻挡视线的物体,以便于消隐(culling)处理能够很有效。然而,入口技术的使用对一个室内场景是没有限制的。例如:一个对于视点可见的动画角色可能会跑到建筑物后面。假设建筑物足够的高,通过屋顶上面并不能看到角色的顶部,那些由视点位置和角色前面的建筑物的边构成的切割片能够在一个入口系统中使用。一旦角色完全在建筑物后面(也就是说角色在切割片的非可见性一面),它就会被完全的消除也就不会被送给渲染器去处理了。

12.1过入口的可见性演示

12.1说明了入口的一种典型情况,左图中的灰色区域说明的是在标准视图体中渲染器需要处理的空间。又边的图说明了入口切割片如何限制所考虑到的内容。如果使用第四章的层级化机制,这种多余切割面选择的实现是非常琐碎的。这种消隐机制保存一个六位的标志。每一位表示这个物体对于相对应的切割面片是否是可见的。这个标志可以扩展到使用任意多个位,试点可以存储另外消隐目的的切割面片,相同面片可以用来裁减。但是在硬件加速的API里,例如OpenGLDirect3D都仅仅支持很小数量的切割面片,一个入口系统如果要利用好API就要限制相关多余切割面片的数量。

一个使用了入口技术的室内场景,空间必须能够被划分为多个凸体区域,通过这样做,区域的组成部分渲染的顺序就不重要了。其实入口自身就是一个凸多边形,这个凸边多形存在于讲两个凸体区域分割开来的面片上。从这种角度上理解:一个入口是双向的,尽管是为了有趣的效果,没有必要非要是这样。但是可以把两个临近的区域够建成一个这样的区域,可以从一个里面看另一个的区域,但从另一个里面就看不到前者了。实际上,第二个区域甚至不会对第一个有连结入口。这代表了单向远距离传屋的概念,我们将会假设入口是非双向的,如果两个临近区域通过一个公共的图形入口都是可见的,那么这两个区域将和这个入口连接起来也就是两个入口共存于空间的同一个位置。

区域和入口结合在一起就可以形成一个任意复杂的场景。例如:可以站在一个区域里,通过一个入口看另一个区域,也可以从那个区域里通过入口另一个区域。渲染机制必须以从后向前的顺序绘制区域以保证正确的虚拟效果。这个方案通过够建一个抽象的直接的图来实现,这种图里面区域就是图的节点、入口就是图的边。这种徒步是父子场景图。而是代表着相邻区域的关系。每一个区域以图中的节点表示,节点包括足够的信息来支持对相邻区域的遍历。入口(portal)作为区域的子节点连结在区域节点上来支持入口消隐。如果一个区域正被相邻图形访问。但是不一定这个区域所有的入口都在视图锥体内(或者是视锥体和切割面相交而定义出空间集的一部分)。对相邻场景图的连续遍历可以忽略这样的入口,有效完成另一种消隐。最后,区域节点还可以有其他的子节点代表区域的绑定面(墙,也就是说:如果区域是个房子)和在区域中的物体及在区域中可见的可绘制体。下面给出一个在入口系统中绘制一个凸区域的伪代码。物体planeSet是目前渲染器用来消隐和裁减的切割面集合。入口所含的是切割面都是通过入口凸多边形和视点位置形成的。

   void Render(Region region)

   {

     if (not region.beingVisited)

       {

              region.beingVisited=true;

              for(each portal in region)

              {

                     if(portal.IsVisibleWithRespectTo(planSet))

                     {

                            planeSet.Add(Portal.planes);

                            Render(portal.adjacentRegion);

                     }

              }

              Render(region.boundingPlanes);

              Render(region.containerdObject);

              region.beingvisited=false;

       }

   }

12.2简单入口示例

12.3入口系统中的L形区域

 访问标志在几种情情况下是必需的,一种是区域有一个双向得入口通向连个相邻区域,另一种是这个区域有一个单项入口通入.或者通出。这避免了在抽象图里导致遍历死循环。图12.2显示了一个凸区域、入口及相应领图的简单集合。

 尽管区域必须视凸的。但是一个非凸体区域也可以通过非可见墙切割成凸区域集合就可以被入口系统处理了。入口的使用不同于典型的入口的代表--某个墙上的口子(门、窗口)。门12.3展示了一个L型的区域在入口系统中如何被表示。

 如同前面提到的一样,入口面可以用来消隐和裁减,然而渲染的性能必须考虑。如果一个场景有大量的入口,则哪儿也就意味着在同一时刻有大量的附加切割面,这样则化在消隐和裁减方面的时间可能会非常巨大甚至以至于让渲染器使用很少的切割面和依赖深度缓存成了更好的选择。

当面向一个凸多边形入口给系统添加的附加切割面太多的时候,同样的问题也会产生。这样你就有了两个选择。一个是用常规的切割面进行消隐处理。另一个则是采用近似处理:通过近似的一个遍比较少的凸多边形来代替原先的老构造入口,然而又可能产生这样的情况:原始情况下可能被去除的物体在近似情况下却被保留下来。权衡的关键在于两种情况下化去的时间的对比,一种情况是采用附加切割面的情况,另一种情况是仅采用深度缓存,后者往往都是封闭的。

12.3二叉空间剖分

一个特别通用的空间排序方法就是二叉空间剖分算法,在这个算法当中,n维空间被超平面剖分成凸子集。在n=2的情况下,剖分结构就是一条直线。n=3的情况下,剖分结构就是一个平面。一个二叉空间剖分(binary space partitioning)树简称BSP树,这是一种用来代表剖分的数据结构。在n=3的情况下,根节点代表所有的空间并且包含所有的剖分空间剖分面。第一个子节点或称前子节点代表着空间得这层剖分的剖分面正面的相关子空间集合。也就是说:如果剖分面是N*X-d=0(大写子母是矢量),那么左子树代表那些N*X-d>0.术语“前”(子树)是与可见性排序相对应的。如果剖分面是由物体的一个表面产生且视点位置是在剖分面的正侧,那么这个(物体)表面就是可见的且叫做前向。第二个子节点或称后子节点代表剖分面的阴面相关空间子集合。任何一个空间子集都可以继续被剖分面继续剖分,则那些节点则存储剖分面,他们的子节点则代表更小的凸空间子集。众多的叶节点则代表最终的剖分空间集。这些空间集可被绑定或解开绑定。图12.4展示了一个二维的BSP树。这个正方形代表所有的R2,内部节点表示了他们所代表的剖分面和叶节点所代表的凸空间区域集合。

BSP树逼八叉树和四叉树更通用,因为它对剖分面的方位没有限制。并且八叉树和四叉树可以用BSP树来实现。由于四叉树里,一个父节点四个兄弟节点,所以为前两个兄弟节点增加一个新的父节点,并且使旧的父节点成为“爷爷”节点。一个新的父节点被简单的增加给另外的两个父节点。所以前两个兄弟节点的父节点代表四方形地左半部分,而兄弟节点则代表对这一半做的进一步的剖分。对八叉树也有相同的方式。一个父节点和八个兄弟节点将会被一个树所取代,这个树中将会使旧的得父节点成为一个曾祖节点,然后增加两个“爷爷”节点、四个“父亲”节点。

12.4  BSP树剖分

这个主题的正式的论文是Fuchs,Kedem(1979),Naylor1980)。关于BSPFAQ(reality.sgi.com/bspfaq/)提供了对这个主题一个概括性介绍和相关信息和源代码的链接。

12.3.1 BSP树构造

 尽管一个BSP树是一个对空间的剖分,但它也可以剖分空间中的物体。如果一个物体在剖分面的正面,那么这个物体就与代表这个剖分面的节点的前子节点相联系。类似的,如果一个物体在剖分面的背面,它则与后子节点相联系。当物体横跨剖分面的时候,类型判别就有难度了。在这种情况下,物体可以被切割为两个子物体,每一个与一个子节点相联系。如果物体是可剖分空间,则子物体们也是可剖分空间且互相共享着一个落在剖分面上的公共表面。一个BSP树的实现中,将场景中的物体视作多边形池,且把共享表面和代表剖分面的节点存储在一起。因为可能做很多分割,所以这样可以减少内存消耗,因为公共表面数据被一次性存储且被可剖分空间共享。下面给出构造树的伪代码。前提条件是初始多边形列表非空的。

    void ConstrucTree(BspTree tree,PolygonList list)

    {

       PolygonList posList negList;

       EdgeList sharedList;

 

       tree.plane=SelectPartitionPlane(list);//Dot(N,X)-c=0

       for (each polygon in list) do

       {

              type=Classify(polygon,tree.plane);

              if(type==POSITIVE) then

              {

                     //Dot(N,X)-c>=0 for all vertices with at least

                     //one positive

                     posList.Add(polygon);

              }

              else if(type==TRANSVERSE)then

              {

                     //Dot(N,X)-c is positive for at least one vertex

                     //and negative for at least one vertex.

                     Polygon posPoly,negPoly;

                     Edge sharedEdge;

                     Split(polygon,tree.plane,posPoly,negPoly,sharedEdge);

                     positiveList.Add(posPoly);

                   negativeList.Add(negPoly);

                     sharedList.Add(sharedEdge);

              }

              else//type==COINCIDENT

              {

                     //Dot(N,X)-c=0 for all vertices

                     tree.coincident.Add(polygon);

              }

       }

       if(sharedList is not empty)

       {

              //Find all disjoint polygon in the intersection of

              //partition plane with polygon list.

              PolygonList compont;

              ComputeConnectedComponents(sharedList,component);

              tree.coincident.Append(component);

       }

      

       if(posList is not empty)

       {

              tree.positive=new BspTree;

              ContructTree(tree.positive,posList);

       }

       if (negList is not empty)

       {

              tree.negative=new BspTree;

              ConstructTree(tree.negatice,negList);

       }

}

SelectPartitionPlane()函数选择一个程序所需求的剖分面,其输入是一个多边形列表,因为一个典型的剖分面肯定包含其中的一个用到的多边形,但是也可能在基于列表数据的情况下选到其他剖分面。例如:在建造一个基于绑定盒子的树时,这种情况就可能发生。一个定向绑定盒可以适合于表中的数据,然后选定剖分面的法向量则满足其轴向线有最大长度。后面的选择砸是努力创造一个平衡的BSP树,另一个选择则可以满足像多边形分割数目最小化这样的准则。

针对三角形列表的Split函数是实质意义上的第一个裁减算法(第三章中提到过),更为广泛的情况是,多边形列表上的循环代表对多边形是否被剖分面切割真假判定。这就使得BSP树可以用作计算性立体几何操作。这个伪代码结构化的说明了在切割共享定点集的正背面多边形。共享边在后面处理以用来计算在剖分面里相交的多边形。对很多系统来说,对这些多边形处理基本没必要,所以共享边代码可以除去。

最后请注意:当ConstructTree函数调用结束的时候,相关的树节点就都包含一致的多边形。还可以使用其它的终止停止标准:1.当正面或背面的多边形数目小于程序要求的门限值;2.当树的深度达到最大深度的时候。这两个标准都在定向绑定盒子树构造中提到过。

12.3.2 隐藏面消除

通过对BSP树的深度优先遍历,可以提供一种非常有效的多边形排序方法。排序的代价是多边形在这个过程中不得不被分割。在静态几何中,树可以在预处理过程中建立,所以排序的代价不包括在实时运行的代价范围内。

从后向前的绘制

12.5不能排序的多边形

首先绘制离视点位置最远的物体,然后绘制离视点位置逐渐靠近的物体,这是画家算法的核心。物体绘制的顺序就像画家在画布上作画的顺序一样:先背景,依次再前景。这种方法的前提条件是两个可见多边形必须可以用一个面分开。图12.5展示了一种不能分开的情况。然而,BSP树构造可以将重叠多边形剖分成不相交的多边形,由树的叶节点所代表的多边形就可以正确的使用由后向前的顺序来绘制了。下面给除了遍历的伪代码,假设BSP构造过程中并没有使用前面提到的共享表机制。对视图方向的监测减少了空间的剖分,因为视图体后面的空间是不可见的。

    void DrawBackToFront(BspTree tree,Camera camera)

    {

       //compute signed distance from eye point e to plane

       //Dot(N,X)-c=0

       float sd=Dot(tree.plane.N,camera.E)-tree.plane.c;

       if(sd>0)

       {

              if(-Dot(tree.plane.N,camera.D)>=camera.cos(A))

              {

                     if(tree.negative is not empty)

                            DrawBackToFront(tree.negative,camera.E);

                     DrawPolygons(tree.coincident);

              }

              if(tree.positive is not empty)

                     DrawBackToFront(tree.positive,camera.E);

       }

       else if(sd<0)

       {

              if(Dot(tree.plane.N,camera.D)>=camera.cos(A))

              {

                     if(tree.positive is not empty)

                            DrawBackToFront(tree.positive,camera.E);

                     DrawPolygons(tree.coincident);

              }

              if(tree.negative is not empty)

                     DrawBackToFront(Tree.negative,camera.E);

       }

       else

       {

              if(Dot(tree.plane.N,camera.D)>=0)

              {

                     if(-Dot(tree.plane.N,camera.D)>=camera.cos(A))

                     {

                            if(tree.negative is not empty)

                                   DrawBackToFront(tree.negative,camera.E);

                            DrawPolygons(tree.coincident);

                     }

                     if(tree.positive is not empty)

                            DrawBackToFront(tree.positive,camera.E);

              }

              else

              {

                     if(Dot(tree.plane.N,camera.D)>=camera.cos(A)) 

                     {

                            if(tree.positive is not empty)

                                   DrawBackToFront(tree.positive,camera.E);

                            DrawPolygons(tree.coincident);

                     }

                     if(tree.negative is not empty)

                            DrawBackToFront(tree.negative,camera.E);

              }

       }

   }

视点的方向是D,视图体的外扩角度是2AA的余弦值将会预先计算存储在camera对象内以用来做选择处理。当sd<0,点积(dot)值用来判定N是否在视图锥体内。如果在的的话,剖分面的定位则可能与视图锥体相交。如果不在的话,剖分面的背面则不会与视图锥体相交且是不可见的,所以不会被绘制,一个更为精确的选择可以通过剖分面和视图锥体的隔离测试来实现。在这种情况下,剖分面法向量合视线方向的点积的正负号很重要。

从前向后的绘制

 从后向前的绘制可以精确的绘制场景,但是像素重绘的代价非常大。深度复杂性之高以至于这种算法根本不能用于实时绘制。最好还是先绘制离视点位置最近的多边形。由于正确的排序,一旦一个像度被写入,就不会再被其他多边形覆盖。这需要一种像素标示符来标示这个像素是否已经被绘制。 注意这种标示符和深度缓存是不一样的。深度缓存使用的时候,并不确定多边形绘制的顺序,深度制在写入之前测试。更重要的是:使用深度缓存,一个像素可以重写入很多次

扫描线标记

 BSP树可以用两种方法来支持这种像素标记(既像素标示符),一种方法是独立地跟踪每一个扫描线。当一个三角形被光栅化的时候,与三角形相交的扫描线之间会有一个或更多像素的间隔,这些像素便被写入。一个一维的BSP 可以用来跟踪写入间隔。每一个节点代表一个间隔[X0,X1),其中左端点包括在区间里,而右端点没有。这种半闭合区间是为了支持每一个三角形都能够对它的边沿和垂直边负责,以此来确保三角形的共享边和共享顶点不会重绘。初始状态下:一个空的扫描线由一个单顶点BSP树代表,如果屏幕宽度是W个像素,那么节点间隔区间就是[0,W)。现在如果一个三角形光栅化过程中与与扫描线的[X0,X1)区间相交,则值X0就会引发区间分为[0,x0)[x0,w),左区间将会与根节点的左子节点相联系,右区间则与根节点的右子节点相联系。值x1将进一步将区间[x0,W)分割为一个[X0,X1)左子节点和一个[X1W)右子节点。图12.6说明了电划分区间的光栅化过程。图中说明了划分的区间和导致区间划分的的X值。考虑一下:在这个扫描线上一个新的区间[X2,X3)将要被光栅化。因为参数的缘故。假设0<=X0<X2<X1<X3<=W.X2首先被处理,在根节点上把它与X0相比较,X2比较大,则右子节点将会被访问,然后将它与X1相比较,X2比较小。因为左子节点代表一个绘制了像素的区间,所以没有划分。值X3现在将被处理,树将被遍历,比较将最终访问代表[X1,W)的节点,这个区间没有绘制像素,所以将被划分为[X1,X3][X3,W].这个左区间将会被标记为已绘制的,右区间标记为未绘制的。

12.6代表一根扫描线上的已绘制象素的一维BSP

 这种标记的方法非常适合与软件渲染,将会保留很多闭合链。注意:在三角形边设置里,顶点属性插值仍然适用。更重要的是:如果一个预先准备好的区间由扫描线BSP树给分割了,那么分割区间端点的顶点属性也必须插值处理。

区域标记

 扫描线标记概念可以扩展到二维。一个BSP树代表目前屏幕上的绘制状态。当一个三角形被光栅化的时候,包含三角形边的每一条线将会被树处理。这条线用来选择三角形里被绘制的点集,在三条边被处理完之后,BSP树至多有7个叶节点,其中的一个与被光栅化的三角形有关。下一个光栅化的三角形则处理它的边,但是重叠是有可能的。技巧是正确的标记节点为绘制的或未绘制的。实际上,区域标记算法创照了一个BSP树,这个BSP树的已绘制节点都是像素的不相交区域。这些像素将为给定帧二绘制。

12.3.3 可见性判定

 给出视点的情况下。可见行判定就是从视点位置来确定场景地那一部分是可见的过程。在一个由多边形物体组成的场景中,知道那些物体是可见的,有助于最小化送到渲染器的数据量,封闭的概念就要提到了。封闭在场景种的物体不一定要被渲染器处理。可见性信息可以用于封闭选择(culling,一个判定那些物体从目前视点是不可见的过程,对一个静态场景来说,视点是不可移动的,可见性信息可以在预处理的过程中得到。然而,如果视点可以移动,可见的物体将是随时间改变的。动态精确的判定可见集是一个花销很大的过程。大多数系统都努力得到一个近似的最小数量的物体集送到渲染器,但是其中有一些可能是不可见的。

上面提到的portal系统在portal数目很小的情况下是一种不错的动态可见性判定方法。这儿两种方法都有描述,一种是用作3D场景、一种是用作2D场景。在这两种情况下,BSP树用来代表剖分的场景空间和从前向后的绘制。把这个树叫做场景树(WORLD TREE)。第二个BSP树用来存储可见性信息,叫做可见性树。

视锥体空间方法

 三维空间的可见性树初始状态代表视图锥体,树中的剖分面就锥体的那六个面。给定视点位置后,遍历场景树,遍历到的多边形被可见性树处理并可能分解为子多边形。最终每个多边形或者可见或者不可见,每一个子多边形的边和视点结合形成新的剖分面集合。视点和相关的面组成一个角锥体,角锥体中位于子多边形之后的任何一部分都是不可见的。可见性树现在存储着角锥体,并且用它对场景树遍历中访问到的多边形做进一步的裁减。

屏幕空间方法

 二维空间的可见性树初始状态代表着与屏幕可绘制象素相关的三角形。给定目前视点位置后,遍历场景树,遍历中碰到的每个多边形都要投射到屏幕,然后被可见性树处理并被分解为子多边形,其中每一个或者完全可见或者完全不可见。因为场景树对多边形是从前向后的排序,所以在整个可见性树计算的过程中,裁减以后可见的子多边将一直保持是可见的,这些子多边可以存储在一个列表中以备程序使用。

12.3.4 选择机制和碰撞监测

 假定一个BSP树代表整个世界,一个选择操作将包括一个线性元素(直线、射线、或线段)与场景中的任何物体是否相交的判定。一种方法是遍历这个场景树对这个线性元素进行分割(类似于扫面线分割,译者注)。一旦在某个节点处出现这个线性元素的子元素,则场景中的那个多边形就找到了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

jaopen

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值