Stage3D Smoke
Bigger version and Javascript/webgl version on my blog :
http://blog.bongiovi.tw/?p=418
/**
* Copyright bongiovi015 ( http://wonderfl.net/user/bongiovi015 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5Cfp
*/
package {
import com.bit101.components.HSlider;
import com.bit101.components.Label;
import com.bit101.components.Window;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.Texture;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import net.hires.debug.Stats;
[SWF(width=465, height=465, frameRate=60, backgroundColor=0)]
public class Smoke extends Sprite {
public static const URL_LIGHT : String = "http://www.bongiovi.tw/experiments/smokes/bridgeLight512.png";
public static const URL_BG : String = "http://www.bongiovi.tw/experiments/smokes/bridgeBG512.jpg";
private var context:Context3D;
// PERLIN NOISE
private var _bmpdPerlin:BitmapData;
private var _seed:int = Math.random() * 0xFFFF;
private var _offset:Array = [new Point, new Point];
// BITMAPDATAS AND TEXTURES
private var _bmpdLight:BitmapData;
private var _bmpdBg:BitmapData;
private var _textureOrg:Texture;
private var _textureBg:Texture;
// SHADER PROGRAMS
private var _passCopy:Pass;
private var _passCopyToTexture:Pass;
private var _passAdd:Pass;
private var _passDecrease:Pass;
private var _passDisplace:Pass;
// BUFFERS
private var vbuffer:VertexBuffer3D;
private var ibuffer:IndexBuffer3D;
// OTHERS AND CONTROLS
private var _isFirstTime:Boolean = true;
private var _preTexture:Texture;
private var _sliderRange:HSlider;
private var _sliderDescrease:HSlider;
private var _sliderSpeed:HSlider;
public function Smoke() {
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, _onContext);
stage.stage3Ds[0].requestContext3D();
addChild(new Stats).y = 465 - 100;
}
protected function _onContext(event:Event):void {
context = stage.stage3Ds[0].context3D;
context.configureBackBuffer(465, 465, 1, false);
context.enableErrorChecking = true;
context.setBlendFactors( Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onBgLoaded);
loader.load(new URLRequest(URL_BG), new LoaderContext(true));
}
private function _onBgLoaded(event:Event):void {
_bmpdBg = event.currentTarget.content.bitmapData;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLightLoaded);
loader.load(new URLRequest(URL_LIGHT), new LoaderContext(true));
}
private function _onLightLoaded(event:Event):void {
_bmpdLight = event.currentTarget.content.bitmapData;
_initTexture();
_initPerlin();
_initBuffer();
_initShader();
_initControl();
addEventListener(Event.ENTER_FRAME, _loop);
}
private function _initControl():void {
var window:Window = new Window(this, 255, 5, "Controls");
window.width = 200;
window.height = 80;
_sliderRange = new HSlider(window.content, 5, 5);
_sliderRange.value = 4;
new Label(window.content, 110, 0, "Smoke Range");
_sliderDescrease = new HSlider(window.content, 5, 25);
_sliderDescrease.value = 5;
new Label(window.content, 110, 20, "Alpha Decrease");
_sliderSpeed = new HSlider(window.content, 5, 45);
_sliderSpeed.value = 50;
new Label(window.content, 110, 40, "Movement Speed");
}
private function _initPerlin():void {
_bmpdPerlin = new BitmapData(128, 128, false, 0);
}
private function _initShader():void {
_passCopy = new PassCopy(context, true, false).assemble();
_passCopyToTexture = new PassCopy(context, false, true, 512, 512).assemble();
_passAdd = new PassAdd(context, false, true, 512, 512).assemble();
_passDecrease = new PassColorAdd(context, false, true, 512, 512).assemble();
_passDisplace = new PassDisplacement(context, false, true, 512, 512).assemble();
}
private function _initBuffer():void {
ibuffer = context.createIndexBuffer(6);
ibuffer.uploadFromVector(Vector.<uint>([0, 1, 2, 0, 2, 3]), 0, 6);
vbuffer = context.createVertexBuffer(4, 5);
var imageSize:Number = 1;
var vbuf:Vector.<Number> = Vector.<Number>([
-imageSize, imageSize, 0, 0, 0,
imageSize, imageSize, 0, 1, 0,
imageSize, -imageSize, 0, 1, 1,
-imageSize, -imageSize, 0, 0, 1
]);
vbuffer.uploadFromVector(vbuf, 0, 4);
}
private function _initTexture():void {
_textureOrg = createTexture(context, _bmpdLight);
_textureBg = createTexture(context, _bmpdBg);
}
private function _loop(event:Event):void {
_bmpdPerlin.perlinNoise(_bmpdPerlin.width, _bmpdPerlin.height, 8, _seed, false, false, 3, false, _offset);
var texturePerlin:Texture = createTexture(context, _bmpdPerlin);
var texture:Texture;
var mtx:Matrix3D = new Matrix3D();
context.setVertexBufferAt(0, vbuffer, 0, "float3");
context.setVertexBufferAt(1, vbuffer, 3, "float2");
if(!_isFirstTime) {
context.setTextureAt(0, _preTexture);
context.setTextureAt(1, _textureOrg);
_passAdd.render(ibuffer);
texture = _passAdd.getTexture();
}
var numIter:int = 10;
var range:Number = _sliderRange.value / 4000 + 0.002;
var alphaDecrease:Number = _sliderDescrease.value / 4000 + .004;
for(var i:int=0; i<numIter; i++) {
context.setTextureAt(0, texture == null ? _textureOrg : texture);
context.setTextureAt(1, texturePerlin);
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([-.5, -.5, range, 1-i/numIter*alphaDecrease]) );
_passDisplace.render(ibuffer);
context.setTextureAt(1, null);
context.setTextureAt(0, _passDisplace.getTexture());
context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mtx, true);
_passCopyToTexture.render(ibuffer);
texture = _passCopyToTexture.getTexture();
}
context.setTextureAt(0, texture);
context.setTextureAt(1, null);
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([0, 0, 0, -0.02]));
_passDecrease.render(ibuffer);
_preTexture = _passDecrease.getTexture();
context.setTextureAt(0, _preTexture);
context.setTextureAt(1, _textureBg);
_passAdd.render(ibuffer);
context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mtx, true);
context.setVertexBufferAt(0, vbuffer, 0, "float3");
context.setVertexBufferAt(1, vbuffer, 3, "float2");
context.setTextureAt(0, _passAdd.getTexture());
context.setTextureAt(1, null);
_passCopy.render(ibuffer);
context.present();
var speed:Number = _sliderSpeed.value/100 + .1;
_offset[0].x += speed;
_offset[1].y += speed;
_isFirstTime = false;
}
public static function createTexture(context:Context3D, source:BitmapData) : Texture {
var w:Number, h:Number, level:int=0;
w = source.width, h = source.height;
var texture:Texture = context.createTexture(w, h, Context3DTextureFormat.BGRA, true);
var bmpd:BitmapData;
var mtx:Matrix = new Matrix;
while(w&&h) {
bmpd = new BitmapData(w, h, true, 0);
bmpd.draw(source, mtx, null, null, null, true);
texture.uploadFromBitmapData(bmpd, level);
w >>= 1;
h >>= 1;
mtx.scale(.5, .5);
level++;
}
bmpd.dispose();
return texture;
}
}
}
import com.adobe.utils.AGALMiniAssembler;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.textures.Texture;
import flash.utils.ByteArray;
class Pass {
protected static const agal:AGALMiniAssembler = new AGALMiniAssembler();
protected var _shaderVertex:String;
protected var _shaderFragment:String;
protected var _program:Program3D;
protected var _context:Context3D;
protected var _isRenderBackToBuffer:Boolean;
protected var _isRenderToTexture:Boolean;
protected var _texture:Texture;
public function Pass(context:Context3D, isRenderBackToBuffer:Boolean, isRenderToTexture:Boolean, width:Number=1, height:Number=1) {
_context = context;
_isRenderBackToBuffer = isRenderBackToBuffer;
_isRenderToTexture = isRenderToTexture;
if(_isRenderToTexture) _texture = _context.createTexture(width, height, Context3DTextureFormat.BGRA, true);
}
public function assemble() : Pass {
var vertexShader:ByteArray = agal.assemble(Context3DProgramType.VERTEX, _shaderVertex);
var fragmentShader:ByteArray = agal.assemble(Context3DProgramType.FRAGMENT, _shaderFragment);
_program = _context.createProgram();
_program.upload(vertexShader, fragmentShader);
return this;
}
public function render(iBuffer:IndexBuffer3D) : void {
if(_isRenderBackToBuffer) _context.setRenderToBackBuffer();
else _context.setRenderToTexture(_texture, false, 1);
_context.clear(0, 0, 0, 0);
_context.setProgram(_program);
_context.drawTriangles(iBuffer);
}
public function getTexture() : Texture { return _texture; }
}
import flash.display3D.Context3D;
class PassCopy extends Pass {
public function PassCopy(context:Context3D, isRenderBackToBuffer:Boolean, isRenderToTexture:Boolean, width:Number=1, height:Number=1) {
super(context, isRenderBackToBuffer, isRenderToTexture, width, height);
_shaderVertex = "" +
"m44 op, va0, vc0\n" +
"mov v0, va1\n";
_shaderFragment = "" +
"tex oc, v0, fst0, <2s, clamp, linear>\n";
}
}
import flash.display3D.Context3D;
class PassAdd extends Pass {
public function PassAdd(context:Context3D, isRenderBackToBuffer:Boolean, isRenderToTexture:Boolean, width:Number=1, height:Number=1) {
super(context, isRenderBackToBuffer, isRenderToTexture, width, height);
_shaderVertex = "" +
"mov op, va0\n" +
"mov v0, va1\n";
// fs0:texture0;
// fst1:texture1;
_shaderFragment = "" +
"tex ft0, v0, fs0, <2d, clamp, linear>\n" +
"tex ft1, v0, fs1, <2d, clamp, linear>\n" +
"add oc, ft0, ft1\n";
}
}
import flash.display3D.Context3D;
class PassColorAdd extends Pass {
public function PassColorAdd(context:Context3D, isRenderBackToBuffer:Boolean, isRenderToTexture:Boolean, width:Number=1, height:Number=1) {
super(context, isRenderBackToBuffer, isRenderToTexture, width, height);
_shaderVertex = "" +
"mov op, va0\n" +
"mov v0, va1\n";
_shaderFragment = "" +
"tex ft0, v0, fs0, <2d,clamp,linear>\n" +
"add oc, ft0, fc0\n";
}
}
import flash.display3D.Context3D;
class PassDisplacement extends Pass {
public function PassDisplacement(context:Context3D, isRenderBackToBuffer:Boolean, isRenderToTexture:Boolean, width:Number=1, height:Number=1) {
super(context, isRenderBackToBuffer, isRenderToTexture, width, height);
_shaderVertex = "" +
"mov op, va0\n" +
"mov v0, va1\n";
// fc0 : [-.5, -.5, .5, .8];
_shaderFragment = "" +
"tex ft0, v0, fs1, <2d,clamp,linear>\n" + // map perlin(fs1) to ft0
"add ft1, ft0, fc0\n" + // displacement ( x-.5, y-.5)
"mul ft1, ft1, fc0.z\n" + // multiply by the size
"add ft2, v0, ft1\n" + // add to coordinate
"tex ft3, ft2, fs0, <2d,clamp,linear>\n" + // map to outpur color
"mov ft3.w, fc0.w\n" +
"mov oc, ft3\n";
}
}