Water Reflection Displacement Effect

Demo

This code simulates the effect you see when looking at a reflection in water- where the waves distort the reflection. It is a relatively simple effect.

The Method Used

  1. The reflection is achieved by copying the sprite to be reflected and rotating it about the x-axis. That will flip it or invert it downwards. Then you can scale it to be a bit smaller than the original in height. The whole operation can be achieved simply in flash by scaling the y-dimension by -3/4. The negative does the inversion and the 3/4 makes it 75% the height of the original image.
  2. Once the reflection has been done, a DisplacementMapFilter is run over the result. The input to the filter is a bitmap that was generated using perlin noise. Perlin noise has been used extensively in various effects on this site and they started back with this one. The perlin noise is generated in colour and the green (X) and red (Y) channels are used to shift the pixels.
  3. To animate the effect, the parameters for generating the perlin noise are tweaked and the displacement filter is re-run on the original reflection. This traversal of the 2D perlin dimensional-space is what keeps the smoothness of the displacement effect.

I added a simple GUI to fiddle with the perlin noise parameters to change the animation result.

The Code

package net.avdw.effect.reflection
{
	import avdw.action.ScreenshotAction;
	import avdw.action.ThumbshotAction;
	import avdw.math.vector2d.Vec2;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.DisplacementMapFilter;
	import flash.filters.DisplacementMapFilterMode;
	import flash.geom.Point;
	import uk.co.soulwire.gui.SimpleGUI;
	/**
	 * ...
	 * @author Andrew van der Westhuizen
	 */
	[SWF(backgroundColor="0xFFFFFF",frameRate="60",width="680",height="495")]

	public class Main extends Sprite
	{
		[Embed(source="../../../../../embed/yoda.png")]
		private const _yoda:Class;
		private var perlinBmp:Bitmap
		private var i:int;;
		private var refl:Bitmap;
		private var dMap:DisplacementMapFilter;
		public var perlin:Object;

		public function Main()
		{
			if (stage)
				init();
			else
				addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			ScreenshotAction.addTo(stage);
			ThumbshotAction.addTo(stage);

			var img:Bitmap = new _yoda();
			img.y = 20;
			img.x = (stage.stageWidth - img.width) / 2;

			refl = new _yoda();
			refl.scaleY = -3/4;
			refl.y = img.height + refl.height + 10;
			refl.x = img.x;
			addChild(refl);

			perlinBmp = new Bitmap(new BitmapData(img.width, img.height*3/4));
			i = 1;

			dMap = new DisplacementMapFilter(perlinBmp.bitmapData, new Point(), BitmapDataChannel.RED, BitmapDataChannel.GREEN, 10, 50, DisplacementMapFilterMode.CLAMP);

			addEventListener(Event.ENTER_FRAME, loop);
			addChild(img);

			perlin = new Object();
			perlin.baseX = 45;
			perlin.baseY = 5;
			perlin.numOctaves = 3;
			perlin.speed = 1;
			perlin.seamless = false;
			perlin.fractalNoise = true;
			perlin.moveX = 1;
			perlin.moveY = 1 / 8;

			var gui:SimpleGUI = new SimpleGUI(this, "Water reflection parameters");
			gui.addSlider("perlin.baseX", 0, 90);
			gui.addSlider("perlin.baseY", 0, 20);
			gui.addSlider("perlin.numOctaves", 1, 8);
			gui.addToggle("perlin.seamless");
			gui.addToggle("perlin.fractalNoise");
			gui.addSlider("perlin.speed", 0.1, 2);
			gui.addSlider("perlin.moveX", -1, 1);
			gui.addSlider("perlin.moveY", -1, 1);			
			gui.show();
		}

		private function loop(e:Event):void
		{
			i++;
			perlinBmp.bitmapData.perlinNoise(perlin.baseX, perlin.baseY, perlin.numOctaves, 50, perlin.seamless, perlin.fractalNoise, 7, true, [new Point(i * perlin.moveX/perlin.speed, i * perlin.moveY/perlin.speed)]);
			refl.filters = [dMap];
		}

	}

}

Here is a gist with a compilable and commented version, at the request of a commenter, Azazel, below.
https://gist.github.com/avanderw/6087271

  • Azazel

    Hi. I’m new to flash and dig this wicked code. how do I apply it to a mc on my stage? (AS3)

    • The code works at a bitmap level and not on the AS3 objects themselves. The easiest would be to draw your mc or display object into a BitmapData object (each frame, if it is animated) and then apply the effect as above (e.g. reflect, scale, apply displacement) to the bitmap data.

      • Azazel

        Thanks for your response, Things is I battle with the AS side of things. would it be possible for you to put an example of a project with all the files in a zip file for me go through and download so I can apply it to my design. I would real appreciate it if you could. regards…

        • I will make some time tomorrow and put something together for you. What exactly must it do? I am assuming that you want to have the effect run on your mc:MovieClip which is in your stage.

          • Azazel

            Yes I have an image in a Mc on my stage that I would like to apply this effect to. Thanks again for taking the time to help me out with this.

          • Azazel

            I am using a AS3 FLA File with the movieclip in that.

        • I took off a few minutes to put together a compilable and commented version of this effect for you. It is in github as a gist.

          https://gist.github.com/avanderw/6087271

          Let me know if you have any troubles with it.

  • gary

    Hi Andrew- can you zip your folder structure with all the source files for this project? That will be really useful in helping me understand this very cool effect. Thanks!