v D 排序.docx
《v D 排序.docx》由会员分享,可在线阅读,更多相关《v D 排序.docx(15页珍藏版)》请在冰豆网上搜索。
vD排序
第九章:
Z-sorting(深度排列)
简介:
本章主要介绍下面三个方面:
a:
什么是Z-sorting(深度排序)
b:
layeringscenerenders(分层渲染场景)
c:
用四叉树进行复杂的渲染的渲染
注:
viewport层就是ViewportLayer类的实例.它包含在viewport内
Z-sorting的解释:
在前面的章节中你一定碰到过Z-sorting(深度排序)的问题,你用的3D模型(model)越复杂,如果Z-sorting控制不准确,导致的问题也将会越来越明显。
下面两幅图显示了两个茶壶,它清楚的显示了什么是Z-sorting(深度排序).
上面的图显示有一部分平面不可见,喷口后面的平面由于排在茶壶身体所在面的后面(即就是排在后面,我们不可见的一面),因而不可见,这就是利用了Z-sorting。
Thepainter'salgorithm(排序算法)
Z-sorting(深度排序)方法将决定每个平面在scene里的显示深度,其深度位置是按照scene里的平面(即3D对象的某一面)距camera的距离来决定的。
在排序时判断哪个平面应该在前面是非常耗cpu的(TheprocessofdeterminingwhichtriangleshouldbeinfrontisCPUintensive.)。
默认的,papervision3d用一种非常快速但不是很精确的算法来为scene里的每个平面排序。
该算法就是Thepainter'salgorithm,它的主旨是:
远处的物体应在近处的物体之前被显示出来(即先显示远处的物体,再显示近处的物体)
该算法的流程如下:
第一:
所有在camera视图里的平面(3D对象的某一面)是按照距camera的距离,由远到近进行排序的。
第二:
所有的平面(3D对象的某一面)是按照已排好的顺序依次显示在scene上
即:
最先开始显示最远处的的对象然后由远及近依次显示各个对象。
最近的对象最后显示。
上面最左边的图里的山是离camera最远的,所以它最先显示出来,然后显示中间那张图里的草型区域,最后显示右图里的树林。
Sortingtriangles——对平面进行排序
为了更好的理解和深度排序有关的问题,我们需要更好的理解深度排序,下面是一幅标有注解的图。
注:
triangle:
平面。
按照Thepainter'salgorithm排序算法,则最先显示的是最远处的平面对象。
所以先显示平面B,然后显示平面A。
下面的两幅图,左边是按由近及远顺序显示的图(图Expected),右边是按照深度排序的方法显示的图(图painter'salgorithm)
有一个很有效的方法去解决上面显示不明了的问题,即对B平面进行再分。
下图是对平面细分的指导:
上面的平面B被再细分成平面B和平面C。
渲染scene时,将会首先显示平面C然后是平面A,最后是平面B。
下图是再分后的结果:
如果换一个视角,将会导致重新排序,平面显示的顺序依旧是由远到近,只是此时各个平面距camera的距离发生了改变。
如下图:
这时,平面A将会最先显示,然后是B最后是C。
但是这里将会导致一个Z-sorting问题:
即平面A将会和B重叠。
Layeringyourrenders:
为了增加3D对象的显示效果,可以增加多个三角形平面(即创建更多的segment),但它不是最好的解决方案。
因为当多个三角形平面的顶点(vertex)投影到scene上时,三角形平面越多,投影到scene上的点也越多,因而消耗的cpu也越多.更好的解决办法是用viewport(视口)层,这些viewport(视口)层被嵌套在viewport里(因为viewport可以多方位旋转,因而有多个viewport层)。
这些viewport(视口)层的概念与photoshop和flash里层的概念一样,他们的工作原理一样,最上层的总是显示在最前面。
在photoshop里,每层只是放一种元素或者一种混合元素。
在Pv3d里也是一样,每层只放一种元素。
例如:
我们可以将椅子放在某一层,然后将桌子放在另一层。
每层的排列顺序可以通过pv3d
里算法实现。
<该段主要是讲pv3d层的概念:
每个层只能放一种元素,该元素既可以是单一的,也可以是混合的>
为了演示上述方法,我们将重用在第八章介绍过了的animatedMill模型。
下面将展示一些Z-sorting(深度排序)的问题。
下面的代码将作为本章的模板。
package{
importflash.events.Event;
importorg.papervision3d.core.animation.clip.AnimationClip3D;
importorg.papervision3d.events.FileLoadEvent;
importorg.papervision3d.materials.ColorMaterial;
importorg.papervision3d.materials.WireframeMaterial;
importorg.papervision3d.materials.special.CompositeMaterial;
importorg.papervision3d.objects.parsers.DAE;
importorg.papervision3d.objects.primitives.Plane;
importorg.papervision3d.view.BasicView;
privateclassViewportLayersExampleextendsBasicView
{
privatevarmill:
DAE;
privatevarfloor:
Plane;
privatevarrotX:
Number=0.1;
privatevarrotY:
Number=0.1;
privatevarcamPitch:
Number=90;
privatevarcamYaw:
Number=270;
privatevareaseOut:
Number=0.1;
publicfunctionViewportLayersExample()
{
stage.frameRate=40;
init();
}
privatefunctioninit():
void
{
mill=newDAE(true,null,true);
mill.addEventListener(FileLoadEvent.LOAD_COMPLETE,modelLoaded);
mill.load("assets/animatedMill.dae");
varcolorMat:
ColorMaterial=newColorMaterial(0x006600);
varwireMat:
WireframeMaterial=newWireframeMaterial();
varfloorMat:
CompositeMaterial=newCompositeMaterial();
floorMat.addMaterial(colorMat);
floorMat.addMaterial(wireMat);
floorMat.doubleSided=true;
floor=newPlane(floorMat,1000,1000,1,1);
floor.y=-410;
scene.addChild(floor);
floor.rotationX=90;
}
privatefunctionmodelLoaded(e:
FileLoadEvent):
void
{
scene.addChild(mill);
varanimationLeft:
AnimationClip3D=newAnimationClip3D("right",0,6);
varanimationRight:
AnimationClip3D=newAnimationClip3D("left",6,12);
mill.animation.addClip(animationRight);
mill.animation.addClip(animationLeft);
mill.play("right");
startRendering();
}
overrideprotectedfunctiononRenderTick(e:
Event=null):
void
{
varxDist:
Number=mouseX-stage.stageWidth*0.5;
varyDist:
Number=mouseY-stage.stageHeight*0.5;
camPitch+=((yDist*rotX)-camPitch+90)*easeOut;
camYaw+=((xDist*rotY)-camYaw+270)*easeOut;
camera.orbit(camPitch,camYaw);
super.onRenderTick();
}
}
}
在init()方法里,我们加载了外部模型,并把它添加到了scene里。
为地板floor对象添加了由WireframeMaterial和ColorMaterial组成的混合材质(floorMat)。
最后onRenderTick(e:
Event=null)方法里的几段代码是创建鼠标的交互性。
移动鼠标,Camera将会绕scene原点进行旋转,因而我们就能看到多个方位。
测试代码,将会是下面这样的画面,下面的地板floor有一些Z-sorting(深度排序)问题。
因为从底部看,模型mill在floor的后面,我们看不到mill模型。
为了能让我们在floor后面看到mill模型,下面我们用层的概念分析一下该问题:
为了解决该问题,我们在此提出上面讲述了很多的层问题。
用层的方法可以解决mill模型不可见的问题。
因为从底部看,模型mill在floor的后面,即floor在最上层,而mill模型在floor的下一层,因而我们看不到mill模型。
为了使mill模型在从底部看可见,我们只需改变floor和mill模型层的顺序的即可。
下面有一些改变层顺序的方法:
用useOwnContainer属性创建一个viewport层
设置3D对象的useOwnContainer为true(3dDisplayObject.useOwnContainer=true;),将会创建一个新的层,该层在所有层的上面(即该层在最上面)。
并且该3d对象被画在了该层上。
因而该3d对象将不会被遮住。
在上面的代码中为了使我们从底部看mill模型可见,只需在modelLoaded()方法里加上下面一句代码:
mill.useOwnContainer=true;
加上上句代码后再次测试,发现现在的效果更好,图片如下:
换多个方位看,我们会发现不存在Z-sorting问题了。
其图片显示如下:
右图是从较低的点的视口看的(即看它的底部)。
把useOwnContainer设为true后,mill模型始终在最上层,因而我们始终能看到mill模型。
但这是不符合真实的,
并且该属性很耗cpu。
下面我们将介绍另外两种方法来代替useOwnContainer方法。
下面的两种方法更符合现实。
用getChildLayer方法创建viewport层并给viewport层排序
通过viewport3D类的getChildLayer方法得到ViewportLayer的实例。
varmillLayer:
ViewportLayer=viewport.getChildLayer(mill);
为了使它运行,我们还需要导入ViewportLayer类:
importorg.papervision3d.view.layer.ViewportLayer;
getChildLayer()的三个参数:
参数数据类型默认值用法描述
do3dDisplayObject3D————获得的do3d的所在层
或者为do3d创建一个新的
层(创建新的层需要第二个参数)
createNewBooleantrue是否为do3d创建一个新的
viewport层(该值一般设
为true,这样才能创建
一个新的层,因为新层
在所有层的最上面)
RecurseBooleantrue是否将do3d里的子对象也
添加到该层
当你得到新的viewport层的实例,你也可以将其他的3D对象添加到同一个层中。
例如我们可以将大量mill模型添加到同一层.作为用法演示:
我们在先前的代码的基础上,将floor添加到millLayer层里。
当然这是没有意义的,这和不用层得到的效果是一样的。
因为添加floor以后,floor就又在最上面了。
我们在这里只是介绍用法:
millLayer.addDisplayObject3D(floor);
实例化ViewportLayer来创建viewport层
下面是用法:
varmillLayer:
ViewportLayer=newViewportLayer(viewport,null);
viewport.containerSprite.addLayer(millLayer);
millLayer.addDisplayObject3D(mill,true);
ViewportLayer类的构造函数的三个参数:
参数数据类型默认值用法描述
ViewportViewport3D————Viewport3D实例对象
do3dDisplayObject3D————被添加进层的3d对象
在getChildLayer()
方法里可以设置Recurse为
true,将3d对象的子对象全部添加到层中。
但是在
ViewportLayer构造函数中并没有Recurse这个参数,因此
在上面的代码中将do3d属性设为null,然后用addDisplayObject3D()方法将do3d对象添加到层,
addDisplayObject3D()方法的第二参数为Recurse,故可以在这里进行设置
isDynamicBooleanfalse是否要去除层在接
下来的渲染。
这个参数时pv3d内部决定的,不能在外部修改,只能在源码处进行修改
addDisplayObject3D()的一个参数为do3d第二个为Recurse。
Sortinglayers
前面的例子展示了使用viewport层来解决Z-sorting(深度排序)问题,但是增加多个层也会带来处理层的问题。
Papervision3D有三个内置的模式(mode)去处理这个问题。
用到的是
sortMode属性。
用法如下:
viewport.containerSprite.sortMode=ViewportLayerSortMode.Z_SORT;
Papervision3D支持viewport层的3种排序算法是ViewportLayerSortMode类三个静态常量属性
ViewportLayerSortMode.Z_SORT
ViewportLayerSortMode.ORIGIN_SORT
ViewportLayerSortMode.INDEX_SORT
在使用上面的3个mode时,必须导入类:
importorg.papervision3d.view.layer.util.ViewportLayerSortMode;
用ViewportLayerSortMode.Z_SORT给viewport层排序:
默认的,这些viewport层是按照Z-sorting深度排序的算法进行排序的.他们排序的方式是基于层上的每个点(vertices)的Z坐标距camera的平均值来决定(即层内所有的点在Z轴上距camera的距离/点的总数)。
因而距camera最远的层将会最先显示在scene在舞台上,然后是较远的显示。
目前,我们只为mill模型创建了一个层,但我们要用层给mill模型和floor进行排序时。
我们需要将floor也添加到viewport层中(一个新的viewport层)
varmillLayer:
ViewportLayer=viewport.getChildLayer(mill);
varfloorLayer:
ViewportLayer=viewport.getChildLayer(floor);
上面的代码创建了两个新层。
当你测试,从顶部和从底部看你的3D模型和floor会发现不存在Z-sorting(深度排序)问题。
但是,把camera放在floor的上面一点点,会发现又出现了Z-sorting(深度排序问题),这时会发现整个floor对象显示在mill模型前面而不是单一的一个平面。
为此,我们可以强制为某个viewport层设置深度(即距camera的距离),如果设置floor距camera的距离为2000,因而达到为floor排序的目的。
下面是用法:
floorLayer.forceDepth=true;
floorLayer.screenDepth=2000;
用上述方法可以达到为viewport重新排序的目的(即强制设置他们的深度)。
用ViewportLayerSortMode.ORIGIN_SORT给层排序
第二种算法是基于每个三D对象的原点进行的:
当用默认的z-sorting(即:
ViewportLayerSortMode.Z_SORT)排序无法满足精确的要求时。
例如有一个模型,在某一面上有多个点(vertices),而在其他面上的点却很少。
当按上述方法对它的深度(即距camera的距离)取平均值显然是不精确的,这将会导致失去平衡。
为了解决这种情况,我们使用
ViewportLayerSortMode.ORIGIN_SORT排序,它是按照3D对象的原点进行排序的。
当一个层中包含很多3D对象时,他们的原点将会取平均值。
用ViewportLayerSortMode.INDEX_SORT给viewport层(ViewportLayer)排序
这种算法是基于层的layerIndex属性来实现的。
我们可以控制设定它:
varmillLayer:
ViewportLayer=viewport.getChildLayer(mill);
varfloorLayer:
ViewportLayer=viewport.getChildLayer(floor);
millLayer.layerIndex=1;
floorLayer.layerIndex=2;
viewport.containerSprite.sortMode=ViewportLayerSortMode.INDEX_SORT;
我们这样设置millLayer.layerIndex=1;floorLayer.layerIndex=2;,将会导致scene里先创建millLayer(即离camera较远)然后再创建floorLayer层(即离camera较近),由于floorLayer层较后创建,因而floorLayer层在的最上面。
尽管这样做对floorLayer层意义不大,但是我们是借此来介绍怎样使用
layerIndex属性。
目前我们已经有了几种可供选择的方法来给viewport层(ViewportLayer)进行排序。
但是这些方法在我们的demo(即演示例子)里的使用都不能达到正确的效果。
一个可供选择的方法是将它设为只有一面可见即:
matrial.oneSide=true;但是对于这个demo,这并不是我们想要的。
当cameral在floor的上表面和下表面时,我们需要正确(即符合实际)的观察floor和mill模型
如果我们用ViewportLayerSortMode.INDEX_SORT给层排序,当cameral在floor的上表面和者下表面之间进行变动时,使用layerIndex属性来交换viewport层(ViewportLayer)的深度
将会产生怎么的结果呢?
在onRenderTick()方法里来设定这些属性
//注意:
下面的代码是demo的代码的基础上进行的
//下面的代码是放在onRenderTick()里面的
if(camera.y<-410)
{
viewport.getChildLayer(mill).layerIndex=1;
viewport.getChildLayer(floor).layerIndex=2;
}
else
{
viewport.getChildLayer(mill).layerIndex=2;
viewport.getChildLayer(floor).layerIndex=1;
}
为了使layerIndex属性正常运行
我们还需要这样设置:
viewport.containerSprite.sortMode=ViewportLayerSortMode.INDEX_SORT;
这行代码放在modelLoaded()方法里
上述方法成功解决了demo里存在的问题但这个方法并不是在每个工程中都适用。
Creatingandsortingsublayers
不仅viewport支持viewport层(ViewportLayer),viewport层他们自己也可以嵌套层(即层中再包含层),因而我们可以在同一个scene里使用多种排序方法。
下面我们按照图示写出相关代码:
//Createviewportlayer1structure
varviewportLayer1:
ViewportLayer=newViewportLayer(viewport,null);
varviewportLayer1_1:
ViewportLayer=newViewportLayer(viewport,chair);
varviewportLayer1_2:
ViewportLayer=newViewportLayer(viewport,table);
viewportLayer1.addLayer(viewportLayer1_1);
viewportLayer1.addLayer(viewportLayer1_2);
viewportLayer1.sortMode=ViewportLayerSortMode.INDEX_SORT;
//Createareg