Papervision3D Essentials 要点整理
2010年09月28日
二、构建你的第一个应用
我们需要一个 场景,摄像机,观察口,有材质的3d对象,和一个渲染引擎.
scene.addChild(sphere);
addChild(viewport);
renderEngine.renderScene(scene,camera,viewport);
BasicView类
startRendering();
override protected function onRenderTick(e:Event=null):void
{
sphere.localRotationX +=1;
super.onRenderTick();
}
BasicView构造方法
参数 数据类型 默认值 说明
1 viewportWidth Number 640 观察口的宽度
2 viewportHeight Number 480 观察口的高度
3 scaleToStage Boolean true true时观察口的尺寸随舞台进行缩放,用来建立全舞台的观察口时很有用
设置观察口的大小为800*600,在BasicView子类的构造器中输入:
Super(800,600,false);
三、基本几何体
类Vertices3D负责创建顶点,TriangleMesh3D类负责创建由顶点和三角面组成的3d对象.
将3d信息转换到2d屏幕,这个过程的顺序称为:渲染管道(rendering pipeline).
初始化 à 投影 à 渲染
投影:投影的过程只处理顶点,不处理三角面和材质.
渲染:此过程用附着的材质信息组成2d投影数据.
纸飞机(PaperPlane):为开发者提供一种用来执行全面测试的简单对象.
显示双面材质的一种方式是设置plane的meterial属性,将doubleSided属性设为true.
plane.material.doubleSided = true;
圆柱 Cylinder
圆锥体Cone
立方体Cube
Cube的构造器第一个参数不是材质,是材质列表(materials list).
var materialsList:MaterialsList = new MaterialsList();
materialsList.addMaterial(red,"front");
materialsList.addMaterial(red,"back");
materialsList.addMaterial(blue,"left");
materialsList.addMaterial(blue,"right");
materialsList.addMaterial(green,"top");
materialsList.addMaterial(green,"bottom");
materialsList.addMaterial( green,"all");
Cube的参数insideFaces指定立方体内部可见的面;参数excludeFaces指定不创建的面.
例如Cube.ALL-CUBE.LEFT;Cube.NONE
获取3d对象的尺寸(包围盒)
trace(paperPlane.boundingBox().size);
箭头 Arrow:
默认值是宽400,高100,深600.
嵌套 Nesting:
使用DisplayObject3D
访问顶点:每个3D对象的顶点都存放在一个数组中.
do3D.geometry.vertices
访问对象的三角面片:
do3D.geometry.faces
四、材质
基础材质类叫做MaterialObject3D
纹理和材质的区别在于纹理是被用作材质的图像源。
材质可以修改属性从而影响纹理贴到物体上的效果,你也可以这样理解材质,就是扩充了的纹理。
让物体不丢失性能且能增加一些额外的细节的技巧叫做烘焙纹理。
PV3d 将材质成2D方式扭曲,造成透视的假象。变形、扭曲后的图片贴在3D物体上看上去就很逼真了,这种PV3D的贴图方式我们叫做仿射贴图。
material.doubleSided = true;
material.oneSide = false;
material.opposite = true;
material.smooth = true;
material.interactive = true;
BitmapFileMaterial:
在文件加载好时,BitmapFileMaterial对象会发出一个FileLoadEvent.LOAD_COMPLETE事件
l FileLoadEvent.LOAD_COMPLETE: 外部位图加载完成时,发出该事件。
l FileLoadEvent.LOAD_PROGRESS: 外部位图加载过程中,发出该事件。
l FileLoadEvent.LOAD_ERROR: 外部位图找不到时,发出该事件。
l FileLoadEvent.SECURITY_LOAD_ERROR: 安全错误发生时,发出该事件。
precise 精细度 当设置为true时,就会动态的创建额外的三角面用更精细的方式来绘制材质
阻止材质扭曲:
当precise属性设置为true时,这会引起文本晃动和闪烁。
定义精度模式来避免摇晃闪烁效果。精度模式默认为:PrecisionMode.NORMAL,我们将它设置为:PrecisionMode.STABLE。
material.precisionMode = PrecisionMode.STABLE;
CompositeMaterial:
将不同的材质组合起来当做一种材质来使用
var material:CompositeMaterial = new CompositeMaterial();
material.addMaterial(bmpMaterial);
material.addMaterial(wireMaterial);
interactivity 交互:
要与材质或者3D对象进行鼠标交互必须先设置viewport的interactive属性为true。
viewport.interactive = true;
super(640,480,false,true);
var viewport:Viewport3D = new Viewport3D(640,480,false,true);
material.interactive = true;
Using ButtonMode 按钮模式:
private function mouseOver(e:MouseEvent):void
{
viewport.buttonMode = true;
}
private function mouseOut(e:MouseEvent):void
{
viewport.buttonMode = false;
}
viewport.getChildLayer(cube_1).buttonMode = true;
l InteractiveScene3DEvent.OBJECT_ADDED
l InteractiveScene3DEvent.OBJECT_CLICK
l InteractiveScene3DEvent.OBJECT_DOUBLE_CLICK
l InteractiveScene3DEvent.OBJECT_MOVE
l InteractiveScene3DEvent.OBJECT_OUT
l InteractiveScene3DEvent.OBJECT_OVER
l InteractiveScene3DEvent.OBJECT_PRESS
l InteractiveScene3DEvent.OBJECT_RELEASE
l InteractiveScene3DEvent.OBJECT_RELEASE_OUTSIDE
Tiling 平铺:
使用平铺得置三个属性第一个属性tiled为True第二个和第三个是水平和纵向重复次数。
material.tiled = true;
material.maxU = 8;
material.maxV = 4;
Flipping your material 翻转材质:
Sprite 对象翻转:
给scaleX设置一个负值,横向翻转显示对象:
flippedSprite.scaleX = -1;
材质翻转:
BitmapMaterialTools.mirrorBitmapY()为纵向翻转.
BitmapMaterialTools.mirrorBitmapX(material.bitmap) ;
Power of two textures 2的幂数纹理:
你可以将位图的长宽随意设置为两个2的幂数组合。
我们可以使用PV3D实现的自动细化功能,这是BitmapMaterial类的一个静态属性。
BitmapMaterial.AUTO_MIP_MAPPING = true;
五、摄像机
Camera3D继承自CameraObject3D,CameraObject3D继承自DisplayObject3D.
moveForward(): 沿自身z轴向前移动对象.
moveBackward(): 沿自身z轴向后移动对象.
moveLeft(): 沿自身x轴向左移动对象
moveRight(): 沿自身x轴向右移动对象
moveUp(): 沿y轴向上移动对象
moveDown(): 沿y轴向下移动对象
分辨物体处于圆台内的过程及圆台之外的物体不渲染的过程称为:剔除(culling)
圆台剔除:这是刚才我们看过的-任何处于圆台之外的物体被剔除.Pv3d包含
背面剔除:背离摄影机的三角面被剔除.Pv3d包含
分配剔除:物体太小或距离太远不能附着材质,被剔除.Papervision3D不包含,但是可以告诉Papervision3D剔除与远景面相交的物体.
闭合剔除:一个对象完全的在另一个对象之后将被剔除,通常称其为隐面去除,Papervision3D不包含
开启圆台剔除:
只有完全的在圆台之外才被剔除camera.useCulling = true; 基于对象而非三角,当do3d开始剔除时打印true;
trace(do3d.culled);
开启观察孔级的剔除:
viewport.autoCulling = true;
此属性默认为true,所以完全可以不设置. 基于三角
剪裁:
通过将被剔除的三角分离到新的三角中的方式来解决此问题.
用FrustumClipping处理Papervision3D里的剪裁
它确保圆台之外的三角不被绘制
三角的顶点不被投影,提高性能
renderer.clipping = new FrustumClipping(FrustumClipping.NEAR);
FrustumClipping.ALL 所有的便都进行剪裁处理
FrustumClipping.NEAR 只有近景面不剪裁
FrustumClipping.TOP 只有 顶部面不剪裁
FrustumClipping.BOTTOM 只有底部面不剪裁
FrustumClipping.LEFT 只有左边面不剪裁
FrustumClipping.RIGHT 只有右边面不剪裁
使用FrustumClipping.NEAR就可以满足大部分情况.
因此出于性能的考虑,最好将圆台内对象的useClipping属性设为false.不过在camera.useCulling为true时,之后的操作便是多余的了.
使用圆台剪裁配合摄影机剔除.有2个好处:
因为对象处于圆台之外早已被剔除,他们不再需要剪裁测试了.
完全在圆台内的对象不需要剪裁测试.
precise在剔除和剪裁测试完毕之后.
precise的目的在于增加附着材质时的透视修正而不是为了阻止不必要的剔除.
l 缩放Zoom:缩放的默认值是40.
l 焦距Focus:减少焦距将增大视野.
l 视野Field of view,通常称其为fov或FoV:视野是摄影机在视图中的垂直角度. 默认值是60.
l 远景平面和近景平面 near,far:近景面与远景面定义了摄影机到2个面的距离.焦距是摄影机与近面之间的距离,near和focus相等.
只要继承自BasicView,就可以通过传递BasicView类构造器一个参数来选择摄影机类型.
super(stage.stageWidth,stage.stageHeight,true,fals e,
CameraType.TARGET);
CameraType.TARGET
是一直注视一个目标的摄影机.目标只要是DisplayObject3D的实例就可以
camera.target = myTarget;
DisplayObject3D.ZERO返回一个空的,坐标为(0,0,0)的DsiplayObject3D对象
CameraType.FREE
行为与目标摄影机完全一样,不过没有注视的目标.自由摄影机一直向前看,方向是本地z轴.如果选择CameraType.FREE类型,目标便是null.
在目标与自由摄影机之间进行切换
很简单,2者的区别是有否有注视的目标.切换到自由摄影机,将目标设为null即可:
camera.target = null;
如果想切换到目标摄影机,为摄影机设定一个DisplayObject3D目标即可.
CameraType.DEBUG
注意到左上角显示的信息:坐标、旋转、视野、近与远
用W,A,S,D或箭头键来移动摄影机.同时还可以拖动鼠标来操作摄影机绕x,y轴旋转.用Q,E使其绕z轴旋转.
CameraType.SPRING
第三人称或狩猎的摄影机
var camera:SpringCamera3D = SpringCamera3D(camera);
camera.mass = 20;
camera.damping = 4;
camera.stiffness = 1;
camera.positionOffset = new Number3D(0,150,-500);
camera.lookOffset = new Number3D(0,0,100);
camera.target = paperPlane;
参数 数据类型 默认值 说明
mass Number 40 摄影机的质量或重量.定义了被拉动时的硬度.mass值越高越难移动
damping Number 4 阻尼:控制内部摩擦力或摄影机克服的弹力效果的力.值越大弹力效果越弱,促进了更平滑的移动.
将值保持在1到20间
stiffness Number 1 弹簧的硬度或弹簧被拉伸的程度.值越大弹簧越难以被拉伸,将值保持在1到20间
positionOffset Number3D Number3D(0,5,-50) 摄影机的位置与目标局部空间的关系
lookOffset Number3D Number3D(0,2,10) 摄影机盯着目标局部空间的点.
Number3D(0,100,0)使摄影机盯着目标之上100单元处.
target属性通常会在init()方法中设置一次而lookAt()方法需要在渲染的方法中来不断的调用.
六、物体运动
我们可以移动什么?
场景中的3d物体
摄影机
点光
基于帧的动画
帧率越高动画越平滑;但帧率过低的话,动画看起来像抽了筋.帧率越高动画越快,反之亦然.
基于时间的动画
基于时间的动画,动画的速度更依赖时间而不是帧率.此类的动画可以用ActionScript3的Timer类或补间引擎来完成.
旋转物体:
moveForward(),moveBackward(),moveLeft(),moveRight( ),moveUp(),moveDown()方法,这些都可以让物体沿自身轴线移动.
localRotationX,localRotationY,和localRotationZ属性用来让物体绕自身的x,y,z轴旋转
本地旋转的另一种选择-pitch(),yaw(),和roll()
本地旋转与下面的3方法相对应:
pitch():绕自身x轴旋转物体(localRotationX)
yaw():绕自身y轴旋转物体(localRotationY)
roll():绕自身z轴旋转物体(localRotationZ)
do3D.yaw(1);
下面这话可以实现相同的功能:
plane.localRotationY++;
另一类的旋转-rotationX,rotationY,rotationZ
另一种旋转do3D物体的设置是rotationX,rotationY,totationZ.它们与本地旋转不同,它们不是让物体绕自身轴线旋转,而是绕局部空间的轴线(父对象坐标系的轴线)
当你设置旋转时,Papervision3D将欧拉角转换为四元数,最终用于旋转.
四元数可以3d物体的任何旋转及运行物体自由的绕任何轴旋转而不会出现上述问题.更多的Papervision3D里的四元数访问:
http://blog.zupko.info/?p=150
Papervision3D在y轴上加减与Flash的y轴加减是相反的.
camera.x = xDist * 2;
camera.y = -yDist * 2;
有缓动的鼠标交互:
camera.x += (xDist camera.x * reachX) * easeOut;
easeOut变量确定了ease-out公式的强度-值越高,运动停顿时间越短.
用鼠标来旋转物体:
private var rotX:Number = 0.5;
private var rotY:Number = 0.5;
grid.rotationX += (yDist * rotX grid.rotationX) * easeOut;
grid.rotationY += (xDist * rotY grid.rotationY) * easeOut;
摄影机环绕物体:
摄影机环绕是让摄影机绕物体外围虚拟的球体进行旋转.这是一个比较流行的技术并且可以用camera.orbit()来完成.
camPitch = yDist * rotX + 90;
camYaw = xDist * rotY + 270;
camera.orbit(camPitch,camYaw);
参数 数据类型 默认值 说明
1 pitch Number 环绕x轴
2 yaw Number 环绕y轴
3 useDegress Boolean true pitch和yaw用角度还是弧度
4 target DisplayObject3D null 环绕的目标选项.如果不传递则使用摄影机的目标,若摄影机目标为null,摄影机会为(0,0,0)为目标
我们可以调用Tweener.isTweening()方法,它会告诉你目前的对象是不是正在进行补间.
if (!Tweener.isTweening(e.displayObject3D))
{
}
摄影机调用copyTransform()来复制Player的位置和旋转,这不止复制do3d的位置也包括旋转和缩放.
camera.copyTransform(player);
八、外部模型
尽量减少模型的多边形
一般来说三角行数量不要超过3000,也就是1500个多边形(一个多边形由两个三角形组成)。
使用uvmap贴图:
如果你的模型只有少数的物体和贴图,使用uvmap是个不错的选择,他的使用方法就是把模型解包(unwrapping)并且把所有的材质定义到一张图像上
烘焙纹理:
烘焙纹理就是将高光、阴影、折射、反射以及整个3d场景整合为一张图片的过程。
物体和材质的命名:
给你的材质和物体建立一个好的命名规范,同样的为你代码中的类、方法以及属性也采取一定的命名规范。
模型的大小和位置:
一定要把物体放在默认的位置,原点位置
model = new DAE();
model.addEventListener(FileLoadEvent.LOAD_COMPLETE ,modelLoaded);
DAE(model).load("assets/teapot.DAE");
scene.addChild(model);
模型: /assets/models/
材质图片: /assets/textures/
daeModel.addFileSearchPath("assets/textures");
1 autoPlay Boolean true 如果模型中内置动画,那么pv3d会自动的播放
2 name String null 随意的名字,让你可以更便捷的使用模型
3 loop Boolean false 定义动画是否循环播放,默认只播放一次
动画裁剪:
AnimationClip3D类为我们提供了这个方法。
参数 数据类型 默认值 描述
1 Name String - 动画剪辑名,制定动画片段的唯一名称
2 starTime Number 0.0 定义动画的开始时间,不能大于总的时间以及结束时间,你可以从daeModel.animation.endTime这个属性里面找到模型的总动画时间
3 endTime Number 0.0 定义动画的结束时间,不能超过总的动画时间
var animationRight:AnimationClip3D = new AnimationClip3D("right",0,6);
var animationLeft:AnimationClip3D = new AnimationClip3D("left",6,12);
DAE(model).animation.addClip(animationRight);
DAE(model).animation.addClip(animationLeft);
如果你想播放向右旋转的动画的话,只要调用play()方法
DAE(model).play("right");
如果你想停止动画,你就这么写
DAE(model).stop();
加载3DS模型:
model = new Max3DS();
model.addEventListener(FileLoadEvent.LOAD_COMPLETE ,modelLoaded);
Max3DS(model).load("assets/teapot.3ds", null, "./assets/");
在这里load()函数有两个方法,第一个是要调用的文件的路径,第二个参数定义的是一个材质文件的列表,在这个案例中我们略过不填,第三个参数定义的是材质存放的目录,这个目录是相对与发布swf文件目录的。
九、深度排序
Z-sorting(深度排序)方法将决定每个平面在scene里的显示深度,其深度位置是按照scene里的平面(即3D对象的某一面)距camera的距离来决定的。
为了增加3D对象的显示效果,可以增加多个三角形平面(即创建更多的segment),但它不是最好的解决方案。
更好的解决办法是用viewport(视口) 层,这些viewport(视口)层被嵌套在viewport里(因为viewport可以多方位旋转,因而有多个viewport层)。
每个层只能放一种元素,该元素既可以是单一的,也可以是混合的 用useOwnContainer属性创建一个viewport层
设置3D对象的useOwnContainer为true(3dDisplayObject. useOwnContainer=true;),将会创建一个新的层,该层在所有层的上面(即该层在最上面)。
用getChildLayer方法创建viewport层并给viewport层排序:
var millLayer:ViewportLayer = viewport.getChildLayer(mill);
getChildLayer()的三个参数:
参数 数据类型 默认值 用法描述
do3d DisplayObject3D ---- 获得的do3d的所在层
或者为do3d创建一个新的
层(创建新的层需要第二个参数)
createNew Boolean true 是否为do3d创建一个新的
viewport层(该值一般设
为true,这样才能创建
一个新的层,因为新层
在所有层的最上面)
Recurse Boolean true 是否将do3d里的子对象也
添加到该层
实例化ViewportLayer来创建 viewport 层:
var millLayer:ViewportLayer = new ViewportLayer(viewport,null);
viewport.containerSprite.addLayer(millLayer);
millLayer.addDisplayObject3D(mill,true);
用ViewportLayerSortMode.Z_SORT给viewport 层排序:
默认的,这些viewport 层是按照Z-sorting深度排序的算法进行排序的.
他们排序的方式是基于层上的每个点(vertices)的Z坐标距camera的平均值来决定(即层内所有的点在Z轴上距camera的距离/点的总数)。
强制为某个viewport 层设置深度(即距camera的距离)
floorLayer.forceDepth = true;
floorLayer.screenDepth = 2000;
用 ViewportLayerSortMode.ORIGIN_SORT给层排序:
第二种算法是基于每个三D对象的原点进行的
当一个层中包含很多3D对象时,他们的原点将会取平均值。
用ViewportLayerSortMode.INDEX_SORT给viewport 层(ViewportLayer)排序:
var millLayer:ViewportLayer = viewport.getChildLayer(mill);
var floorLayer:ViewportLayer = viewport.getChildLayer(floor);
millLayer.layerIndex = 1;
floorLayer.layerIndex = 2;
viewport.containerSprite.sortMode = ViewportLayerSortMode.INDEX_SORT;
不仅viewport支持viewport层(ViewportLayer),viewport 层他们自己也可以嵌套层(即层中再包含层)
四叉树渲染引擎(Quadtree rendering):
实例化QuadrantRenderEngine只需要一个参数 该参数指定你要选择哪种排序的方法。这些方法是QuadrantRenderEngine类的静态常量。
QuadrantRenderEngine.CORRECT_Z_FILTER: :为不交叉的平面进行正确的排序。
QuadrantRenderEngine.QUAD_SPLIT_FILTER: 为交叉的平面进行排序,引擎将会自动的分割交叉的平面,然后进行正确的排序。
QuadrantRenderEngine.ALL_FILTERS:这个是上面两种类型的组合。它是默认值(即不传参数时默认是它)
我们可以不让3D对象不被四叉树渲染引擎(Quadtree rendering)进行渲染,因而该3D对象将会被默认的BasicRenderEngine引擎进行渲染。 我们可以用设置3D对象的testQuad属性为false来实现。
mill.getChildByName("Blades",true).testQuad = false;
mill.getChildByName("Stand",true).testQuad = false;
mill.getChildByName("House",true).testQuad = false;
因为直接设置mill.testQuad = false;对子对对象不会产生任何影响.
十、粒子
一个粒子可以被描述为一个二维图形,可以相对于一个3D点缩放和定位。
可以设想为一个轻量级的平面,始终面对着摄像头。
这种用面对摄像机的2D图形代替3D物体的技术就叫告示牌。
var particles:Particles = new Particles();
scene.addChild(particles);
for(var i:uint = 0; i 3D矢量绘图和3D文本
使用Letter3DMaterial创建材质
material = new Letter3DMaterial(0×000000);
这个类的构造函数有两个传入参数:
fillColor uint 0xFF00FF 定义材质的颜色,使用24位的 十六进制的数字,同时定义文本颜色也是使用该类数字
fillAlpha Number 1 设置材质的透明度
你也可以定义文本的外观。
通常是如下三属性--linethickness, linealpha, 与linecolor
text3D.material.lineThickness = 2;
text3D.material.lineAlpha = 1;
text3D.material.lineColor = 0xFF0000;
建立一个Font3D实例
Papervision3D拥有四个可选择的字体:
l HelveticaBold
l HelveticaLight
l HelveticaMedium
l HelveticaRoman
建立一个Text3D实例,传入文本,字体,材质参数,并将其添加到场景或其它的do3D。
text3D = new Text3D(text,font3D,material);
1 text String - 定义你想显示的文本
2 font Font3D - 设置文本的字体
3 material Letter3DMaterial - 设置字体材质
4 name String null Text3D实例名,可选
Text3D拥有更多的属性可选择,如下是它额外提供的参数:
l align:left、right、center
l letterSpacing:每个字符的间距
l lineSpacing:用于定义文本的垂直距离
创建字体:Five3D
添加交互性到3D矢量文本和3D矢量图形:
在init()方法的首行添加:
VectorShapeHitTest.instance.assignViewport(viewpor t);
添加交互性到3D文本:
我们添加监听器到Text3D实例的每一个字母。
for each(var letter:VectorLetter3D in text3D.letters)
{
letter.addEventListener(InteractiveScene3DEvent.OB JECT_OVER,overLetterListener);
letter.addEventListener(InteractiveScene3DEvent.OB JECT_OUT,outLetterListener);
}
Text3D拥有一个letters的属性,其是一个数组,储存着文本的各个字母。
绘制矢量图形-线条,圆,与矩形:
它也提供名为VectorShape3D类,用其绘制基本的矢量图
形,比如线条,圆,矩形。矢量图最初以2D形态绘制,然后放入3D空间。
var line:VectorShape3D = new VectorShape3D(material);
line.graphics.lineStyle(2,0×00CCFF);
line.graphics.beginFill(0×666699)
line.graphics.moveTo(-300,-300);
line.graphics.lineTo(300,-300);
scene.addChild(line);
使用Lines3D库绘制线条:
Lines3D使用Vertex3D建立线条的起点和终点
var blueMaterial:LineMaterial = new LineMaterial(0×0000FF);
1 color Number 0xFF0000 定义线的颜色,使用24位十六进制表示颜色数值
2 alpha Number 1 设定材料的透明度
lines = new Lines3D();
scene.addChild(lines);
var blueLine:Line3D = new Line3D(lines,blueMaterial,3,v0,v1);
1 instance Lines3D - Lines3D实例会持有以及渲染线
2 material LineMaterial - 针对线的材质
3 size Number - 线的宽度(粗细)
4 vertex0 Vertex3D - 起点,它是线的起点的3D坐标
5 vertex1 Vertex3D - 终点,它是线的终点的3D坐标
lines.addLine(blueLine);
曲线:
redLine.addControlVertex(-150,-300,0);
使用addNewLine()方法添加新线:
lines.addNewLine(5,0,0,0,300,0,300);
1 size Number - 线的宽度
2 x0 Number - 起点的X坐标
3 y0 Number - 起点的Y坐标
4 z0 Number - 起点的Z坐标
5 x1 Number - 终点的X坐标
6 y1 Number - 终点的Y坐标
7 z1 Number - 终点的Z坐标
建立分割的线:
lines.addNewSegmentedLine(3,8,300,0,300,600,0,0);
1 size Number - 线的宽度
2 segments Number - 分段数
3 x0 Number - 起点的X坐标
4 y0 Number - 起点的Y坐标
5 z0 Number - 起点的Z坐标
6 x1 Number - 终点的X坐标
7 y1 Number - 终点的Y坐标
8 z1 Number - 终点的Z坐标
增加交互到Lines3D的线:
var material:LineMaterial = new LineMaterial(0×000000,0.6);
material.interactive = true;
十三、性能的优化
var stats:StatsView = new StatsView(renderer);
addChild(stats);
FPS:Frames Per Second。每秒显示帧
每秒显示帧取决于最后的渲染需要的时间。
Tri: Triangles. 三角形
最后渲染时渲染的三角形数量。
Sha: Shaders. 着色器
最后渲染时渲染的已着色的三角形。已着色的三角形不计入 Tri 值。
Lin: Lines. 直线
最后渲染时渲染的直线的数量。
Par: Particles. 微粒
最后渲染时渲染的微粒的数量。
Ren: Rendered. 已渲染的
此值尚未使用。
RT: Render Time. 渲染时间
最后的渲染所需要的时间,以毫秒计算。
PT: Projection Time. 投影时间
投影最后渲染需要的时间,以毫秒计算。
COb: Culled Objects. 剔除的物体
最后渲染时剔除物体的数量。所以真正剔除对象的数量是这个数字除以 2。
CTr: Culled Triangles. 剔除的三角形
最后的渲染剔除的三角形的数量。
CPa : Culled Particles. 剔除的粒子
最后渲染时剔除的粒子的数量。
FOb: Filtered Objects. 过滤的物体
添加到 viewport 的 viewportObjectFilter 属性的物体的数量。
Mem: Memory. 内存
Flashplayer 和浏览器使用的内存
Poly count: Polygon Count.多边形的数量
只有当你调用 statsview 里的 updatePolyCount()方法时才会更新此值。
例如: stats.updatePolyCount(scene);
Stage quality 舞台品质:stage.quality = StageQuality.LOW;
StageQuality.BEST:图像是反锯齿的,使用了 4 乘 4 像素栅格,可以使 bitmap 很平滑。
StageQuality.HIGH:图像是反锯齿的,使用了 4 乘 4 像素栅格,如果 movie 是静态的,
可以使 bitmap 很平滑。一般地,stage 的品质都设置成这个值。
StageQuality.MEDIUM:图像是反锯齿的,使用了 2 乘 2 像素栅格,这不会使 bitmap
平滑并对文本阅读有点影响,对动画会有一点优化。
StageQuality.LOW:图像不是反锯齿的,bitmap 也不平滑。对文本的显示和动画的优化
有很大的影响。
其它普通的Flash优化:
在你不需要 event 监听器时,将它移除。
删除不需要的对象:
当你不再需要 scene 中子对象时,应将它删除并且将它的引用和 material 删除。
scene.removeChild(do3d);
do3d.material.destroy();
do3d = null;
Viewport 大小
镜头视域
剔除:
Camera.useCulling = true;
将 useCulling 设为 true,所有在视域外的物体都会被忽略,这会节省性能。
优化 material:
透明度:没有透明度的图片渲染得更快
tiled:永远试着把一个 material 的 tiled 属性设成 true。根据你的情况这会轻微地提高了性能(这是 Flash 播放器存在的一性能的 bug)。
双纹理的作用:双纹理的作用是用来 mipmapping(映射),它允许你将 material 的 smoothing 设为 true而又不丢失性能。
BitmapMaterial.AUTO_MIP_MAPPING = true;
当你设为这样的时候,Papervision3D 会更正所有新建的 bitmap material。缺点就是它会比把这个属性设为 false 的时候要用更多一点内存。
material 的大小:将 material 的宽和高尽量做小。
将在 object 背后的其它 object 删除:
动态设置far plane
Level of detail(细节的级别):
Papervision3D 内建了对 LOD 的支持,但它不支持 do3D 之间的窜改。这个没有窜改功能的技术叫做 simple level of detail(细节的简单级别),或者是 SLOD。
slod = new SimpleLevelOfDetail(spheres,600,2000);
scene.addChild(slod);
实例化 SimpleLevelOfDetail 类需要 4 个参数:
object Array -一个包含 3D object 的数组。数组的第一个元素代表最近而且拥有最多 detail 的,最后的元素是最远而且最简单的 3D object。
MinDepth Number 1000定义最近而且最多 detail 的 object 应该使用的距离。
MaxDepth Number 10000定义最远而且最少 detail 的 object 应该使用的距离。
Distances Array null一个用来定义 object 数组中每个 object深度值的一个 integer 数组。默认设为 null。如果提供了该数组,它会替换 minDepth 和 maxDepth 参数。
渲染的优化:
只有在需要的时候渲染:可以使用 stopRendering() 方法停止渲染器。也可以用startRendering() 方法开始渲染。
选择性地渲染:
renderer.renderLayers(scene,camera,viewport,[layer Reference]);
第 4 个参数,这个参数是一个包含了需要被渲染的viewport 的层的数组。
十四、其他技巧
获取一个大致的三维空间的二维鼠标位置:
var vMouse: virtualMouse= viewport.interactiveSceneManager.virtualMouse;
……
public function onMove(evt:InteractiveScene3DEvent):void {
if (InteractiveSceneManager.MOUSE_IS_DOWN) {
mc1.graphics.beginFill(0xBBBBBB,1);
mc1.graphics.drawCircle(vMouse.x, vMouse.y, 10);
mc1.graphics.endFill();
}
}
计算鼠标到目标物体之间的屏幕距离:
sphere.autoCalcScreenCoords = true; //允许do3d自动计算自身的屏幕坐标
var distanceX:Number = viewport.containerSprite.mouseX sphere.screen.x;
var distanceY:Number = viewport.containerSprite.mouseY sphere.screen.y;
var distance:Number = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
// var distance:Number = Number3D.sub(distanceX, distanceY).modulo;
// modulo 取模
父对象的应用:
private function onButtonClick(evt:MouseEvent):void {
if (sphere1.parent==do3d1) {
do3d1.removeChild(sphere1);
do3d2.addChild(sphere1);
} else {
do3d2.removeChild(sphere1);
do3d1.addChild(sphere1);
}
}
鼠标获取空间坐标的基础 & 射线和平面相交:
Pv3d里面描述一个无限大的三维平面,及其相关运算,是使用Plane3D这个类
var plane3d:Plane3D=new Plane3D();
plane3d.setNormalAndPoint(法线向量,点);
然后用方法setNormalAndPoint()来描述这个平面,它必须接受两个参数:
1 法线向量,是一个三维数Number3D,比如一个XZ的平面的法线描述方法就是new Number3D(0,1,0)
2平面上的某个点,也是一个三维数Number3D,比如原点坐标就是new Number3D(0,0,0);
private var plane3D:Plane3D = new Plane3D(XZPLANE, Number3D.ZERO);
var ray:Number3D = camera.unproject(viewport.containerSprite.mouseX, viewport.containerSprite.mouseY);
ray = Number3D.add(ray, camera.position);
var cameraVertex3D:Vertex3D = new Vertex3D(camera.x, camera.y, camera.z);
var rayVertex3D:Vertex3D = new Vertex3D(ray.x, ray.y, ray.z);
var intersectPoint:Vertex3D = plane3D.getIntersectionLine(cameraVertex3D, rayVertex3D);
之后返回来的intersectPoint这个三维数的intersectPoint.x, intersectPoint.y, intersectPoint.z既是我们鼠标点击在这个平面plane3d上反映出来的大概空间位置了。
将viewport作为遮罩层:
rover = new roverAsset() as Bitmap;
rover.mask = viewport;
addChild(rover);
&
exposedViewport.containerSprite.mask = portalViewport; // 两个viewport之间的遮罩
循环获取父对象的所有子对象:
for each(var sphere:Sphere in exposedSphere.children)
{
viewportLayer2.addDisplayObject3D(sphere); // 给viewportLayer添加对象
}
创建自己的网格三角面TriangleMesh3D:
var material:ColorMaterial = new ColorMaterial(0xcc0000);
material.doubleSided = true;
//创建三角面的三个顶点
var vertex3D_1:Vertex3D = new Vertex3D(-200, -200, 0);
var vertex3D_2:Vertex3D = new Vertex3D(200, -200, 0);
var vertex3D_3:Vertex3D = new Vertex3D(-200, 200, 0);
var triangleVertices:Array = [vertex3D_1, vertex3D_2, vertex3D_3];
//use null because we haven't created its parent mesh yet
var triangle3d:Triangle3D = new Triangle3D(null, triangleVertices, material);
var triangleFaces:Array = [triangle3d];
triangleMesh3d = new TriangleMesh3D(material, triangleVertices, triangleFaces, null);
triangle3d.instance = triangleMesh3d;
scene.addChild(triangleMesh3d);
四元组Quaternion和Slerp的应用:
Quaternion的四个值分别是轴向量(x,y,z)和沿该轴的转动角度,但其中不包括坐标位置的数据;
Slerp是0-1范围内的一个数,表示两个四元组之间的某个状态。
quaternion = Quaternion.slerp(startQuaternion, endQuaternion, slerp);
// 初始化四元组
planeQuat = Quaternion.createFromMatrix(plane.transform);
mouseQuat = Quaternion.createFromMatrix(mouse3D.transform);
// 逐帧变化
slerp += (1 slerp) * .1;
slerpQuat = Quaternion.slerp(planeQuat, mouseQuat, slerp);
plane.transform = slerpQuat.matrix;
plane.moveBackward(RADIUS);
renderer.renderScene(scene, camera, viewport);
2010年09月28日
二、构建你的第一个应用
我们需要一个 场景,摄像机,观察口,有材质的3d对象,和一个渲染引擎.
scene.addChild(sphere);
addChild(viewport);
renderEngine.renderScene(scene,camera,viewport);
BasicView类
startRendering();
override protected function onRenderTick(e:Event=null):void
{
sphere.localRotationX +=1;
super.onRenderTick();
}
BasicView构造方法
参数 数据类型 默认值 说明
1 viewportWidth Number 640 观察口的宽度
2 viewportHeight Number 480 观察口的高度
3 scaleToStage Boolean true true时观察口的尺寸随舞台进行缩放,用来建立全舞台的观察口时很有用
设置观察口的大小为800*600,在BasicView子类的构造器中输入:
Super(800,600,false);
三、基本几何体
类Vertices3D负责创建顶点,TriangleMesh3D类负责创建由顶点和三角面组成的3d对象.
将3d信息转换到2d屏幕,这个过程的顺序称为:渲染管道(rendering pipeline).
初始化 à 投影 à 渲染
投影:投影的过程只处理顶点,不处理三角面和材质.
渲染:此过程用附着的材质信息组成2d投影数据.
纸飞机(PaperPlane):为开发者提供一种用来执行全面测试的简单对象.
显示双面材质的一种方式是设置plane的meterial属性,将doubleSided属性设为true.
plane.material.doubleSided = true;
圆柱 Cylinder
圆锥体Cone
立方体Cube
Cube的构造器第一个参数不是材质,是材质列表(materials list).
var materialsList:MaterialsList = new MaterialsList();
materialsList.addMaterial(red,"front");
materialsList.addMaterial(red,"back");
materialsList.addMaterial(blue,"left");
materialsList.addMaterial(blue,"right");
materialsList.addMaterial(green,"top");
materialsList.addMaterial(green,"bottom");
materialsList.addMaterial( green,"all");
Cube的参数insideFaces指定立方体内部可见的面;参数excludeFaces指定不创建的面.
例如Cube.ALL-CUBE.LEFT;Cube.NONE
获取3d对象的尺寸(包围盒)
trace(paperPlane.boundingBox().size);
箭头 Arrow:
默认值是宽400,高100,深600.
嵌套 Nesting:
使用DisplayObject3D
访问顶点:每个3D对象的顶点都存放在一个数组中.
do3D.geometry.vertices
访问对象的三角面片:
do3D.geometry.faces
四、材质
基础材质类叫做MaterialObject3D
纹理和材质的区别在于纹理是被用作材质的图像源。
材质可以修改属性从而影响纹理贴到物体上的效果,你也可以这样理解材质,就是扩充了的纹理。
让物体不丢失性能且能增加一些额外的细节的技巧叫做烘焙纹理。
PV3d 将材质成2D方式扭曲,造成透视的假象。变形、扭曲后的图片贴在3D物体上看上去就很逼真了,这种PV3D的贴图方式我们叫做仿射贴图。
material.doubleSided = true;
material.oneSide = false;
material.opposite = true;
material.smooth = true;
material.interactive = true;
BitmapFileMaterial:
在文件加载好时,BitmapFileMaterial对象会发出一个FileLoadEvent.LOAD_COMPLETE事件
l FileLoadEvent.LOAD_COMPLETE: 外部位图加载完成时,发出该事件。
l FileLoadEvent.LOAD_PROGRESS: 外部位图加载过程中,发出该事件。
l FileLoadEvent.LOAD_ERROR: 外部位图找不到时,发出该事件。
l FileLoadEvent.SECURITY_LOAD_ERROR: 安全错误发生时,发出该事件。
precise 精细度 当设置为true时,就会动态的创建额外的三角面用更精细的方式来绘制材质
阻止材质扭曲:
当precise属性设置为true时,这会引起文本晃动和闪烁。
定义精度模式来避免摇晃闪烁效果。精度模式默认为:PrecisionMode.NORMAL,我们将它设置为:PrecisionMode.STABLE。
material.precisionMode = PrecisionMode.STABLE;
CompositeMaterial:
将不同的材质组合起来当做一种材质来使用
var material:CompositeMaterial = new CompositeMaterial();
material.addMaterial(bmpMaterial);
material.addMaterial(wireMaterial);
interactivity 交互:
要与材质或者3D对象进行鼠标交互必须先设置viewport的interactive属性为true。
viewport.interactive = true;
super(640,480,false,true);
var viewport:Viewport3D = new Viewport3D(640,480,false,true);
material.interactive = true;
Using ButtonMode 按钮模式:
private function mouseOver(e:MouseEvent):void
{
viewport.buttonMode = true;
}
private function mouseOut(e:MouseEvent):void
{
viewport.buttonMode = false;
}
viewport.getChildLayer(cube_1).buttonMode = true;
l InteractiveScene3DEvent.OBJECT_ADDED
l InteractiveScene3DEvent.OBJECT_CLICK
l InteractiveScene3DEvent.OBJECT_DOUBLE_CLICK
l InteractiveScene3DEvent.OBJECT_MOVE
l InteractiveScene3DEvent.OBJECT_OUT
l InteractiveScene3DEvent.OBJECT_OVER
l InteractiveScene3DEvent.OBJECT_PRESS
l InteractiveScene3DEvent.OBJECT_RELEASE
l InteractiveScene3DEvent.OBJECT_RELEASE_OUTSIDE
Tiling 平铺:
使用平铺得置三个属性第一个属性tiled为True第二个和第三个是水平和纵向重复次数。
material.tiled = true;
material.maxU = 8;
material.maxV = 4;
Flipping your material 翻转材质:
Sprite 对象翻转:
给scaleX设置一个负值,横向翻转显示对象:
flippedSprite.scaleX = -1;
材质翻转:
BitmapMaterialTools.mirrorBitmapY()为纵向翻转.
BitmapMaterialTools.mirrorBitmapX(material.bitmap) ;
Power of two textures 2的幂数纹理:
你可以将位图的长宽随意设置为两个2的幂数组合。
我们可以使用PV3D实现的自动细化功能,这是BitmapMaterial类的一个静态属性。
BitmapMaterial.AUTO_MIP_MAPPING = true;
五、摄像机
Camera3D继承自CameraObject3D,CameraObject3D继承自DisplayObject3D.
moveForward(): 沿自身z轴向前移动对象.
moveBackward(): 沿自身z轴向后移动对象.
moveLeft(): 沿自身x轴向左移动对象
moveRight(): 沿自身x轴向右移动对象
moveUp(): 沿y轴向上移动对象
moveDown(): 沿y轴向下移动对象
分辨物体处于圆台内的过程及圆台之外的物体不渲染的过程称为:剔除(culling)
圆台剔除:这是刚才我们看过的-任何处于圆台之外的物体被剔除.Pv3d包含
背面剔除:背离摄影机的三角面被剔除.Pv3d包含
分配剔除:物体太小或距离太远不能附着材质,被剔除.Papervision3D不包含,但是可以告诉Papervision3D剔除与远景面相交的物体.
闭合剔除:一个对象完全的在另一个对象之后将被剔除,通常称其为隐面去除,Papervision3D不包含
开启圆台剔除:
只有完全的在圆台之外才被剔除camera.useCulling = true; 基于对象而非三角,当do3d开始剔除时打印true;
trace(do3d.culled);
开启观察孔级的剔除:
viewport.autoCulling = true;
此属性默认为true,所以完全可以不设置. 基于三角
剪裁:
通过将被剔除的三角分离到新的三角中的方式来解决此问题.
用FrustumClipping处理Papervision3D里的剪裁
它确保圆台之外的三角不被绘制
三角的顶点不被投影,提高性能
renderer.clipping = new FrustumClipping(FrustumClipping.NEAR);
FrustumClipping.ALL 所有的便都进行剪裁处理
FrustumClipping.NEAR 只有近景面不剪裁
FrustumClipping.TOP 只有 顶部面不剪裁
FrustumClipping.BOTTOM 只有底部面不剪裁
FrustumClipping.LEFT 只有左边面不剪裁
FrustumClipping.RIGHT 只有右边面不剪裁
使用FrustumClipping.NEAR就可以满足大部分情况.
因此出于性能的考虑,最好将圆台内对象的useClipping属性设为false.不过在camera.useCulling为true时,之后的操作便是多余的了.
使用圆台剪裁配合摄影机剔除.有2个好处:
因为对象处于圆台之外早已被剔除,他们不再需要剪裁测试了.
完全在圆台内的对象不需要剪裁测试.
precise在剔除和剪裁测试完毕之后.
precise的目的在于增加附着材质时的透视修正而不是为了阻止不必要的剔除.
l 缩放Zoom:缩放的默认值是40.
l 焦距Focus:减少焦距将增大视野.
l 视野Field of view,通常称其为fov或FoV:视野是摄影机在视图中的垂直角度. 默认值是60.
l 远景平面和近景平面 near,far:近景面与远景面定义了摄影机到2个面的距离.焦距是摄影机与近面之间的距离,near和focus相等.
只要继承自BasicView,就可以通过传递BasicView类构造器一个参数来选择摄影机类型.
super(stage.stageWidth,stage.stageHeight,true,fals e,
CameraType.TARGET);
CameraType.TARGET
是一直注视一个目标的摄影机.目标只要是DisplayObject3D的实例就可以
camera.target = myTarget;
DisplayObject3D.ZERO返回一个空的,坐标为(0,0,0)的DsiplayObject3D对象
CameraType.FREE
行为与目标摄影机完全一样,不过没有注视的目标.自由摄影机一直向前看,方向是本地z轴.如果选择CameraType.FREE类型,目标便是null.
在目标与自由摄影机之间进行切换
很简单,2者的区别是有否有注视的目标.切换到自由摄影机,将目标设为null即可:
camera.target = null;
如果想切换到目标摄影机,为摄影机设定一个DisplayObject3D目标即可.
CameraType.DEBUG
注意到左上角显示的信息:坐标、旋转、视野、近与远
用W,A,S,D或箭头键来移动摄影机.同时还可以拖动鼠标来操作摄影机绕x,y轴旋转.用Q,E使其绕z轴旋转.
CameraType.SPRING
第三人称或狩猎的摄影机
var camera:SpringCamera3D = SpringCamera3D(camera);
camera.mass = 20;
camera.damping = 4;
camera.stiffness = 1;
camera.positionOffset = new Number3D(0,150,-500);
camera.lookOffset = new Number3D(0,0,100);
camera.target = paperPlane;
参数 数据类型 默认值 说明
mass Number 40 摄影机的质量或重量.定义了被拉动时的硬度.mass值越高越难移动
damping Number 4 阻尼:控制内部摩擦力或摄影机克服的弹力效果的力.值越大弹力效果越弱,促进了更平滑的移动.
将值保持在1到20间
stiffness Number 1 弹簧的硬度或弹簧被拉伸的程度.值越大弹簧越难以被拉伸,将值保持在1到20间
positionOffset Number3D Number3D(0,5,-50) 摄影机的位置与目标局部空间的关系
lookOffset Number3D Number3D(0,2,10) 摄影机盯着目标局部空间的点.
Number3D(0,100,0)使摄影机盯着目标之上100单元处.
target属性通常会在init()方法中设置一次而lookAt()方法需要在渲染的方法中来不断的调用.
六、物体运动
我们可以移动什么?
场景中的3d物体
摄影机
点光
基于帧的动画
帧率越高动画越平滑;但帧率过低的话,动画看起来像抽了筋.帧率越高动画越快,反之亦然.
基于时间的动画
基于时间的动画,动画的速度更依赖时间而不是帧率.此类的动画可以用ActionScript3的Timer类或补间引擎来完成.
旋转物体:
moveForward(),moveBackward(),moveLeft(),moveRight( ),moveUp(),moveDown()方法,这些都可以让物体沿自身轴线移动.
localRotationX,localRotationY,和localRotationZ属性用来让物体绕自身的x,y,z轴旋转
本地旋转的另一种选择-pitch(),yaw(),和roll()
本地旋转与下面的3方法相对应:
pitch():绕自身x轴旋转物体(localRotationX)
yaw():绕自身y轴旋转物体(localRotationY)
roll():绕自身z轴旋转物体(localRotationZ)
do3D.yaw(1);
下面这话可以实现相同的功能:
plane.localRotationY++;
另一类的旋转-rotationX,rotationY,rotationZ
另一种旋转do3D物体的设置是rotationX,rotationY,totationZ.它们与本地旋转不同,它们不是让物体绕自身轴线旋转,而是绕局部空间的轴线(父对象坐标系的轴线)
当你设置旋转时,Papervision3D将欧拉角转换为四元数,最终用于旋转.
四元数可以3d物体的任何旋转及运行物体自由的绕任何轴旋转而不会出现上述问题.更多的Papervision3D里的四元数访问:
http://blog.zupko.info/?p=150
Papervision3D在y轴上加减与Flash的y轴加减是相反的.
camera.x = xDist * 2;
camera.y = -yDist * 2;
有缓动的鼠标交互:
camera.x += (xDist camera.x * reachX) * easeOut;
easeOut变量确定了ease-out公式的强度-值越高,运动停顿时间越短.
用鼠标来旋转物体:
private var rotX:Number = 0.5;
private var rotY:Number = 0.5;
grid.rotationX += (yDist * rotX grid.rotationX) * easeOut;
grid.rotationY += (xDist * rotY grid.rotationY) * easeOut;
摄影机环绕物体:
摄影机环绕是让摄影机绕物体外围虚拟的球体进行旋转.这是一个比较流行的技术并且可以用camera.orbit()来完成.
camPitch = yDist * rotX + 90;
camYaw = xDist * rotY + 270;
camera.orbit(camPitch,camYaw);
参数 数据类型 默认值 说明
1 pitch Number 环绕x轴
2 yaw Number 环绕y轴
3 useDegress Boolean true pitch和yaw用角度还是弧度
4 target DisplayObject3D null 环绕的目标选项.如果不传递则使用摄影机的目标,若摄影机目标为null,摄影机会为(0,0,0)为目标
我们可以调用Tweener.isTweening()方法,它会告诉你目前的对象是不是正在进行补间.
if (!Tweener.isTweening(e.displayObject3D))
{
}
摄影机调用copyTransform()来复制Player的位置和旋转,这不止复制do3d的位置也包括旋转和缩放.
camera.copyTransform(player);
八、外部模型
尽量减少模型的多边形
一般来说三角行数量不要超过3000,也就是1500个多边形(一个多边形由两个三角形组成)。
使用uvmap贴图:
如果你的模型只有少数的物体和贴图,使用uvmap是个不错的选择,他的使用方法就是把模型解包(unwrapping)并且把所有的材质定义到一张图像上
烘焙纹理:
烘焙纹理就是将高光、阴影、折射、反射以及整个3d场景整合为一张图片的过程。
物体和材质的命名:
给你的材质和物体建立一个好的命名规范,同样的为你代码中的类、方法以及属性也采取一定的命名规范。
模型的大小和位置:
一定要把物体放在默认的位置,原点位置
model = new DAE();
model.addEventListener(FileLoadEvent.LOAD_COMPLETE ,modelLoaded);
DAE(model).load("assets/teapot.DAE");
scene.addChild(model);
模型: /assets/models/
材质图片: /assets/textures/
daeModel.addFileSearchPath("assets/textures");
1 autoPlay Boolean true 如果模型中内置动画,那么pv3d会自动的播放
2 name String null 随意的名字,让你可以更便捷的使用模型
3 loop Boolean false 定义动画是否循环播放,默认只播放一次
动画裁剪:
AnimationClip3D类为我们提供了这个方法。
参数 数据类型 默认值 描述
1 Name String - 动画剪辑名,制定动画片段的唯一名称
2 starTime Number 0.0 定义动画的开始时间,不能大于总的时间以及结束时间,你可以从daeModel.animation.endTime这个属性里面找到模型的总动画时间
3 endTime Number 0.0 定义动画的结束时间,不能超过总的动画时间
var animationRight:AnimationClip3D = new AnimationClip3D("right",0,6);
var animationLeft:AnimationClip3D = new AnimationClip3D("left",6,12);
DAE(model).animation.addClip(animationRight);
DAE(model).animation.addClip(animationLeft);
如果你想播放向右旋转的动画的话,只要调用play()方法
DAE(model).play("right");
如果你想停止动画,你就这么写
DAE(model).stop();
加载3DS模型:
model = new Max3DS();
model.addEventListener(FileLoadEvent.LOAD_COMPLETE ,modelLoaded);
Max3DS(model).load("assets/teapot.3ds", null, "./assets/");
在这里load()函数有两个方法,第一个是要调用的文件的路径,第二个参数定义的是一个材质文件的列表,在这个案例中我们略过不填,第三个参数定义的是材质存放的目录,这个目录是相对与发布swf文件目录的。
九、深度排序
Z-sorting(深度排序)方法将决定每个平面在scene里的显示深度,其深度位置是按照scene里的平面(即3D对象的某一面)距camera的距离来决定的。
为了增加3D对象的显示效果,可以增加多个三角形平面(即创建更多的segment),但它不是最好的解决方案。
更好的解决办法是用viewport(视口) 层,这些viewport(视口)层被嵌套在viewport里(因为viewport可以多方位旋转,因而有多个viewport层)。
每个层只能放一种元素,该元素既可以是单一的,也可以是混合的 用useOwnContainer属性创建一个viewport层
设置3D对象的useOwnContainer为true(3dDisplayObject. useOwnContainer=true;),将会创建一个新的层,该层在所有层的上面(即该层在最上面)。
用getChildLayer方法创建viewport层并给viewport层排序:
var millLayer:ViewportLayer = viewport.getChildLayer(mill);
getChildLayer()的三个参数:
参数 数据类型 默认值 用法描述
do3d DisplayObject3D ---- 获得的do3d的所在层
或者为do3d创建一个新的
层(创建新的层需要第二个参数)
createNew Boolean true 是否为do3d创建一个新的
viewport层(该值一般设
为true,这样才能创建
一个新的层,因为新层
在所有层的最上面)
Recurse Boolean true 是否将do3d里的子对象也
添加到该层
实例化ViewportLayer来创建 viewport 层:
var millLayer:ViewportLayer = new ViewportLayer(viewport,null);
viewport.containerSprite.addLayer(millLayer);
millLayer.addDisplayObject3D(mill,true);
用ViewportLayerSortMode.Z_SORT给viewport 层排序:
默认的,这些viewport 层是按照Z-sorting深度排序的算法进行排序的.
他们排序的方式是基于层上的每个点(vertices)的Z坐标距camera的平均值来决定(即层内所有的点在Z轴上距camera的距离/点的总数)。
强制为某个viewport 层设置深度(即距camera的距离)
floorLayer.forceDepth = true;
floorLayer.screenDepth = 2000;
用 ViewportLayerSortMode.ORIGIN_SORT给层排序:
第二种算法是基于每个三D对象的原点进行的
当一个层中包含很多3D对象时,他们的原点将会取平均值。
用ViewportLayerSortMode.INDEX_SORT给viewport 层(ViewportLayer)排序:
var millLayer:ViewportLayer = viewport.getChildLayer(mill);
var floorLayer:ViewportLayer = viewport.getChildLayer(floor);
millLayer.layerIndex = 1;
floorLayer.layerIndex = 2;
viewport.containerSprite.sortMode = ViewportLayerSortMode.INDEX_SORT;
不仅viewport支持viewport层(ViewportLayer),viewport 层他们自己也可以嵌套层(即层中再包含层)
四叉树渲染引擎(Quadtree rendering):
实例化QuadrantRenderEngine只需要一个参数 该参数指定你要选择哪种排序的方法。这些方法是QuadrantRenderEngine类的静态常量。
QuadrantRenderEngine.CORRECT_Z_FILTER: :为不交叉的平面进行正确的排序。
QuadrantRenderEngine.QUAD_SPLIT_FILTER: 为交叉的平面进行排序,引擎将会自动的分割交叉的平面,然后进行正确的排序。
QuadrantRenderEngine.ALL_FILTERS:这个是上面两种类型的组合。它是默认值(即不传参数时默认是它)
我们可以不让3D对象不被四叉树渲染引擎(Quadtree rendering)进行渲染,因而该3D对象将会被默认的BasicRenderEngine引擎进行渲染。 我们可以用设置3D对象的testQuad属性为false来实现。
mill.getChildByName("Blades",true).testQuad = false;
mill.getChildByName("Stand",true).testQuad = false;
mill.getChildByName("House",true).testQuad = false;
因为直接设置mill.testQuad = false;对子对对象不会产生任何影响.
十、粒子
一个粒子可以被描述为一个二维图形,可以相对于一个3D点缩放和定位。
可以设想为一个轻量级的平面,始终面对着摄像头。
这种用面对摄像机的2D图形代替3D物体的技术就叫告示牌。
var particles:Particles = new Particles();
scene.addChild(particles);
for(var i:uint = 0; i 3D矢量绘图和3D文本
使用Letter3DMaterial创建材质
material = new Letter3DMaterial(0×000000);
这个类的构造函数有两个传入参数:
fillColor uint 0xFF00FF 定义材质的颜色,使用24位的 十六进制的数字,同时定义文本颜色也是使用该类数字
fillAlpha Number 1 设置材质的透明度
你也可以定义文本的外观。
通常是如下三属性--linethickness, linealpha, 与linecolor
text3D.material.lineThickness = 2;
text3D.material.lineAlpha = 1;
text3D.material.lineColor = 0xFF0000;
建立一个Font3D实例
Papervision3D拥有四个可选择的字体:
l HelveticaBold
l HelveticaLight
l HelveticaMedium
l HelveticaRoman
建立一个Text3D实例,传入文本,字体,材质参数,并将其添加到场景或其它的do3D。
text3D = new Text3D(text,font3D,material);
1 text String - 定义你想显示的文本
2 font Font3D - 设置文本的字体
3 material Letter3DMaterial - 设置字体材质
4 name String null Text3D实例名,可选
Text3D拥有更多的属性可选择,如下是它额外提供的参数:
l align:left、right、center
l letterSpacing:每个字符的间距
l lineSpacing:用于定义文本的垂直距离
创建字体:Five3D
添加交互性到3D矢量文本和3D矢量图形:
在init()方法的首行添加:
VectorShapeHitTest.instance.assignViewport(viewpor t);
添加交互性到3D文本:
我们添加监听器到Text3D实例的每一个字母。
for each(var letter:VectorLetter3D in text3D.letters)
{
letter.addEventListener(InteractiveScene3DEvent.OB JECT_OVER,overLetterListener);
letter.addEventListener(InteractiveScene3DEvent.OB JECT_OUT,outLetterListener);
}
Text3D拥有一个letters的属性,其是一个数组,储存着文本的各个字母。
绘制矢量图形-线条,圆,与矩形:
它也提供名为VectorShape3D类,用其绘制基本的矢量图
形,比如线条,圆,矩形。矢量图最初以2D形态绘制,然后放入3D空间。
var line:VectorShape3D = new VectorShape3D(material);
line.graphics.lineStyle(2,0×00CCFF);
line.graphics.beginFill(0×666699)
line.graphics.moveTo(-300,-300);
line.graphics.lineTo(300,-300);
scene.addChild(line);
使用Lines3D库绘制线条:
Lines3D使用Vertex3D建立线条的起点和终点
var blueMaterial:LineMaterial = new LineMaterial(0×0000FF);
1 color Number 0xFF0000 定义线的颜色,使用24位十六进制表示颜色数值
2 alpha Number 1 设定材料的透明度
lines = new Lines3D();
scene.addChild(lines);
var blueLine:Line3D = new Line3D(lines,blueMaterial,3,v0,v1);
1 instance Lines3D - Lines3D实例会持有以及渲染线
2 material LineMaterial - 针对线的材质
3 size Number - 线的宽度(粗细)
4 vertex0 Vertex3D - 起点,它是线的起点的3D坐标
5 vertex1 Vertex3D - 终点,它是线的终点的3D坐标
lines.addLine(blueLine);
曲线:
redLine.addControlVertex(-150,-300,0);
使用addNewLine()方法添加新线:
lines.addNewLine(5,0,0,0,300,0,300);
1 size Number - 线的宽度
2 x0 Number - 起点的X坐标
3 y0 Number - 起点的Y坐标
4 z0 Number - 起点的Z坐标
5 x1 Number - 终点的X坐标
6 y1 Number - 终点的Y坐标
7 z1 Number - 终点的Z坐标
建立分割的线:
lines.addNewSegmentedLine(3,8,300,0,300,600,0,0);
1 size Number - 线的宽度
2 segments Number - 分段数
3 x0 Number - 起点的X坐标
4 y0 Number - 起点的Y坐标
5 z0 Number - 起点的Z坐标
6 x1 Number - 终点的X坐标
7 y1 Number - 终点的Y坐标
8 z1 Number - 终点的Z坐标
增加交互到Lines3D的线:
var material:LineMaterial = new LineMaterial(0×000000,0.6);
material.interactive = true;
十三、性能的优化
var stats:StatsView = new StatsView(renderer);
addChild(stats);
FPS:Frames Per Second。每秒显示帧
每秒显示帧取决于最后的渲染需要的时间。
Tri: Triangles. 三角形
最后渲染时渲染的三角形数量。
Sha: Shaders. 着色器
最后渲染时渲染的已着色的三角形。已着色的三角形不计入 Tri 值。
Lin: Lines. 直线
最后渲染时渲染的直线的数量。
Par: Particles. 微粒
最后渲染时渲染的微粒的数量。
Ren: Rendered. 已渲染的
此值尚未使用。
RT: Render Time. 渲染时间
最后的渲染所需要的时间,以毫秒计算。
PT: Projection Time. 投影时间
投影最后渲染需要的时间,以毫秒计算。
COb: Culled Objects. 剔除的物体
最后渲染时剔除物体的数量。所以真正剔除对象的数量是这个数字除以 2。
CTr: Culled Triangles. 剔除的三角形
最后的渲染剔除的三角形的数量。
CPa : Culled Particles. 剔除的粒子
最后渲染时剔除的粒子的数量。
FOb: Filtered Objects. 过滤的物体
添加到 viewport 的 viewportObjectFilter 属性的物体的数量。
Mem: Memory. 内存
Flashplayer 和浏览器使用的内存
Poly count: Polygon Count.多边形的数量
只有当你调用 statsview 里的 updatePolyCount()方法时才会更新此值。
例如: stats.updatePolyCount(scene);
Stage quality 舞台品质:stage.quality = StageQuality.LOW;
StageQuality.BEST:图像是反锯齿的,使用了 4 乘 4 像素栅格,可以使 bitmap 很平滑。
StageQuality.HIGH:图像是反锯齿的,使用了 4 乘 4 像素栅格,如果 movie 是静态的,
可以使 bitmap 很平滑。一般地,stage 的品质都设置成这个值。
StageQuality.MEDIUM:图像是反锯齿的,使用了 2 乘 2 像素栅格,这不会使 bitmap
平滑并对文本阅读有点影响,对动画会有一点优化。
StageQuality.LOW:图像不是反锯齿的,bitmap 也不平滑。对文本的显示和动画的优化
有很大的影响。
其它普通的Flash优化:
在你不需要 event 监听器时,将它移除。
删除不需要的对象:
当你不再需要 scene 中子对象时,应将它删除并且将它的引用和 material 删除。
scene.removeChild(do3d);
do3d.material.destroy();
do3d = null;
Viewport 大小
镜头视域
剔除:
Camera.useCulling = true;
将 useCulling 设为 true,所有在视域外的物体都会被忽略,这会节省性能。
优化 material:
透明度:没有透明度的图片渲染得更快
tiled:永远试着把一个 material 的 tiled 属性设成 true。根据你的情况这会轻微地提高了性能(这是 Flash 播放器存在的一性能的 bug)。
双纹理的作用:双纹理的作用是用来 mipmapping(映射),它允许你将 material 的 smoothing 设为 true而又不丢失性能。
BitmapMaterial.AUTO_MIP_MAPPING = true;
当你设为这样的时候,Papervision3D 会更正所有新建的 bitmap material。缺点就是它会比把这个属性设为 false 的时候要用更多一点内存。
material 的大小:将 material 的宽和高尽量做小。
将在 object 背后的其它 object 删除:
动态设置far plane
Level of detail(细节的级别):
Papervision3D 内建了对 LOD 的支持,但它不支持 do3D 之间的窜改。这个没有窜改功能的技术叫做 simple level of detail(细节的简单级别),或者是 SLOD。
slod = new SimpleLevelOfDetail(spheres,600,2000);
scene.addChild(slod);
实例化 SimpleLevelOfDetail 类需要 4 个参数:
object Array -一个包含 3D object 的数组。数组的第一个元素代表最近而且拥有最多 detail 的,最后的元素是最远而且最简单的 3D object。
MinDepth Number 1000定义最近而且最多 detail 的 object 应该使用的距离。
MaxDepth Number 10000定义最远而且最少 detail 的 object 应该使用的距离。
Distances Array null一个用来定义 object 数组中每个 object深度值的一个 integer 数组。默认设为 null。如果提供了该数组,它会替换 minDepth 和 maxDepth 参数。
渲染的优化:
只有在需要的时候渲染:可以使用 stopRendering() 方法停止渲染器。也可以用startRendering() 方法开始渲染。
选择性地渲染:
renderer.renderLayers(scene,camera,viewport,[layer Reference]);
第 4 个参数,这个参数是一个包含了需要被渲染的viewport 的层的数组。
十四、其他技巧
获取一个大致的三维空间的二维鼠标位置:
var vMouse: virtualMouse= viewport.interactiveSceneManager.virtualMouse;
……
public function onMove(evt:InteractiveScene3DEvent):void {
if (InteractiveSceneManager.MOUSE_IS_DOWN) {
mc1.graphics.beginFill(0xBBBBBB,1);
mc1.graphics.drawCircle(vMouse.x, vMouse.y, 10);
mc1.graphics.endFill();
}
}
计算鼠标到目标物体之间的屏幕距离:
sphere.autoCalcScreenCoords = true; //允许do3d自动计算自身的屏幕坐标
var distanceX:Number = viewport.containerSprite.mouseX sphere.screen.x;
var distanceY:Number = viewport.containerSprite.mouseY sphere.screen.y;
var distance:Number = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
// var distance:Number = Number3D.sub(distanceX, distanceY).modulo;
// modulo 取模
父对象的应用:
private function onButtonClick(evt:MouseEvent):void {
if (sphere1.parent==do3d1) {
do3d1.removeChild(sphere1);
do3d2.addChild(sphere1);
} else {
do3d2.removeChild(sphere1);
do3d1.addChild(sphere1);
}
}
鼠标获取空间坐标的基础 & 射线和平面相交:
Pv3d里面描述一个无限大的三维平面,及其相关运算,是使用Plane3D这个类
var plane3d:Plane3D=new Plane3D();
plane3d.setNormalAndPoint(法线向量,点);
然后用方法setNormalAndPoint()来描述这个平面,它必须接受两个参数:
1 法线向量,是一个三维数Number3D,比如一个XZ的平面的法线描述方法就是new Number3D(0,1,0)
2平面上的某个点,也是一个三维数Number3D,比如原点坐标就是new Number3D(0,0,0);
private var plane3D:Plane3D = new Plane3D(XZPLANE, Number3D.ZERO);
var ray:Number3D = camera.unproject(viewport.containerSprite.mouseX, viewport.containerSprite.mouseY);
ray = Number3D.add(ray, camera.position);
var cameraVertex3D:Vertex3D = new Vertex3D(camera.x, camera.y, camera.z);
var rayVertex3D:Vertex3D = new Vertex3D(ray.x, ray.y, ray.z);
var intersectPoint:Vertex3D = plane3D.getIntersectionLine(cameraVertex3D, rayVertex3D);
之后返回来的intersectPoint这个三维数的intersectPoint.x, intersectPoint.y, intersectPoint.z既是我们鼠标点击在这个平面plane3d上反映出来的大概空间位置了。
将viewport作为遮罩层:
rover = new roverAsset() as Bitmap;
rover.mask = viewport;
addChild(rover);
&
exposedViewport.containerSprite.mask = portalViewport; // 两个viewport之间的遮罩
循环获取父对象的所有子对象:
for each(var sphere:Sphere in exposedSphere.children)
{
viewportLayer2.addDisplayObject3D(sphere); // 给viewportLayer添加对象
}
创建自己的网格三角面TriangleMesh3D:
var material:ColorMaterial = new ColorMaterial(0xcc0000);
material.doubleSided = true;
//创建三角面的三个顶点
var vertex3D_1:Vertex3D = new Vertex3D(-200, -200, 0);
var vertex3D_2:Vertex3D = new Vertex3D(200, -200, 0);
var vertex3D_3:Vertex3D = new Vertex3D(-200, 200, 0);
var triangleVertices:Array = [vertex3D_1, vertex3D_2, vertex3D_3];
//use null because we haven't created its parent mesh yet
var triangle3d:Triangle3D = new Triangle3D(null, triangleVertices, material);
var triangleFaces:Array = [triangle3d];
triangleMesh3d = new TriangleMesh3D(material, triangleVertices, triangleFaces, null);
triangle3d.instance = triangleMesh3d;
scene.addChild(triangleMesh3d);
四元组Quaternion和Slerp的应用:
Quaternion的四个值分别是轴向量(x,y,z)和沿该轴的转动角度,但其中不包括坐标位置的数据;
Slerp是0-1范围内的一个数,表示两个四元组之间的某个状态。
quaternion = Quaternion.slerp(startQuaternion, endQuaternion, slerp);
// 初始化四元组
planeQuat = Quaternion.createFromMatrix(plane.transform);
mouseQuat = Quaternion.createFromMatrix(mouse3D.transform);
// 逐帧变化
slerp += (1 slerp) * .1;
slerpQuat = Quaternion.slerp(planeQuat, mouseQuat, slerp);
plane.transform = slerpQuat.matrix;
plane.moveBackward(RADIUS);
renderer.renderScene(scene, camera, viewport);