V3d
2010-02-25 17:54 宝宝合凤凰 阅读(604) 评论(0) 收藏 举报Papervision3D GreatWhite 2.0基础教程:第七篇 – 高级
这篇文章是对上一篇的关于贴图的进阶,在这篇文章里不光要介绍如何给物体贴图,还要加上光和阴影,这让场景看上去更加真实。
前面在讲阴影的时候,我们是指定了一些阴影材质的,比如GouraudMaterial 和PhongMaterial 。事实上,在pv3d的源代码package org.papervision3d.materials.shadematerials 里有更多的阴影材质,但是他们都是基于纯色的阴影效果而不是位图的的阴影效果。
要将位图和阴影效果叠加到一起,我们就需要制作一个Shader 和一个BitmapMaterial 用ShadedMaterial来将他们混合到一起。用Papervision3D GreatWhite来实现这个特性是十分简单的。
关于Shader在pv3d的源代码package org.papervision3d.materials.shaders里有很多,包括FlatShader, GouraudShader, PhongShader, CellShader 和EnvMapShader。这里的最后一种EnvMapShader允许为我们为光源添加一张位图,用于环境反光的效果。
关于BitmapMaterial 在第上一篇文章里就有介绍。
另外在Papervision3D的世界里还有一种特性叫纹理(bump mapping),纹理可以让光照物体的同时产生更加真实的反光效果。在Papervision3D 里,纹理可以作用在Phong 和environment 两种阴影效果上。
下面我们继续拿之前的代码做例子,为了方便理解不会有太大的修改。但是为了便于显示各种材质的效果,我们只放一个球在场景中,当点击球体就会自动换一种材质(渲染中实时更换材质也是Papervision3D的一个特性之一)
package {
  import flash.display.Bitmap;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import org.papervision3d.events.InteractiveScene3DEvent;
  import org.papervision3d.lights.PointLight3D;
  import org.papervision3d.materials.BitmapMaterial;
  import org.papervision3d.materials.shaders.CellShader;
  import org.papervision3d.materials.shaders.EnvMapShader;
  import org.papervision3d.materials.shaders.FlatShader;
  import org.papervision3d.materials.shaders.GouraudShader;
  import org.papervision3d.materials.shaders.PhongShader;
  import org.papervision3d.materials.shaders.ShadedMaterial;
  import org.papervision3d.materials.shaders.Shader;
  import org.papervision3d.objects.DisplayObject3D;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.view.BasicView;
  public class Example007 extends BasicView {
    [Embed(source="/../assets/pv3d.png")] private var Pv3dBitmapImage:Class;
    [Embed(source="/../assets/randomBump.png")] private var BumpImage:Class;
    [Embed(source="/../assets/mountains.png")] private var EnvImage:Class;
    private var pv3dBitmap:Bitmap = new Pv3dBitmapImage();
    private var bumpMap:Bitmap = new BumpImage();
    private var envMap:Bitmap = new EnvImage();
    private var bitmapMaterial:BitmapMaterial;
    private var sphere:Sphere;
    private var light:PointLight3D;
    private var doRotation:Boolean = false;
    private var lastMouseX:int;
    private var lastMouseY:int;
    private var cameraPitch:Number = 60;
    private var cameraYaw:Number = -60;
    private var shaders:Array = ["flat", "cell", "gouraud", "phong", "phongBump", "env", "envBump"];
    private var shaderIndex:int = 0;
    private var shaderText:TextField;
    private var textFormat:TextFormat;
    public function Example007() {
      super(0, 0, true, true);
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Listen to mouse up and down events on the stage
      stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
      stage.addChild(shaderText);
      // Start rendering the scene
      startRendering();
    }
    private function init3D():void {
      // position the camera
      camera.z = -500;
      camera.orbit(60, -60);
    }
    private function createScene():void {
      // create text and format to display current shader type
      textFormat = new TextFormat();
      textFormat.size = 20;
      textFormat.font = "Arial";
      shaderText = new TextField();
      shaderText.x = 50;
      shaderText.y = 50;
      shaderText.textColor = 0xFFFFFF;
      shaderText.text = "flat";
      shaderText.setTextFormat(textFormat);
      shaderText.autoSize = TextFieldAutoSize.LEFT;
      // Specify a point light source and its location
      light = new PointLight3D(true);
      light.x = 500;
      light.y = 500;
      light.z = -200;
      // create bitmap material with smoothing
      bitmapMaterial = new BitmapMaterial(pv3dBitmap.bitmapData, false);
      bitmapMaterial.smooth = true;
      // create sphere
      sphere = new Sphere(getShadedBitmapMaterial(bitmapMaterial, "flat"), 150, 20, 20);
      // Add a listener to the spheres to listen to InteractiveScene3DEvent events
      sphere.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnObject);
      // Add the light and sphere to the scene
      scene.addChild(sphere);
      scene.addChild(light);
    }
    private function getShadedBitmapMaterial(bitmapMaterial:BitmapMaterial, shaderType:String):ShadedMaterial {
      var shader:Shader;
      if (shaderType == "flat") {
        // create new flat shader
        shader = new FlatShader(light, 0xFFFFFF, 0x333333);
      } else if (shaderType == "cell") {
        // create new cell shader with 5 colour levels
        shader = new CellShader(light, 0xFFFFFF, 0x333333, 5);
      } else if (shaderType == "gouraud") {
        // create new gouraud shader
        shader = new GouraudShader(light, 0xFFFFFF, 0x333333);
      } else if (shaderType == "phong") {
        // create new phong shader
        shader = new PhongShader(light, 0xFFFFFF, 0x333333, 50);
      } else if (shaderType == "phongBump") {
        // create new phong shader with bump map
        shader = new PhongShader(light, 0xFFFFFF, 0x333333, 50, bumpMap.bitmapData);
      } else if (shaderType == "env") {
        // create new environment map shader
        shader = new EnvMapShader(light, envMap.bitmapData, envMap.bitmapData, 0x333333);
      } else if (shaderType == "envBump") {
        // create new environment map shader with bump map
        shader = new EnvMapShader(light, envMap.bitmapData, envMap.bitmapData, 0x333333, bumpMap.bitmapData);
      }
      // create new shaded material by combining the bitmap material with shader
      var shadedMaterial:ShadedMaterial =  new ShadedMaterial(bitmapMaterial, shader);
      shadedMaterial.interactive = true;
      return shadedMaterial;
    }
    override protected function onRenderTick(event:Event=null):void {
      // rotate the sphere
      sphere.yaw(-1);
      // If the mouse button has been clicked then update the camera position
      if (doRotation) {
        // convert the change in mouse position into a change in camera angle
        var dPitch:Number = (mouseY - lastMouseY) / 2;
        var dYaw:Number = (mouseX - lastMouseX) / 2;
        // update the camera angles
        cameraPitch -= dPitch;
        cameraYaw -= dYaw;
        // limit the pitch of the camera
        if (cameraPitch < = 0) {
          cameraPitch = 0.1;
        } else if (cameraPitch >= 180) {
          cameraPitch = 179.9;
        }
        // reset the last mouse position
        lastMouseX = mouseX;
        lastMouseY = mouseY;
        // reposition the camera
        camera.orbit(cameraPitch, cameraYaw);
      }
      // call the renderer
      super.onRenderTick(event);
    }
    // called when mouse down on stage
    private function onMouseDown(event:MouseEvent):void {
      doRotation = true;
      lastMouseX = event.stageX;
      lastMouseY = event.stageY;
    }
    // called when mouse up on stage
    private function onMouseUp(event:MouseEvent):void {
      doRotation = false;
    }
    // called when mouse down on a sphere
    private function onMouseDownOnObject(event:InteractiveScene3DEvent):void {
      var object:DisplayObject3D = event.displayObject3D;
      // calculate index of next shader
      shaderIndex++;
      if (shaderIndex == shaders.length) {
        shaderIndex = 0;
      }
      // dynamically modify the material of the object and update text
      object.material = getShadedBitmapMaterial(bitmapMaterial, shaders[shaderIndex]);
      shaderText.text = shaders[shaderIndex];
      shaderText.setTextFormat(textFormat);
    }
  }
}
代码执行后可以看到一个球体,每当点击一次球体就会自动更换一种混合了位图的反光材质(点击图片看效果)
和之前的代码做比较,初始化3D场景的时候没什么大变化,不同的是我们新加了3张图,下面的代码是将他们转换成Bitmap 的方法。(这里提供的代码仍然是flex下输出用的,使用flash的朋友还是要自行处理自己的图)
    [Embed(source="/../assets/pv3d.png")] private var Pv3dBitmapImage:Class;
    [Embed(source="/../assets/randomBump.png")] private var BumpImage:Class;
    [Embed(source="/../assets/mountains.png")] private var EnvImage:Class;
    private var pv3dBitmap:Bitmap = new Pv3dBitmapImage();
    private var bumpMap:Bitmap = new BumpImage();
    private var envMap:Bitmap = new EnvImage();
