はじめてのPixel BenderとShader 「墨」
...
@author @kndys
/**
* Copyright knd ( http://wonderfl.net/user/knd )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/eJWb
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
/**
* ...
* @author @kndys
*/
[SWF(width="465",height="465",backgroundColor="0x808080",frameRate="30")]
public class Main extends Sprite
{
private var loader:URLLoader;
private var _overdrive:OverdriveBitmapData;
private var _drop:BitmapData;
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);
// entry point
var bmd:BitmapData = new BitmapData(465, 465, true, 0xff808080);
_overdrive = new OverdriveBitmapData(bmd,
1 / stage.frameRate, //更新間隔
[2.5, 2.5, 2.5, 0.0], //減衰
[20, 20, 20, 0.0], //伝達速度
[-0.5, -0.5, -0.5, 0.0]); //反射係数
var bmp:Bitmap = new Bitmap(_overdrive);
addChild(bmp);
addEventListener(Event.ENTER_FRAME, loop);
//雨粒画像
var shape:Shape = new Shape();
shape.graphics.beginFill(0x333333, 1);
shape.graphics.drawCircle(5, 5, 5);
_drop = new BitmapData(shape.width, shape.height, true, 0x0);
_drop.draw(shape);
}
private function loop(e:Event):void
{
_overdrive.update(); //前回の状態と今回の状態から次回の状態を計算して更新する
//BitmapDataサブクラスなので今回の状態を簡単に加工できます
_overdrive.copyPixels(_drop, _drop.rect, new Point(465 * Math.random(), 465 * Math.random()), null, null, true);
}
}
}
/* overdrive.pbj ソース
* 非常に単純なコードです。
kernel Overdrive
{
input image4 currentImage;
input image4 previousImage;
output pixel4 nextPixel;
parameter float current;
parameter float previous;
parameter float around;
parameter float reflection;
void
evaluatePixel()
{
float2 pos = outCoord();
float4 colorN = sampleNearest(currentImage, pos + float2(0.0, -1.0));
float4 colorS = sampleNearest(currentImage, pos + float2(0.0, 1.0));
if (colorN.a == 0.0) colorN = reflection * colorS;
else if (colorS.a == 0.0) colorS = reflection * colorN;
float4 colorW = sampleNearest(currentImage, pos + float2(-1.0, 0.0));
float4 colorE = sampleNearest(currentImage, pos + float2(1.0, 0.0));
if (colorW.a == 0.0) colorW = reflection * colorE;
else if (colorE.a == 0.0) colorE = reflection * colorW;
nextPixel = float4(0.5)
+ current * (sampleNearest(currentImage, pos) - float4(0.5))
+ previous * (sampleNearest(previousImage, pos) - float4(0.5))
+ around * (colorN + colorS + colorW + colorE - float4(2.0));
}
}
*/
import flash.display.BitmapData;
import flash.display.Shader;
import flash.display.ShaderData;
import flash.display.ShaderInput;
import flash.display.ShaderJob;
import mx.utils.Base64Decoder;
/**
* ...
* @author @kndys
*/
internal class OverdriveBitmapData extends BitmapData
{
private var _previous:BitmapData;
private var _next:BitmapData;
private var _shader:Shader;
private var _job:ShaderJob;
private var _dt:Number;
private var _decay:Array;
private var _velocity:Array;
private var _reflection:Array;
/**
*
* @param initialImage 初期状態のBitmapData
* @param dt update()の間隔 [sec]
* @param decay 減衰の大きさ [1/sec] (RGBA各チャネルについてArrayで指定)
* @param velocity 伝達の速さ [pixel/sec] (RGBA各チャネルについてArrayで指定)
* @param reflection 境界での反射の仕方(RGBA各チャネルについてArrayで指定)
*/
public function OverdriveBitmapData(initialImage:BitmapData, dt:Number = 0.0, decay:Array = null, velocity:Array = null, reflection:Array = null)
{
super(initialImage.width, initialImage.height, initialImage.transparent);
super.draw(initialImage);
_previous = new BitmapData(initialImage.width, initialImage.height, initialImage.transparent);
_previous.draw(initialImage);
_next = new BitmapData(initialImage.width, initialImage.height, initialImage.transparent, 0x0);
_shader = new Shader();
var decoder:Base64Decoder = new Base64Decoder();
decoder.decode("pQEAAACkCQBPdmVyZHJpdmWgDG5hbWVzcGFjZQBvdmVyZHJpdmUAoAx2ZW5kb3IAQGtuZHlzAKAIdmVyc2lvbgABAKAMZGVzY3JpcHRpb24AAKEBAgAADF9PdXRDb29yZACjAARjdXJyZW50SW1hZ2UAowEEcHJldmlvdXNJbWFnZQChAgQBAA9uZXh0UGl4ZWwAoQEEAgAPY3VycmVudAChAQQDAA9wcmV2aW91cwChAQQEAA9hcm91bmQAoQEEBQAPcmVmbGVjdGlvbgAdAAAxAAAQADIGAIAAAAAAMgYAQL+AAAAdBgAxAACwAAEGADEGABAAMAcA8QYAsAAdBgDzBwAbADIHAIAAAAAAMgcAQD+AAAAdBwAxAACwAAEHADEHABAAMAgA8QcAsAAdBwDzCAAbADIIAIAAAAAAMggAQAAAAAAyCAAgAAAAADIIABAAAAAAKAYA8wgAGwAdAYCAAIAAADQAAAABgAAAHQgA8wUAGwADCADzBwAbAB0GAPMIABsANQAAAAAAAAAyCACAAAAAADIIAEAAAAAAMggAIAAAAAAyCAAQAAAAACgHAPMIABsAHQGAQACAAAA0AAAAAYBAAB0IAPMFABsAAwgA8wYAGwAdBwDzCAAbADYAAAAAAAAANgAAAAAAAAAyCACAv4AAADIIAEAAAAAAHQgAMQAAsAABCAAxCAAQADAJAPEIALAAHQgA8wkAGwAyCQCAP4AAADIJAEAAAAAAHQkAMQAAsAABCQAxCQAQADAKAPEJALAAHQkA8woAGwAyCgCAAAAAADIKAEAAAAAAMgoAIAAAAAAyCgAQAAAAACgIAPMKABsAHQGAgACAAAA0AAAAAYAAAB0KAPMFABsAAwoA8wkAGwAdCADzCgAbADUAAAAAAAAAMgoAgAAAAAAyCgBAAAAAADIKACAAAAAAMgoAEAAAAAAoCQDzCgAbAB0BgEAAgAAANAAAAAGAQAAdCgDzBQAbAAMKAPMIABsAHQkA8woAGwA2AAAAAAAAADYAAAAAAAAAMgoAgD8AAAAyCgBAPwAAADIKACA/AAAAMgoAED8AAAAwCwDxAACwADIMAIA/AAAAMgwAQD8AAAAyDAAgPwAAADIMABA/AAAAHQ0A8wsAGwACDQDzDAAbAB0LAPMCABsAAwsA8w0AGwAdDADzCgAbAAEMAPMLABsAMAoA8QAAsAEyCwCAPwAAADILAEA/AAAAMgsAID8AAAAyCwAQPwAAAB0NAPMKABsAAg0A8wsAGwAdCgDzAwAbAAMKAPMNABsAHQsA8wwAGwABCwDzCgAbAB0KAPMGABsAAQoA8wcAGwAdDADzCgAbAAEMAPMIABsAHQoA8wwAGwABCgDzCQAbADIMAIBAAAAAMgwAQEAAAAAyDAAgQAAAADIMABBAAAAAHQ0A8woAGwACDQDzDAAbAB0KAPMEABsAAwoA8w0AGwAdDADzCwAbAAEMAPMKABsAHQEA8wwAGwA=");
_shader.byteCode = decoder.drain(); //new OverdriveShader();
setShaderInput();
_dt = dt;
_decay = decay;
_velocity = velocity;
_reflection = reflection;
setShaderParam();
}
private function setShaderParam():void
{
if ( dt == 0.0 || _decay == null || _velocity == null || _reflection == null) return;
var dat:ShaderData = _shader.data;
var prmCurr:Array = [];
var prmPrev:Array = [];
var prmArnd:Array = [];
var prmRefl:Array = [];
dat.current.value = prmCurr;
dat.previous.value = prmPrev;
dat.around.value = prmArnd;
dat.reflection.value = prmRefl;
for (var i:int = 0; i< 4; i++)
{
var a:Number = _velocity[i] * dt;
a *= a;
var b:Number = _decay[i] * _dt;
var c:Number = 1 / (2 + b);
prmCurr[i] = (4 - 8 * a) * c;
prmPrev[i] = - (2 - b) * c;
prmArnd[i] = 2 * a * c;
prmRefl[i] = _reflection[i]
}
}
public function update():void
{
_job = new ShaderJob(_shader, _next);
_job.start(true);
_previous.draw(super);
super.draw(_next);
}
private function setShaderInput():void
{
var dat:ShaderData = _shader.data;
var current:ShaderInput = dat.currentImage;
var previous:ShaderInput = dat.previousImage;
current.input = super;
previous.input = _previous;
}
public function get dt():Number { return _dt; }
public function set dt(value:Number):void
{
_dt = value;
setShaderParam();
}
public function get decay():Array { return _decay; }
public function set decay(value:Array):void
{
_decay = value;
setShaderParam();
}
public function get velocity():Array { return _velocity; }
public function set velocity(value:Array):void
{
_velocity = value;
setShaderParam();
}
public function get reflection():Array { return _reflection; }
public function set reflection(value:Array):void
{
_reflection = value;
setShaderParam();
}
}