Reflection in Away3D

Reflection in Away3D

Click here or the image above for the reflection demo
View the source code
→ Download the source code

I alluded to a desire to implement reflections in Away3D in my prior post on reflections in AS3. Well here it is. It wasn’t quite as easy of a translation as I thought it would be, but with lots of digging through the , as well as spotting a good example at geepers.co.uk, I managed to get a basic demo up and running.

While there’s a good bit of code that goes into this demo, the secret sauce is right here:

  // must render before making the following calls
  _view.render();
 
  // find 2d bounding box of sphere
  _drawn = (_view.session.getContainer(_view) as DisplayObjectContainer).getChildAt(0);
  _bounds = _drawn.getRect(this);

What this code does is give us the DisplayObject that contains our 3D object within the given view. We are also able to determine the bounds of the DisplayObject and its 2 dimensional position within the view. With this information we are able to effectively create a snapshot of the visible 3d object. From there we need to create BitmapData that is the inverse of the DisplayObject. To do that we apply the appropriate matrix (invert the y scale) to the DisplayObject when it is drawn to BitmapData. After that we assign the BitmapData to the display Bitmap and then position it according to the bounds of the DisplayObject.

  // redraw reflection
  _bmd = new BitmapData(_drawn.width, _drawn.height, true, 0x00ffffff);
  _matrix.createBox(_xscale, -_yscale, 0, _drawn.width/2, _drawn.height * _yscale / 2);
  _bmd.draw(_drawn, _matrix);
  _bitmapReflect.bitmapData = _bmd;
  _bitmapReflect.x = _bounds.x;
  _bitmapReflect.y = stage.stageHeight / 2 + _distance;

Finally we create an alpha gradient mask that will be applied to the Bitmap in order to give it that cool, fading reflection look.

  // redraw gradient mask for reflection
  _matrix.createGradientBox(_bitmapReflect.width, _bitmapReflect.height * _yscale / 2, Math.PI / 2, 0, 0);
  _bitmapReflectGradient.graphics.clear();
  _bitmapReflectGradient.graphics.beginGradientFill("linear", [0xffffff, 0xffffff], [0.9, 0], [0, 255], _matrix);
  _bitmapReflectGradient.graphics.drawRect(0, 0, _bitmapReflect.width, _bitmapReflect.height);
  _bitmapReflectGradient.graphics.endFill();
  _bitmapReflectGradient.x = _bitmapReflect.x;
  _bitmapReflectGradient.y = _bitmapReflect.y;

And there you go, reflections in Away3D. There are a number of limitations to this method, though:

  • It can only draw reflections of objects currently visible in the view.  In fact, there should be a check on the
    (_view.session.getContainer(_view) as DisplayObjectContainer).getChildAt(0)
    call to make sure that the view contains any children.
  • The reflection is drawn of the entire view, not just the object in question.  That’s why this works best with only one object in the view.  You can use multiple views to get reflections for multiple objects.
  • This example only does reflections of the view as its manipulated along the X & Y planes.  Moving the object along the Z plane will not effect the reflection properly.  I’m sure a method utilizing planes could do better.

If those limitations don’t bother you, then go to town. If they do, be patient. I’m working on another idea for reflection in Away3D that will be a lot more flexible, though more complex and processor intensive.

Remember how my previous blog post on shadows (of course you do) showed how you can easily enhance the 3D feel of your site? Of course you do. Now you can add reflections to that repetoire. It’s all about subtle changes to give your work a more polished look and feel. Have fun and let me know if you create reflections this way, or if you have an implementation of your own.