下面3张图都是我自己找的,有需要的你可以替换成自己想要的图。
被修改的主要代码在createScene 方法里,加了一个文本字段用来显示当前的贴图类型
      // create text and format to display current shader type
      textFormat = new TextFormat();
      textFormat.size = 20;
      textFormat.font = "Arial";
      shaderText = new TextField();
      shaderText.x = 50;
      shaderText.y = 50;
      shaderText.textColor = 0xFFFFFF;
      shaderText.text = "flat";
      shaderText.setTextFormat(textFormat);
      shaderText.autoSize = TextFieldAutoSize.LEFT;
加上上一篇例子里拿掉的光源,并关联到每一个shader里
      // Specify a point light source and its location
      light = new PointLight3D(true);
      light.x = 500;
      light.y = 500;
      light.z = -200;
加上位图材质,为了获得更好的效果,我们将smooth属性设为true
      // create bitmap material with smoothing
      bitmapMaterial = new BitmapMaterial(pv3dBitmap.bitmapData, false);
      bitmapMaterial.smooth = true;
下面的球体我们用了一个getShadedBitmapMaterial 方法来获得默认的带阴影的贴图材质,并添加一个事件监听器用来更换贴图材质。
      // create sphere
      sphere = new Sphere(getShadedBitmapMaterial(bitmapMaterial, "flat"), 150, 20, 20);
      // Add a listener to the spheres to listen to InteractiveScene3DEvent events
      sphere.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnObject);
getShadedBitmapMaterial 方法是用来将位图材质和阴影混合到一起的方法以返回一个ShadedMaterial
    private function getShadedBitmapMaterial(bitmapMaterial:BitmapMaterial, shaderType:String):ShadedMaterial {
      var shader:Shader;
      if (shaderType == "flat") {
        // create new flat shader
        shader = new FlatShader(light, 0xFFFFFF, 0x333333);
      } else if (shaderType == "cell") {
        // create new cell shader with 5 colour levels
        shader = new CellShader(light, 0xFFFFFF, 0x333333, 5);
      } else if (shaderType == "gouraud") {
        // create new gouraud shader
        shader = new GouraudShader(light, 0xFFFFFF, 0x333333);
      } else if (shaderType == "phong") {
        // create new phong shader
        shader = new PhongShader(light, 0xFFFFFF, 0x333333, 50);
      } else if (shaderType == "phongBump") {
        // create new phong shader with bump map
        shader = new PhongShader(light, 0xFFFFFF, 0x333333, 50, bumpMap.bitmapData);
      } else if (shaderType == "env") {
        // create new environment map shader
        shader = new EnvMapShader(light, envMap.bitmapData, envMap.bitmapData, 0x333333);
      } else if (shaderType == "envBump") {
        // create new environment map shader with bump map
        shader = new EnvMapShader(light, envMap.bitmapData, envMap.bitmapData, 0x333333, bumpMap.bitmapData);
      }
      // create new shaded material by combining the bitmap material with shader
      var shadedMaterial:ShadedMaterial =  new ShadedMaterial(bitmapMaterial, shader);
      shadedMaterial.interactive = true;
      return shadedMaterial;
    }
getShadedBitmapMaterial 方法让我们可以很简单的通过指定shaderType:(flat,cell,gouraud,phong,phongBump,env,envBump)来生成各种混合贴图材质。其中的phong和env因为可以使用纹理,所以都各自多了一个phongBump和envBump。从代码中可以看到混合过程是十分简单的,指定好变量就可以。
多的这个onMouseDownOnObject是用来动态更换球体的贴图用的。
    // called when mouse down on a sphere
    private function onMouseDownOnObject(event:InteractiveScene3DEvent):void {
      var object:DisplayObject3D = event.displayObject3D;
      // calculate index of next shader
      shaderIndex++;
      if (shaderIndex == shaders.length) {
        shaderIndex = 0;
      }
      // dynamically modify the material of the object and update text
      object.material = getShadedBitmapMaterial(bitmapMaterial, shaders[shaderIndex]);
      shaderText.text = shaders[shaderIndex];
      shaderText.setTextFormat(textFormat);
    }
