Posts Tagged ‘reflection’

Reflection in Away3D, Take 2

Reflection in Away3D

About 13 seconds after I finished my last “Reflections in Away3D” demo I thought up a much better way to do it. I guess that’s what happens when you apply loosely applicable code (see “Reflection in AS3″) to a new scenario. This method, while a bit more resource intensive, is MUCH more functional. Instead of doing a simple Bitmap inversion with a gradient mask and locking the view to only 2 out of 3 planes at a time, I use Away3D Plane objects and multiple views/cameras to create real-time reflections.  This is a 3D engine after all.

The revision I’m presenting here is just a proof of concept. In the near future I hope to release a class that encapsulates reflections much like SimpleShadow in Away3D. You’ll recognize SimpleShadow from the my Away3D shadows demo, which was written by the bad-ass Away3D dev and Prefab creator Fabrice Closier. Inspiration for this code definitely comes from his motivational work.  The reflections aren’t perfect, the overlapping isn’t perfect, but it does help to add a little more dimension to the scene.

UPDATE: Normally I would release the source code, but in my ignorance and zeal to get this done I totally missed the Away3D ReflectivePlane class.  It is designed to do exactly what this plane does using a lot more math, but only one additional camera/view per reflective plane.  It also does realistic reflections relative to the position of the viewing camera.  In other words, way better than what I was offering!

Li's ReflectivePlane Demo

Li's ReflectivePlane Demo

The only problem with ReflectivePlane is that it was designed a few Away3D revisions back and now has some intermittent initialization glitches.  It seemed to me to be better for the Away3d community that I try to recruit ReflectivePlane’s author, Alejandro Santander (AKA, “Li”), to help troubleshoot the problems rather than muddy the waters by adding another solution to an already solved scenario.  When we have a resolution to this problem, I’ll be sure to let you know… in the form of a demo WITH source code this time!

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.

Reflection in AS3

Source code + Flash Develop project

Inspired by this tutorial on reflections in AS3 at adobe devnet, I decided to play with reflections in Actionscript3. As usual I can never follow a tutorial or step by step process without constantly trying to tweak it.

In my process of tweaking I found a slight short coming in the example given. While it does a fantastic job of handling standard DisplayObjects, I found that things get a little messed up when you add filters to these objects. Notice how the edges of the first image are cut off and sharp, whereas the the second image that accounts for the filters’ impact on DisplayObjects’ dimensions looks smooth:

Reflections cut-off

Filters cut-off

Reflections cut-off

Filters complete

In my efforts to remedy this problem I came across a very useful function for BitmapData called generateFilterRect(). Given a Rectangle and a BitmapFilter this function will return the Rectangle that would actually encompass the DisplayObject along with its filter. With a simple bit of iteration we can traverse an object’s list of filters and determine what the max dimensions will be. Here’s the function I added to take care of it:

  private function _createReflectionBitmapData(obj:Sprite):BitmapData {
      var filterRect:Rectangle;
      var width:Number = obj.width;
      var height:Number = obj.height;
      var bmd:BitmapData = new BitmapData(width, height, true, 0xffffff);
      var matrix:Matrix = new Matrix();
 
      // filters can cause a display object to render outside of its rectangle
      for each (var filter:BitmapFilter in obj.filters) {
        filterRect = bmd.generateFilterRect(bmd.rect, filter);
        width = filterRect.width > width ? filterRect.width : width;
        height = filterRect.height > height ? filterRect.height : height;
      }
 
      // create, invert, and position relfection bitmapdata
      bmd = new BitmapData(width, height, true, 0xffffff);
      matrix.createBox(1, -1, 0, (width - obj.width)/2, height - (height - obj.height)/2);
      bmd.draw(obj, matrix);
 
      return bmd;
    }

The end result is BitmapData that contains our inverted DisplayObject, now properly sized and positioned. We can now pass this into a Bitmap object and continue on with the code in the tutorial mentioned at the beginning of this post. Or… you can use my stripped down and less functional version to get a simple view of how reflections are achieved in AS3:

package
{
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.DisplayObject;
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.filters.BitmapFilter;
  import flash.filters.GlowFilter;
  import flash.geom.Matrix;
  import flash.geom.Rectangle;
 
  /**
   * ...
   * @author Tony Lukasavage - SavageLook.com
   */
  public class Main extends Sprite
  {
    public function Main():void
    {
      if (stage) init();
      else addEventListener(Event.ADDED_TO_STAGE, init);
    }
 
    private function init(e:Event = null):void
    {
      removeEventListener(Event.ADDED_TO_STAGE, init);
 
      // create main glow circle
      var obj:Sprite = new Sprite();
      var matrix:Matrix = new Matrix();
      var radius:Number = 100;
      matrix.createGradientBox(radius*2, radius*2, Math.PI / 2);
      obj.graphics.beginGradientFill("linear", [0x888888, 0xffffff], [1, 1], [0, 255], matrix);
      obj.graphics.drawCircle(radius, radius, radius);
      obj.graphics.endFill();
      obj.x = stage.stageWidth / 2 - radius;
      obj.y = stage.stageHeight / 2 - radius*1.5;
      obj.filters = [new GlowFilter(0xffffff, 1, 20, 20, 2, 1)];
      this.addChild(obj);
 
      // create reflection
      var bmd:BitmapData = _createReflectionBitmapData(obj);
      var bitmapReflect:Bitmap = new Bitmap(bmd);
      bitmapReflect.x = stage.stageWidth / 2 - bitmapReflect.width/2;
      bitmapReflect.y = obj.y + radius*2;
      this.addChild(bitmapReflect);
 
      // create gradient for reflection
      var grad:Sprite = new Sprite();
      matrix.createGradientBox(bitmapReflect.width, bitmapReflect.height / 2, Math.PI / 2, 0, 0);
      grad.graphics.beginGradientFill("linear", [0xffffff, 0xffffff], [1, 0], [0, 255], matrix);
      grad.graphics.drawRect(0, 0, bitmapReflect.width, bitmapReflect.height);
      grad.x = bitmapReflect.x;
      grad.y = bitmapReflect.y;
      grad.cacheAsBitmap = true;
      bitmapReflect.cacheAsBitmap = true;
      bitmapReflect.mask = grad;
      this.addChild(grad);
    }
 
    private function _createReflectionBitmapData(obj:Sprite):BitmapData {
      var filterRect:Rectangle;
      var width:Number = obj.width;
      var height:Number = obj.height;
      var bmd:BitmapData = new BitmapData(width, height, true, 0xffffff);
      var matrix:Matrix = new Matrix();
 
      // filters can cause a display object to render outside of its rectangle
      for each (var filter:BitmapFilter in obj.filters) {
        filterRect = bmd.generateFilterRect(bmd.rect, filter);
        width = filterRect.width > width ? filterRect.width : width;
        height = filterRect.height > height ? filterRect.height : height;
      }
 
      // create, invert, and position relfection bitmapdata
      bmd = new BitmapData(width, height, true, 0xffffff);
      matrix.createBox(1, -1, 0, (width - obj.width)/2, height - (height - obj.height)/2);
      bmd.draw(obj, matrix);
 
      return bmd;
    }
  }
}

And thats the basics. If you want an in depth description of each step and how they are performed, I will again refer you to the terrific tutorial by Ben Pritchard. In the meantime, enjoy your new found knowledge and start putting it to use. Everyone else is. Seriously. Like every image anymore has a reflection under it. This is really starting to irk my “swim against the current” side. Oh well, nothing a glass of Jack can’t cure.

Is everyone doing this in Away3D? No? Well you can add me to the list soon ;)