3 Responses to “Reflection in Away3D”

  1. [...] 原文:Reflection in Away3D 链接:https://savagelook.com/blog/away3d/reflection-in-away3d [...]

  2. says:

    Hey,

    following an example of Stephen Weber (http://www.prodevtips.com/2010/05/25/itunes-cover-flow-with-as3/) I am creating a 3D version made using Away3D (FP 10).
    Considering that “ReflectivePlane Class” doesn’t work I am trying to get a reflection using your system. Unfortunately there is something wrong that is driving me crazy since days… The bitmapdata does not correspond to View.

    Here there is a smaller version of my case.
    Looking forward to hear from somebody soon. Best regards

    package
    {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.display.DisplayObject;
    import flash.geom.Rectangle;
    import flash.geom.Matrix;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObjectContainer;

    //import the required parts of Away3D
    import away3d.containers.View3D;
    import away3d.containers.Scene3D;
    import away3d.containers.ObjectContainer3D;
    import away3d.core.utils.Cast;
    import away3d.core.base.Object3D;
    import away3d.core.base.Element;
    import away3d.core.render.BasicRenderer;
    import away3d.cameras.HoverCamera3D;
    import away3d.primitives.Cube;

    //TweenLite – Tweening Engine – SOURCE: http://blog.greensock.com/tweenliteas3/
    import com.greensock.TweenLite;

    [SWF("#000000", height="600", width="800", frameRate="31")]
    public class Main extends Sprite
    {
    private var renderer:BasicRenderer;

    private var scene:Scene3D;
    private var camera:HoverCamera3D;
    private var view:View3D;
    private var container:ObjectContainer3D=new ObjectContainer3D()

    //How many image covers
    private var coverflowItemsTotal:Number;

    //Holder for current CoverflowItem
    private var _currentCover:Number;

    // Store covers
    private var coverArray:Array=new Array();
    // Display the middle of the cover or not
    private var startIndexInCenter:Boolean = true;

    // Which cover to display in the beginning
    private var startIndex:Number = 0;

    private var centerX:Number;
    private var centerY:Number;

    //padding between each cover
    private var coverflowSpacing:Number = 70;
    // transition time for movement
    private var transitionTime:Number = 0.75;

    //Z Position of Current CoverflowItem
    private var centerCoverflowZPosition:Number = -125;

    // size of the image cover
    private var coverflowImageWidth:Number = 100;
    private var coverflowImageHeight:Number = 100;
    private var coverflowImageDepth:Number = 30;

    private var _drawn:DisplayObject;
    private var _bounds:Rectangle;
    private var _distance:Number = 130;

    private var _matrix:Matrix = new Matrix();
    private var _bitmapReflect:Bitmap = new Bitmap();
    private var _bitmapReflectGradient:Sprite = new Sprite();
    private var _bmd:BitmapData;

    private var _yscale:Number = 1;
    private var _xscale:Number = 1;
    ;

    public function Main()
    {
    centerX = 0;
    centerY = 0;

    init();
    init3D();
    setItems(6);
    }

    private function init():void
    {
    stage.quality = StageQuality.HIGH;
    stage.scaleMode = StageScaleMode.NO_SCALE;
    stage.align = StageAlign.TOP_LEFT;
    }

    private function init3D():void
    {
    camera = new HoverCamera3D();
    renderer = new BasicRenderer();
    scene = new Scene3D();
    view = new View3D();
    view.scene = scene;
    view.camera = camera;
    view.renderer = renderer;
    view.x = stage.stageWidth / 2;
    view.y = stage.stageHeight / 2;

    camera.tiltAngle = 0;
    camera.zoom = 2;
    camera.focus=200;
    camera.distance=-250;

    this.addChild(view);

    // configure reflection display objects
    _bitmapReflectGradient.cacheAsBitmap = true;
    _bitmapReflect.cacheAsBitmap = true;
    _bitmapReflect.mask = _bitmapReflectGradient;

    this.addChild(_bitmapReflect);
    this.addChild(_bitmapReflectGradient);
    }

    private function setItems(_coverflowItemsTotal:int):void
    {
    coverflowItemsTotal = _coverflowItemsTotal;

    container=new ObjectContainer3D();
    for (var i:int=0; i> 1;
    gotoCoverflowItem(startIndex);
    }
    else
    {
    gotoCoverflowItem(startIndex);

    }
    _currentCover = startIndex;

    //These buttons have to be created
    left_btn.addEventListener(MouseEvent.CLICK,coverSlider_Previous);
    right_btn.addEventListener(MouseEvent.CLICK,coverSlider_Next);

    stage.addEventListener(Event.RESIZE, handleBrowserResize);
    stage.addEventListener(Event.ENTER_FRAME,render);
    handleBrowserResize();
    }

    // display the previous image
    private function clickPre(e:Event=null):void
    {
    _currentCover–;
    if (_currentCover coverflowItemsTotal – 1)
    {
    _currentCover = 0;
    }
    gotoCoverflowItem(_currentCover);
    }
    // move to a certain cover via number
    private function gotoCoverflowItem(n:int):void
    {
    _currentCover = n;
    reOrderCover(n);
    }

    // change each cover’s position and rotation
    private function reOrderCover(currentCover:uint):void
    {
    for (var i:uint = 0, len:uint = coverArray.length; i < len; i++)
    {
    var cover:Cube= coverArray[i];

    if (i currentCover) {
    //Right Side
    TweenLite.to(cover, transitionTime, {x:(centerX + (i – currentCover) * coverflowSpacing + coverflowImageWidth/2), z:(coverflowImageWidth/2), rotationY:65});
    } else {
    //Center Coverflow
    TweenLite.to(cover, transitionTime, {x:centerX, z:centerCoverflowZPosition, rotationY:0});
    }
    }
    for (i = 0; i currentCover; i–)
    {
    view.scene.addChild(coverArray[i]);
    }

    view.scene.addChild(coverArray[currentCover]);
    }

    private function render(e:Event):void
    {
    camera.hover();
    _xscale = _yscale = 1 – ((300 – view.y) / 600);
    view.render();

    // redraw reflection
    _drawn = (view.session.getContainer(view) as DisplayObjectContainer).getChildAt(0);
    _bounds = _drawn.getRect(this);

    // redraw reflection
    _bmd = new BitmapData(_drawn.width, _drawn.height, true, 0x00ffffff);
    _matrix.createBox(_xscale, -_yscale, 0, _drawn.width/2, _drawn.height * _yscale / 2);
    _bmd.draw(_drawn, _matrix);
    _bitmapReflect.bitmapData = _bmd;
    _bitmapReflect.x = _bounds.x;
    _bitmapReflect.y = stage.stageHeight / 2 + _distance;

    // redraw gradient mask for reflection
    _matrix.createGradientBox(_bitmapReflect.width, _bitmapReflect.height * _yscale / 2, Math.PI / 2, 0, 0);
    _bitmapReflectGradient.graphics.clear();
    _bitmapReflectGradient.graphics.beginGradientFill(“linear”, [0xffffff, 0xffffff], [0.9, 0], [0, 255], _matrix);
    _bitmapReflectGradient.graphics.drawRect(0, 0, _bitmapReflect.width, _bitmapReflect.height);
    _bitmapReflectGradient.graphics.endFill();
    _bitmapReflectGradient.x = _bitmapReflect.x;
    _bitmapReflectGradient.y = _bitmapReflect.y;
    }

    private function coverSlider_Previous(e:MouseEvent):void
    {
    clickPre();
    }

    private function coverSlider_Next(e:MouseEvent):void
    {
    clickNext();
    }

    private function handleBrowserResize(e:Event=null):void
    {
    view.x = stage.stageWidth / 2;
    view.y = stage.stageHeight / 2;
    }
    }
    }

  3. NanaNono技术站 - Away3D里的倒影 says:

    [...] 原文:Reflection in Away3D [...]