动态更换贴图只需要重新指定3D Object的material 属性就可以,很方便。shaderIndex只是为了切换贴图的时候产生一个循环。
很高兴写完了这篇文章,拖了很长时间了。
Papervision3D GreatWhite 2.0基础教程:第六篇 – 贴图
在这篇文章里终于要开始介绍贴图了,前几篇的例子中大家可以看到球体都是纯色的,只不过换上了不同的阴影材质而看上去不同。现在如果给他们贴上位图将会使效果看上去更棒。
好,这篇文章仍然以介绍位图贴图的基础方法为主,暂时还不会将的太深,所以不会涉及到光影在位图贴图中的应用。
下面的代码仍然用前篇的例子修改而来,不过我会把光源和2个球体拿掉,换成2个球2个方块。
Example006 
package {
  import caurina.transitions.Tweener;
  import flash.display.Bitmap;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import org.papervision3d.events.InteractiveScene3DEvent;
  import org.papervision3d.materials.BitmapMaterial;
  import org.papervision3d.materials.utils.MaterialsList;
  import org.papervision3d.objects.DisplayObject3D;
  import org.papervision3d.objects.primitives.Cube;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.view.BasicView;
  public class Example006 extends BasicView {
    [Embed(source="/../assets/pv3d.png")] private var MyTextureImage:Class;
    private static const ORBITAL_RADIUS:Number = 200;
    private var bitmap:Bitmap = new MyTextureImage();
    private var cube1:Cube;
    private var cube2:Cube;
    private var sphere1:Sphere;
    private var sphere2:Sphere;
    private var objectGroup:DisplayObject3D;
    private var doRotation:Boolean = false;
    private var lastMouseX:int;
    private var lastMouseY:int;
    private var cameraPitch:Number = 60;
    private var cameraYaw:Number = -60;
    public function Example006() {
      super(0, 0, true, true);
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Listen to mouse up and down events on the stage
      stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
      // Start rendering the scene
      startRendering();
    }
    private function init3D():void {
      // position the camera
      camera.z = -500;
      camera.orbit(60, -60);
    }
    private function createScene():void {
      // create interactive bitmap material
      var bitmapMaterial:BitmapMaterial = new BitmapMaterial(bitmap.bitmapData, false);
      bitmapMaterial.interactive = true;
      // create an interactive tiled bitmap material (bitmap tiled as 2 x 2)
      var tiledBitmapMaterial:BitmapMaterial = new BitmapMaterial(bitmap.bitmapData, false);
      tiledBitmapMaterial.interactive = true;
      tiledBitmapMaterial.tiled = true;
      tiledBitmapMaterial.maxU = 2;
      tiledBitmapMaterial.maxV = 2;
      // create cube with simple bitmap material
      cube1 = new Cube(getBitmapMaterials(bitmapMaterial), 100, 100, 100);
      cube1.x =  ORBITAL_RADIUS;
      // create cube with tiled bitmap material
      cube2 = new Cube(getBitmapMaterials(tiledBitmapMaterial), 100, 100, 100);
      cube2.x = -ORBITAL_RADIUS;
      // create sphere with simple bitmap material
      sphere1 = new Sphere(bitmapMaterial, 50, 10, 10);
      sphere1.z =  ORBITAL_RADIUS;
      // create sphere with tiled bitmap material
      sphere2 = new Sphere(tiledBitmapMaterial, 50, 10, 10);
      sphere2.z = -ORBITAL_RADIUS;
      // Create a 3D object to group the spheres
      objectGroup = new DisplayObject3D();
      objectGroup.addChild(cube1);
      objectGroup.addChild(cube2);
      objectGroup.addChild(sphere1);
      objectGroup.addChild(sphere2);
      // Add a listener to each of the spheres to listen to InteractiveScene3DEvent events
      cube1.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnObject);
      cube2.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnObject);
      sphere1.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnObject);
      sphere2.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnObject);
      // Add the light and spheres to the scene
      scene.addChild(objectGroup);
    }
    private function getBitmapMaterials(bitmapMaterial:BitmapMaterial):MaterialsList {
      // create list of materials for all faces of the cube,
      //    all with the same bitmap material
      var materials:MaterialsList = new MaterialsList();
      materials.addMaterial(bitmapMaterial, "all");
      return materials;
    }
    override protected function onRenderTick(event:Event=null):void {
      // rotate the objects
      cube1.yaw(-3);
      cube2.yaw(-3);
      sphere1.yaw(-3);
      sphere2.yaw(-3);
      // rotate the group of objects
      objectGroup.yaw(1);
      // If the mouse button has been clicked then update the camera position
      if (doRotation) {
        // convert the change in mouse position into a change in camera angle
        var dPitch:Number = (mouseY - lastMouseY) / 2;
        var dYaw:Number = (mouseX - lastMouseX) / 2;
        // update the camera angles
        cameraPitch -= dPitch;
        cameraYaw -= dYaw;
        // limit the pitch of the camera
        if (cameraPitch < = 0) {
          cameraPitch = 0.1;
        } else if (cameraPitch >= 180) {
          cameraPitch = 179.9;
        }
        // reset the last mouse position
        lastMouseX = mouseX;
        lastMouseY = mouseY;
        // reposition the camera
        camera.orbit(cameraPitch, cameraYaw);
      }
      // call the renderer
      super.onRenderTick(event);
    }
    // called when mouse down on stage
    private function onMouseDown(event:MouseEvent):void {
      doRotation = true;
      lastMouseX = event.stageX;
      lastMouseY = event.stageY;
    }
    // called when mouse up on stage
    private function onMouseUp(event:MouseEvent):void {
      doRotation = false;
    }
    // called when mouse down on a sphere
    private function onMouseDownOnObject(event:InteractiveScene3DEvent):void {
      var object:DisplayObject3D = event.displayObject3D;
      Tweener.addTween(object, {y:200, time:1, transition:"easeOutSine", onComplete:function():void {goBack(object);} });
    }
    // called when a tween created in onMouseDownOnObject has terminated
    private function goBack(object:DisplayObject3D):void {
      Tweener.addTween(object, {y:0, time:2, transition:"easeOutBounce"});
    }
  }
}
当代码执行以后,你会看到2个球体2个方块,和上篇的例子一样你可以用鼠标控制镜头转动,点里面的物体可以让他跳起来,不同之处在于他们都贴上了位图。(点击下面图片看效果)
[Embed(source="/../assets/pv3d.png")] private var MyTextureImage:Class;
指定一张png用来做做贴图(这里是flex输出时使用的方法,如果用flash还要自行准备一个Bitmap)
private var bitmap:Bitmap = new MyTextureImage();
将我们指定的png转换成Bitmap ,这里指定的图你也换一张自己的图来看看效果。
下面内容和前几篇基本都是一样的了,3D场景的初始化,事件的添加都是一样,所以这里不再解释这些了。主要的变化来自createScene 方法。
这里我们使用了BitmapMaterials 来给物体贴图,这样就可以使用我们指定的图片来显示在3D物体上。
// create interactive bitmap material var bitmapMaterial:BitmapMaterial = new BitmapMaterial(bitmap.bitmapData, false); bitmapMaterial.interactive = true
注意BitmapMaterial所需的第一参数是BitmapData而非Bitmap。第2个参数指示是否高精度的意思,默认是false,你可以修改这个值看它的效果。使用位图贴图本身是件很占用CPU的事情,如果再开启高精度会更加重CPU的负担,这里我们使用false,是为了更高的运行效率。这里有一个例子可以看看他们之中的差异:http://www.papervision3d.org/demos/LinearMapping/
下面一个BitmapMaterial为了演示网格贴图,调整了他的参数设置。
// create an interactive tiled bitmap material (bitmap tiled as 2 x 2) var tiledBitmapMaterial:BitmapMaterial = new BitmapMaterial(bitmap.bitmapData, false); tiledBitmapMaterial.interactive = true; tiledBitmapMaterial.tiled = true; tiledBitmapMaterial.maxU = 2; tiledBitmapMaterial.maxV = 2;
网格贴图可以让一块贴图去重复应用,对用来贴有大量重复图案的物体,比如墙纸,街道是非常方便的应用,而且节省效率。设置也很方便,tiled 为true,maxU和maxV 设为2就可以创建一个2*2的网格了。
将做好的贴图贴到3D物体上,和使用阴影贴图一样方便。
// create cube with simple bitmap material cube1 = new Cube(getBitmapMaterials(bitmapMaterial), 100, 100, 100); cube1.x = ORBITAL_RADIUS; // create cube with tiled bitmap material cube2 = new Cube(getBitmapMaterials(tiledBitmapMaterial), 100, 100, 100); cube2.x = -ORBITAL_RADIUS; // create sphere with simple bitmap material sphere1 = new Sphere(bitmapMaterial, 50, 10, 10); sphere1.z = ORBITAL_RADIUS; // create sphere with tiled bitmap material sphere2 = new Sphere(tiledBitmapMaterial, 50, 10, 10); sphere2.z = -ORBITAL_RADIUS;
事实上,这里的方块贴图是第一次出现在这系教程里,他的贴图是有一点不同于其他物体的,因为方块有6个面,所以我们要用MaterialsList来把各个面的Material和到一起。这里我们用all标签指示所有面用同一个贴图。
private function getBitmapMaterials(bitmapMaterial:BitmapMaterial):MaterialsList {
      // create list of materials for all faces of the cube,
      //    all with the same bitmap material
      var materials:MaterialsList = new MaterialsList();
      materials.addMaterial(bitmapMaterial, "all");
      return materials;
}
你也可以为各个面指定不同的贴图,使用“front”, “back”, “top”, “bottom”, “left” 和“right”这6个标签。
关于Papervision3D 的贴图其实还有很多种,可以用视频来贴,可以直接通过一张图的url来贴,可以使用movieclip来贴,等等,试试其他贴图方式吧,他们都很简单,阅读一下帮助手册就知道了。
下一篇要介绍如何给贴图加上光影效果。
Papervision3D GreatWhite 2.0基础教程:第五篇 – 交互
在这篇文章里,要讲的不是给3D场景添加更多的视觉效果,而是要介绍一下如何在Papervision3D 里做一个的简单的交互,在Papervision3D 里的每一个多边形都可以响应鼠标事件去执行方法函数。而这一切在Papervision3D 里都是很简单的。
下面要看的代码很简单,修改仍然是以上篇文章为基础:4个围绕原点转动的球体。在这里会额外添加2个事件监听器:一个普通的flash鼠标事件监听器,加在stage上用来控制摄象机的位置以转动镜头。一个Papervision3D 内置的InteractiveScene3DEvent 监听器,加在3D场景里的DisplayObject3D 上,使DisplayObject3D 可以响应鼠标事件,注意这种的用法和不同。
另外这篇文章里还出现了另一个新东西:Tweener。这是个很好的帮手,Tweener可以帮你完成一个物体在指定时间里其内部多种属性值从a变化到b的过程中所需要的计算。Tweener 是个开源的类包,在这里不会过多的介绍它。http://code.google.com/p/tweener/ 在这里可以下载到,里面附带有详细的范例和说明文档。
Example005
package {
  import caurina.transitions.Tweener;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import org.papervision3d.core.proto.MaterialObject3D;
  import org.papervision3d.events.InteractiveScene3DEvent;
  import org.papervision3d.lights.PointLight3D;
  import org.papervision3d.materials.shadematerials.CellMaterial;
  import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
  import org.papervision3d.materials.shadematerials.GouraudMaterial;
  import org.papervision3d.materials.shadematerials.PhongMaterial;
  import org.papervision3d.objects.DisplayObject3D;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.view.BasicView;
  public class Example005 extends BasicView {
    private static const ORBITAL_RADIUS:Number = 200;
    private var sphere1:Sphere;
    private var sphere2:Sphere;
    private var sphere3:Sphere;
    private var sphere4:Sphere;
    private var sphereGroup:DisplayObject3D;
    private var light:PointLight3D;
    private var doRotation:Boolean = false;
    private var lastMouseX:int;
    private var lastMouseY:int;
    private var cameraPitch:Number = 60;
    private var cameraYaw:Number = -60;
    public function Example005() {
      // specify that we want the scene to be interactive
      super(0, 0, true, true);
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Listen to mouse up and down events on the stage
      stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
      // Start rendering the scene
      startRendering();
    }
    private function init3D():void {
      // position the camera
      camera.z = -500;
      camera.orbit(cameraPitch, cameraYaw);
    }
    private function createScene():void {
      // Specify a point light source and its location
      light = new PointLight3D(true);
      light.x = 400;
      light.y = 1000;
      light.z = -400;
      // Create a new material (flat shaded) and make it interactive
      var flatShadedMaterial:MaterialObject3D = new FlatShadeMaterial(light, 0x6654FF, 0x060433);
      flatShadedMaterial.interactive = true;
      sphere1 = new Sphere(flatShadedMaterial, 50, 10, 10);
      sphere1.x = -ORBITAL_RADIUS;
      // Create a new material (flat shaded) and make it interactive
      var gouraudMaterial:MaterialObject3D = new GouraudMaterial(light, 0x6654FF, 0x060433);
      gouraudMaterial.interactive = true;
      sphere2 = new Sphere(gouraudMaterial, 50, 10, 10);
      sphere2.x =  ORBITAL_RADIUS;
      // Create a new material (flat shaded) and make it interactive
      var phongMaterial:MaterialObject3D = new PhongMaterial(light, 0x6654FF, 0x060433, 150);
      phongMaterial.interactive = true;
      sphere3 = new Sphere(phongMaterial, 50, 10, 10);
      sphere3.z = -ORBITAL_RADIUS;
      // Create a new material (flat shaded) and make it interactive
      var cellMaterial:MaterialObject3D = new CellMaterial(light, 0x6654FF, 0x060433, 5);
      cellMaterial.interactive = true;
      sphere4 = new Sphere(cellMaterial, 50, 10, 10);
      sphere4.z =  ORBITAL_RADIUS;
      // Create a 3D object to group the spheres
      sphereGroup = new DisplayObject3D();
      sphereGroup.addChild(sphere1);
      sphereGroup.addChild(sphere2);
      sphereGroup.addChild(sphere3);
      sphereGroup.addChild(sphere4);
      // Add a listener to each of the spheres to listen to InteractiveScene3DEvent events
      sphere1.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere);
      sphere2.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere);
      sphere3.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere);
      sphere4.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere);
      // Add the light and spheres to the scene
      scene.addChild(sphereGroup);
      scene.addChild(light);
    }
    override protected function onRenderTick(event:Event=null):void {
      // rotate the spheres
      sphere1.yaw(-8);
      sphere2.yaw(-8);
      sphere3.yaw(-8);
      sphere4.yaw(-8);
      // rotate the group of spheres
      sphereGroup.yaw(3);
      // If the mouse button has been clicked then update the camera position
      if (doRotation) {
        // convert the change in mouse position into a change in camera angle
        var dPitch:Number = (mouseY - lastMouseY) / 2;
        var dYaw:Number = (mouseX - lastMouseX) / 2;
        // update the camera angles
        cameraPitch -= dPitch;
        cameraYaw -= dYaw;
        // limit the pitch of the camera
        if (cameraPitch < = 0) {
          cameraPitch = 0.1;
        } else if (cameraPitch >= 180) {
          cameraPitch = 179.9;
        }
        // reset the last mouse position
        lastMouseX = mouseX;
        lastMouseY = mouseY;
        // reposition the camera
        camera.orbit(cameraPitch, cameraYaw);
      }
      // call the renderer
      super.onRenderTick(event);
    }
    // called when mouse down on stage
    private function onMouseDown(event:MouseEvent):void {
      doRotation = true;
      lastMouseX = event.stageX;
      lastMouseY = event.stageY;
    }
    // called when mouse up on stage
    private function onMouseUp(event:MouseEvent):void {
      doRotation = false;
    }
    // called when mouse down on a sphere
    private function onMouseDownOnSphere(event:InteractiveScene3DEvent):void {
      var object:DisplayObject3D = event.displayObject3D;
      Tweener.addTween(object, {y:200, time:1, transition:"easeOutSine", onComplete:function():void {goBack(object);} });
    }
    // called when a tween created in onMouseDownOnSphere has terminated
    private function goBack(object:DisplayObject3D):void {
      Tweener.addTween(object, {y:0, time:2, transition:"easeOutBounce"});
    }
  }
}
执行代码后,你会看到4个旋转着的球体,和之前的效果一样。不同的地方在于,当你球体的时候它会跳起来,在空白处按住鼠标拖拽可以移动摄象机的镜头。(点击图片可以看到flash效果)
下面解释一下代码吧,看看2种交互有何不同。
Stage 交互控制转动摄象机镜头
查看构造函数里发现我们添加了几个普通的Flash 鼠标事件监听器,用来控制stage和鼠标的交互,使摄象机可以围绕原点从各个方位进行拍摄。
// Listen to mouse up and down events on the stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
当鼠标在stage上做一个click的动作时,会先后调用onMouseDown 和 onMouseUp两个方法。这2个指定了一个布尔值来表示用户是否希望旋转摄象机镜头。
// called when mouse down on stage
private function onMouseDown(event:MouseEvent):void {
      doRotation = true;
      lastMouseX = event.stageX;
      lastMouseY = event.stageY;
}
// called when mouse up on stage
private function onMouseUp(event:MouseEvent):void {
      doRotation = false;
}
布尔值的作用在于渲染函数onRenderTick 里控制摄象机的位置。
// If the mouse button has been clicked then update the camera position
if (doRotation) {
        // convert the change in mouse position into a change in camera angle
        var dPitch:Number = (mouseY - lastMouseY) / 2;
        var dYaw:Number = (mouseX - lastMouseX) / 2;
        // update the camera angles
        cameraPitch -= dPitch;
        cameraYaw -= dYaw;
        // limit the pitch of the camera
        if (cameraPitch < = 0) {
          cameraPitch = 0.1;
        } else if (cameraPitch >= 180) {
          cameraPitch = 179.9;
        }
        // reset the last mouse position
        lastMouseX = mouseX;
        lastMouseY = mouseY;
        // reposition the camera
        camera.orbit(cameraPitch, cameraYaw);
}
orbit 方法是可以围绕任意点做空间转动的,这里他默认围绕原点转动了,这个方法在控制摄象机镜头的时候很好用。通过计算这样每一帧都可以得到一个新角度值,再执行super.onRenderTick 以生成画面,等待下一帧的计算。原理上和onEnterFrame 里mc.x ++是一样的道理,动画就是这样产生的。
球体交互和tweening应用
这第2种交互是直接作用在3D objects上的,还记得之前viewport 里的第4个参数是控制场景是否响应鼠标事件吗?这里就需要把它设为true了,这里因为使用了BasicView 类,所以是修改构造函数里的super方法。
public function Example005() {
      // specify that we want the scene to be interactive
      super(0, 0, true, true);
然后,每一个3D objects如果想响应交互,还需要把材质的interactive 属性设置为true,举个例子:
// Create a new material (flat shaded) and make it interactive var flatShadedMaterial:MaterialObject3D = new FlatShadeMaterial(light, 0x6654FF, 0x060433); flatShadedMaterial.interactive = true; sphere1 = new Sphere(flatShadedMaterial, 50, 10, 10);
接下来给3D objects添加事件监听器,来响应在球体上的点击事件。
// Add a listener to each of the spheres to listen to InteractiveScene3DEvent events sphere1.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere); sphere2.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere); sphere3.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere); sphere4.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onMouseDownOnSphere);
下面写一个onMouseDownOnSphere方法来响应事件。
// called when mouse down on a sphere
private function onMouseDownOnSphere(event:InteractiveScene3DEvent):void {
      var object:DisplayObject3D = event.displayObject3D;
      Tweener.addTween(object, {y:200, time:1, transition:"easeOutSine", onComplete:function():void {goBack(object);} });
}
这里可以看到Tweener的用法了,很简便的设置就可以实现time为1秒里,object的属性y从当前值变化到200,变化方式为easeOutSine,当过程结束以后自动调用goBack方法。
// called when a tween created in onMouseDownOnSphere has terminated
private function goBack(object:DisplayObject3D):void {
      Tweener.addTween(object, {y:0, time:2, transition:"easeOutBounce"});
}
goBack方法的意思是,因为球体在最高处调用了它后,球体使用2秒时间从最高处返回初始值。
看,在Papervision3D 里实现交互是不是很简单,其实这篇文章只是介绍了一个很基础的交互方法。想要在Papervision3D 里实现一些更复杂的交互,建议看下源代码内InteractiveScene3DEvent ,看看还能提供多少可能性。希望这篇文章对你有帮助,它很长,看下来不容易。
下一篇文章将要介绍Papervision3D 里的贴图
Papervision3D GreatWhite 2.0基础教程:第四篇 – 光影
这篇要介绍一下另一个构成3D世界基本元素:光。光的加入会让我们渲染出来的场景看上去更加真实。在Papervision3D的世界里,光要产生效果,必须要配合照在特殊的材质上才能看到。这篇文章的例子会介绍它们是如何工作的。
在开始之前,有必要介绍几个在3D世界中描述光的术语。
Ambient – 阴影色
Diffuse – 过渡色
Specular – 高光色
然后还要介绍4种基本的材质,光照在上面会产生阴影,表现为向光面更亮,背光面更暗,不同的材质表现出来的效果都不一样。他们是:Flat shaded、Gouraud shaded、Phong shaded 和 Cell shaded,这四种材质可以在orgpapervision3dmaterialsshadematerials 中找到。
下面看看Example004中代码如何写。
package {
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import org.papervision3d.core.proto.MaterialObject3D;
  import org.papervision3d.lights.PointLight3D;
  import org.papervision3d.materials.shadematerials.CellMaterial;
  import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
  import org.papervision3d.materials.shadematerials.GouraudMaterial;
  import org.papervision3d.materials.shadematerials.PhongMaterial;
  import org.papervision3d.objects.DisplayObject3D;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.view.BasicView;
  public class Example004 extends BasicView {
    private static const ORBITAL_RADIUS:Number = 200;
    private var sphere1:Sphere;
    private var sphere2:Sphere;
    private var sphere3:Sphere;
    private var sphere4:Sphere;
    private var sphereGroup:DisplayObject3D;
    public function Example004() {
      super(0, 0, true, false);
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Start rendering the scene
      startRendering();
    }
    private function init3D():void {
      // position the camera
      camera.x = -200;
      camera.y =  200;
      camera.z = -500;
    }
    private function createScene():void {
      // Specify a point light source and its location
      var light:PointLight3D = new PointLight3D(true);
      light.x = 400;
      light.y = 1000;
      light.z = -400;
      // Create a new material (flat shaded) and apply it to a sphere
      var flatShadedMaterial:MaterialObject3D = new FlatShadeMaterial(light, 0x6654FF, 0x060433);
      sphere1 = new Sphere(flatShadedMaterial, 50, 10, 10);
      sphere1.x = -ORBITAL_RADIUS;
      // Create a new material (Gouraud shaded) and apply it to a sphere
      var gouraudMaterial:MaterialObject3D = new GouraudMaterial(light, 0x6654FF, 0x060433);
      sphere2 = new Sphere(gouraudMaterial, 50, 10, 10);
      sphere2.x =  ORBITAL_RADIUS;
      // Create a new material (Phong shaded) and apply it to a sphere
      var phongMaterial:MaterialObject3D = new PhongMaterial(light, 0x6654FF, 0x060433, 150);
      sphere3 = new Sphere(phongMaterial, 50, 10, 10);
      sphere3.z = -ORBITAL_RADIUS;
      // Create a new material (cell shaded) and apply it to a sphere
      var cellMaterial:MaterialObject3D = new CellMaterial(light, 0x6654FF, 0x060433, 5);
      sphere4 = new Sphere(cellMaterial, 50, 10, 10);
      sphere4.z =  ORBITAL_RADIUS;
      // Create a 3D object to group the spheres
      sphereGroup = new DisplayObject3D();
      sphereGroup.addChild(sphere1);
      sphereGroup.addChild(sphere2);
      sphereGroup.addChild(sphere3);
        sphereGroup.addChild(sphere4);
      // Add the light and spheres to the scene
      scene.addChild(sphereGroup);
      scene.addChild(light);
    }
    override protected function onRenderTick(event:Event=null):void {
      // rotate the spheres
      sphere1.yaw(-8);
      sphere2.yaw(-8);
      sphere3.yaw(-8);
      sphere4.yaw(-8);
      // rotate the group of spheres
      sphereGroup.yaw(3);
      // call the renderer
      super.onRenderTick(event);
    }
  }
}
执行代码后,会看到4个旋转的球体,每个球体都对光有不同阴影效果。(点击图片可以看效果)
这个例子的代码仍然是前几篇的代码大致相同的,3D场景的初始化过程:viewport 和 camera的设置也是一样的,不同之处在于构建了四个球体,并加入了光源。
加入光源在Papervision3D 中是件很简单的事情:new 一个PointLight3D,再设置一下坐标就好了。
// Specify a point light source and its location var light:PointLight3D = new PointLight3D(true); light.x = 400; light.y = 1000; light.z = -400;
PointLight3D构造函数中传入true的意思是是否人为的让这个光源点可见,在这个例子里其实不是必需的,但是但是在调式的时候让光源点可见是很有帮助的。
让新加入场景的光产生效果,我们需要可以产生阴影的材质。另外我们在画面上看到球体的颜色是球体的材质提供的,所以你可以这样认为,Papervision3D 中的光源只能产生白色光。
// Create a new material (flat shaded) and apply it to a sphere var flatShadedMaterial:MaterialObject3D = new FlatShadeMaterial(light, 0x6654FF, 0x060433); sphere1 = new Sphere(flatShadedMaterial, 50, 10, 10); sphere1.x = -ORBITAL_RADIUS; // Create a new material (Gouraud shaded) and apply it to a sphere var gouraudMaterial:MaterialObject3D = new GouraudMaterial(light, 0x6654FF, 0x060433); sphere2 = new Sphere(gouraudMaterial, 50, 10, 10); sphere2.x = ORBITAL_RADIUS; // Create a new material (Phong shaded) and apply it to a sphere var phongMaterial:MaterialObject3D = new PhongMaterial(light, 0x6654FF, 0x060433, 150); sphere3 = new Sphere(phongMaterial, 50, 10, 10); sphere3.z = -ORBITAL_RADIUS; // Create a new material (cell shaded) and apply it to a sphere var cellMaterial:MaterialObject3D = new CellMaterial(light, 0x6654FF, 0x060433, 5); sphere4 = new Sphere(cellMaterial, 50, 10, 10); sphere4.z = ORBITAL_RADIUS;
以上的代码作用是构建4个球体,每个球体都指定了一种材质,你可以发现4种材质的第一个参数都是指定光源,后面跟了2种颜色。对于FlatShadeMaterial 和 GouraudMaterial 这2种颜色分别表示diffuse 和 ambient。对于PhongMaterial 这2种颜色分别表示specular 和 ambient,后面一个参数150表示反射率,这个值可以从0 到255变化。对于CellMaterial 这2种颜色表示光在这2种颜色渐变,后面一个参数5表示渐变分为5段。
然后将这些球体加入一个DisplayObjet3D 组里并加入scene的显示列表。
// Create a 3D object to group the spheres sphereGroup = new DisplayObject3D(); sphereGroup.addChild(sphere1); sphereGroup.addChild(sphere2); sphereGroup.addChild(sphere3); sphereGroup.addChild(sphere4); // Add the light and spheres to the scene scene.addChild(sphereGroup); scene.addChild(light);
最后给这些球体加入动画效果,让这4个球各自饶着自己的Y轴自转,4个球组成的DisplayObject3D组也饶着自己的Y轴自转。
// rotate the spheres sphere1.yaw(-8); sphere2.yaw(-8); sphere3.yaw(-8); sphere4.yaw(-8); // rotate the group of spheres sphereGroup.yaw(3);
ok,结束。这篇文章只是写了一点很基础的关于光影的应用,当然你可以找网上找到更多的一些较为复杂的效果。不过不管怎么说,从这里开始是个很不错的选择。
下一篇将要介绍如何在场景中加入鼠标事件互动。
Papervision3D GreatWhite 2.0基础教程:第三篇 – 运动
前面两篇文章已经介绍了如何创建一个简单的3D场景,现在来给他加入一些动画效果。要让PV3D的场景展现动画效果,需要修改他的渲染函数,在Example001 里直接修改Event.FRAME_ENTER 事件,在Example002 里需要重载BasicView 里的onRenderTick 函数。
接下来的例子还会继续使用BasicView 类,因为他比较方便。
package {
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import org.papervision3d.core.geom.Lines3D;
  import org.papervision3d.core.geom.renderables.Line3D;
  import org.papervision3d.core.geom.renderables.Vertex3D;
  import org.papervision3d.core.proto.MaterialObject3D;
  import org.papervision3d.materials.WireframeMaterial;
  import org.papervision3d.materials.special.LineMaterial;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.view.BasicView;
  public class Example003 extends BasicView {
    private static const ORBITAL_RADIUS:Number = 200;
    private var sphere:Sphere;
    private var theta:Number = 0;
    public function Example003() {
      super(0, 0, true, false);
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Start rendering the scene
      startRendering();
    }
    private function init3D():void {
      // position the camera
      camera.x = -200;
      camera.y =  200;
      camera.z = -500;
    }
    private function createScene():void {
      // First object : a sphere
      // Create a new material for the sphere : simple white wireframe
      var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFFFFF);
      // Create a new sphere object using wireframe material, radius 50 with
      //   10 horizontal and vertical segments
      sphere = new Sphere(sphereMaterial, 50, 10, 10);
      // Position the sphere (default = [0, 0, 0])
      sphere.x = -ORBITAL_RADIUS;
      // Second object : x-, y- and z-axis
      // Create a default line material and a Lines3D object (container for Line3D objects)
      var defaultMaterial:LineMaterial = new LineMaterial(0xFFFFFF);
      var axes:Lines3D = new Lines3D(defaultMaterial);
      // Create a different colour line material for each axis
      var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000);
      var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00);
      var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF);
      // Create a origin vertex
      var origin:Vertex3D = new Vertex3D(0, 0, 0);
      // Create a new line (length 100) for each axis using the different materials and a width of 2.
      var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 2, origin, new Vertex3D(100, 0, 0));
      var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 2, origin, new Vertex3D(0, 100, 0));
      var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 2, origin, new Vertex3D(0, 0, 100));
      // Add lines to the Lines3D container
      axes.addLine(xAxis);
      axes.addLine(yAxis);
      axes.addLine(zAxis);
      // Add the sphere and the lines to the scene
      scene.addChild(sphere);
      scene.addChild(axes);
    }
    override protected function onRenderTick(event:Event=null):void {
      // rotate the sphere
      sphere.yaw(-4);
      // change the position of the sphere
      theta += 3;
      var x:Number = - Math.cos(theta * Math.PI / 180) * ORBITAL_RADIUS;
      var z:Number =   Math.sin(theta * Math.PI / 180) * ORBITAL_RADIUS;
      sphere.x = x;
      sphere.z = z;
      // call the renderer
      super.onRenderTick(event);
    }
  }
}
输出影片会发现之前例子中的球体和坐标线,但是因为我们已经加入了动画效果,所以会发现球体会分别饶着自己和坐标原点的Z轴做圆周转动。(点击图片可以看到这个例子)
对比Example002 中的代码,会发现大部分几乎是一样的,唯一不同的地方来自渲染函数onRenderTick
override protected function onRenderTick(event:Event=null):void {
      // rotate the sphere
      sphere.yaw(-4);
      // change the position of the sphere
      theta += 3;
      var x:Number = - Math.cos(theta * Math.PI / 180) * ORBITAL_RADIUS;
      var z:Number =   Math.sin(theta * Math.PI / 180) * ORBITAL_RADIUS;
      sphere.x = x;
      sphere.z = z;
      // call the renderer
      super.onRenderTick(event);
    }
产生动画效果仍然是很简单的事情,只需要修改3D Object的坐标,旋转参数既可。关于一个3D Object究竟有多少参数可以设置,就细心查看Papervision3D 的API文档吧。学会看API文档也是学习的一个很重要步骤。Papervision3D 的API文档在googlecode可以下载到。
Papervision3D GreatWhite 2.0基础教程:第二篇 – 基础
上篇文章讲到了Papervision3D v2.0有一个更加方便的方法来创建一个3D场景,就是使用BasicView 类。让你的DocumentClass去继承BasicView 类,BasicView 继承自Sprite类,所以可以省掉很多麻烦。
Example002.as
下面的例子使用了和第一篇同样的代码,只不过关键的地方替换成使用BasicView来做了。
package {
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import org.papervision3d.core.geom.Lines3D;
  import org.papervision3d.core.geom.renderables.Line3D;
  import org.papervision3d.core.geom.renderables.Vertex3D;
  import org.papervision3d.core.proto.MaterialObject3D;
  import org.papervision3d.materials.WireframeMaterial;
  import org.papervision3d.materials.special.LineMaterial;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.view.BasicView;
  public class Example002 extends BasicView {
    private var sphere:Sphere;
    public function Example002() {
      super(0, 0, true, false);
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Start rendering the scene
      startRendering();
    }
    private function init3D():void {
      // position the camera
      camera.x = -100;
      camera.y = -100;
      camera.z = -500;
    }
    private function createScene():void {
      // First object : a sphere
      // Create a new material for the sphere : simple white wireframe
      var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFFFFF);
      // Create a new sphere object using wireframe material, radius 50 with
      //   10 horizontal and vertical segments
      var sphere:Sphere = new Sphere(sphereMaterial, 50, 10, 10);
      // Position the sphere (default = [0, 0, 0])
      sphere.x = -100;
      // Second object : x-, y- and z-axis
      // Create a default line material and a Lines3D object (container for Line3D objects)
      var defaultMaterial:LineMaterial = new LineMaterial(0xFFFFFF);
      var axes:Lines3D = new Lines3D(defaultMaterial);
      // Create a different colour line material for each axis
      var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000);
      var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00);
      var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF);
      // Create a origin vertex
      var origin:Vertex3D = new Vertex3D(0, 0, 0);
      // Create a new line (length 100) for each axis using the different materials and a width of 2.
      var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 2, origin, new Vertex3D(100, 0, 0));
      var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 2, origin, new Vertex3D(0, 100, 0));
      var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 2, origin, new Vertex3D(0, 0, 100));
      // Add lines to the Lines3D container
      axes.addLine(xAxis);
      axes.addLine(yAxis);
      axes.addLine(zAxis);
      // Add the sphere and the lines to the scene
      scene.addChild(sphere);
      scene.addChild(axes);
    }
  }
}
输出以后会发现,他产生的效果和第一篇中的例子是一样的,但是代码少了不少。(点击图片可以看到这个例子)
初始化并渲染
和第一篇的例子做比较,会发现初始的过程变简单了。在构造函数里就已经自己把几个PV3D必须的类实例化了,你可以直接通过camera scene 等访问到。
public function Example002() {
      super(0, 0, true, false);
如果你查看BasicView的源代码,会发现这4个参数是直接传到Viewport3D 的。
下一步,做一个初始化函数来设定camera的位置。
private function init3D():void {
      // position the camera
      camera.x = -100;
      camera.y = -100;
      camera.z = -500;
    }
接下来的代码和第一篇基本一样了,所以这里就不再重复了,唯一不同的是在构造函数中的最后一行。
// Start rendering the scene
      startRendering();
这里使用了一个很简单的指令来渲染场景,如果你查看startRendering在BasicView的源代码,会发现也是通过enterframe事件调用一个方法onRenderTick。这个方法是可以在我们继承的这个类中重载的,以实现一些更复杂的表现效果。
BasicView类的用法到这里就结束了结束。下一篇开始会介绍如何给你一个3D Object 添加一些简单的运动效果。
Papervision3D GreatWhite 2.0基础教程:第一篇 – 入门
这系文章是译稿,目前Papervision3D的最新版本为GreatWhite 2.0,http://papervision3d.googlecode.com/ 可以下载到。这篇文章就是基于最新的GreatWhite 2.0的基础教程。
要构建一个Papervision3D场景,你需要记住几个关键的类:Scene3D, Viewport3D, Camera3D 和 BasicRenderEngine,他们是这个核心的基础。在这系文章的下一篇,还将介绍Papervision3D 2.0的另一个类:BasicView,它将上面几个基础核心类都整合在一起,使用她可以让你更快更方便的创建一个3D应用。不过现在要讲的仍然是基础的构建方法。
Example001.as
这个例子只绘制了一个线框贴图的球体和X、Y、Z 3根坐标轴,通过这个例子很好的说明的一个3D场景是如何建立的。
package {
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import org.papervision3d.cameras.Camera3D;
  import org.papervision3d.core.geom.Lines3D;
  import org.papervision3d.core.geom.renderables.Line3D;
  import org.papervision3d.core.geom.renderables.Vertex3D;
  import org.papervision3d.core.proto.MaterialObject3D;
  import org.papervision3d.materials.WireframeMaterial;
  import org.papervision3d.materials.special.LineMaterial;
  import org.papervision3d.objects.DisplayObject3D;
  import org.papervision3d.objects.primitives.Sphere;
  import org.papervision3d.render.BasicRenderEngine;
  import org.papervision3d.scenes.Scene3D;
  import org.papervision3d.view.Viewport3D;
  public class Example001 extends Sprite {
    private var scene:Scene3D;
    private var camera:Camera3D;
    private var viewport:Viewport3D;
    private var renderer:BasicRenderEngine;
    public function Example001() {
      // set up the stage
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      // Initialise Papervision3D
      init3D();
      // Create the 3D objects
      createScene();
      // Initialise Event loop
      this.addEventListener(Event.ENTER_FRAME, loop);
    }
    private function init3D():void {
      // create viewport
      viewport = new Viewport3D(0, 0, true, false);
      addChild(viewport);
      // Create new camera with fov of 60 degrees (= default value)
      camera = new Camera3D(60);
      // initialise the camera position (default = [0, 0, -1000])
      camera.x = -100;
      camera.y = -100;
      camera.z = -500;
      // target camera on origin
      camera.target = DisplayObject3D.ZERO;
      // Create a new scene where our 3D objects will be displayed
      scene = new Scene3D();
      // Create new renderer
      renderer = new BasicRenderEngine();
    }
    private function createScene():void {
      // First object : a sphere
      // Create a new material for the sphere : simple white wireframe
      var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFFFFF);
      // Create a new sphere object using wireframe material, radius 50 with
      //   10 horizontal and vertical segments
      var sphere:Sphere = new Sphere(sphereMaterial, 50, 10, 10);
      // Position the sphere (default = [0, 0, 0])
      sphere.x = -100;
      // Second object : x-, y- and z-axis
      // Create a default line material and a Lines3D object (container for Line3D objects)
      var defaultMaterial:LineMaterial = new LineMaterial(0xFFFFFF);
      var axes:Lines3D = new Lines3D(defaultMaterial);
      // Create a different colour line material for each axis
      var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000);
      var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00);
      var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF);
      // Create a origin vertex
      var origin:Vertex3D = new Vertex3D(0, 0, 0);
      // Create a new line (length 100) for each axis using the different materials and a width of 2.
      var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 2, origin, new Vertex3D(100, 0, 0));
      var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 2, origin, new Vertex3D(0, 100, 0));
      var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 2, origin, new Vertex3D(0, 0, 100));
      // Add lines to the Lines3D container
      axes.addLine(xAxis);
      axes.addLine(yAxis);
      axes.addLine(zAxis);
      // Add the sphere and the lines to the scene
      scene.addChild(sphere);
      scene.addChild(axes);
    }
    private function loop(event:Event):void {
      // Render the 3D scene
      renderer.renderScene(scene, camera, viewport);
    }
  }
}
当代码执行以后,你会看到一个线框球和3根坐标轴。(点击图片可以看到这个例子)
初始化Stage
留意Example001.as中的代码:
stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE;
第一行的意思是,当影片输出的时候,整个影片相对浏览器的左上方对齐。第二行的意思是,影片不会跟随浏览的尺寸大小而发生缩放。
this.addEventListener(Event.ENTER_FRAME, loop);
这一行的意思是为场景注册一个事件监听器,每当场景ENTER_FRAME的时候,就执行一次loop函数,ENTER_FRAME的频率就是输出影片时设置的每秒帧数。
初始化3D场景
初始化3D场景依赖3个关键的类,前文已经提到过:Scene3D, Viewport3D and Camera3D
首先创建一个Viewport3D 实例,并通过addChild加入到flash舞台的显示节点内。Viewport3D 用来将3D场景内计算出来的3维图象映射到flash的2维平面。他就相当于我们的显示器,在玩3D游戏的时候,我们看到游戏里面的画面是3维的,但是很明显你的液晶显示屏是个平面,这是视觉错觉让你觉得他是3维的,3D引擎的作用的就是产生这个视觉错觉。
viewport = new Viewport3D(0, 0, true, false); addChild(viewport);
Viewport3D类的前2个参数指示这个构建出来的窗口尺寸。如果将他们都设为0,则Papervision3D默认使用整个stage的尺寸大小来构建这个窗口。第3个参数则指示当舞台的大小发生变化时候Viewport3D是否reSize。最后一个参数指示构建出来的这个窗口是否响应鼠标交互事件,关于交互以后会讲到。
接下来要创建一个摄像机,你可以想象一下通过Viewport3D看到的画面都是由这个摄像机拍摄下来的,摄像机可以设置一些参数,比如放大倍数,焦距,XYZ位于空间的位置。
// Create new camera with fov of 60 degrees (= default value) camera = new Camera3D(60); // initialise the camera position (default = [0, 0, -1000]) camera.x = -100; camera.y = -100; camera.z = -500; // target camera on origin camera.target = DisplayObject3D.ZERO;
这里我们创建了一个摄像机,他的视角为60度(默认值),控制位置为[-100, -100, -500],摄像机的默认观察对象为场景的[0, 0, 0]点。
最后需要创建一个Scene3D 和一个 BasicRenderEngine
// Create a new scene where our 3D objects will be displayed scene = new Scene3D(); // Create new renderer renderer = new BasicRenderEngine();
所有被创建且需要显示在场景上的3D物体,都需要addChild到Scene3D的显示列表里,这点和flash的addChild很像。BasicRenderEngine则是用来计算每一个画面该显示什么内容。
看,构建一个PV3D的世界很简单是吗?(其实还有一个更简单的方法,通过BasicView类。在下一篇会讲到)
创建一个3D Object到舞台
下一步,在舞台上加入2个简单的3D Object,一个用线框贴图的球体,3条有不同颜色并指示坐标轴方位的线。
球体是Papervision3D 的一个基本物体,所以你不需要知道关于写一个球体的代码是怎么样的,你只需要new 一个Sphere,并填入一些参数就好了。
// Create a new material for the sphere : simple white wireframe var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFFFFF); // Create a new sphere object using wireframe material, radius 50 with // 10 horizontal and vertical segments var sphere:Sphere = new Sphere(sphereMaterial, 50, 10, 10); // Position the sphere (default = [0, 0, 0]) sphere.x = -100;
这里出现了一个新东西:material(材质)。在Papervision3D 的世界,任何一个需要被显示的物体,都需要关联一个材质。将材质贴在一个物体上,你才能实实在在的看到他。就像一个气球,如果气球的表皮破了,里面的空气你是看不到的。在Papervision3D 里,如果你细心留意一下org/papervision3d/materials,你会发现里面有很多种生成材质的类。在这个例子里,使用了一个白色线框材质用来做贴图。
在这个例子里,创建球体时指定了4个参数,第1个是球体的贴图材质,第2个是球体的半径,第3个是球体横向分隔度,第4个是球体的纵向分隔度。修改这些参数来体会他们代表的意义是个学习的好方法。
接下来是创建线体
// Create a default line material and a Lines3D object (container for Line3D objects) var defaultMaterial:LineMaterial = new LineMaterial(0xFFFFFF); var axes:Lines3D = new Lines3D(defaultMaterial); // Create a different colour line material for each axis var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000); var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00); var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF); // Create a origin vertex var origin:Vertex3D = new Vertex3D(0, 0, 0); // Create a new line (length 100) for each axis using the different materials and a width of 2. var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 2, origin, new Vertex3D(100, 0, 0)); var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 2, origin, new Vertex3D(0, 100, 0)); var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 2, origin, new Vertex3D(0, 0, 100)); // Add lines to the Lines3D container axes.addLine(xAxis); axes.addLine(yAxis); axes.addLine(zAxis);
这些线是由Line3D 类创建的,创建完后这些线要一起加入到一个Lines3D 组里。
当球体和线都创建好了以后,就可以将他们加入到Scene3D的显示列表里。
// Add the sphere and the lines to the scene scene.addChild(sphere); scene.addChild(axes);
现在我们初始化好了3D环境,创建好了3D Object,只剩最后一步渲染了。
渲染场景
这里我们用之前创建的BasicRendererEngine 配合每个enterframe都会执行的loop函数来渲染场景。
// Render the 3D scene renderer.renderScene(scene, camera, viewport);
BasicRendererEngine 关联住scene, camera, viewport 后,会通过计算将3D 内容转换成2D 画面显示出来。
结束!第一篇文章比较长,后面的文章会讲的再快一点。
 
                    
                     
                    
                 
                    
                









 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号