In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

God Rays with AGAL

"God Rays" / Fake Volumetric lighting.. 
See: http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html
@author Devon O.
Get Adobe Flash player
by devon_o 10 Mar 2013
/**
 * Copyright devon_o ( http://wonderfl.net/user/devon_o )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jaOM
 */

package
{
    /**
     * "God Rays" / Fake Volumetric lighting.. 
     * See: http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html
     * @author Devon O.
     */
    
    import com.adobe.utils.AGALMiniAssembler;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.Program3D;
    import flash.display3D.textures.Texture;
    import flash.display3D.VertexBuffer3D;
    import flash.events.Event;
    import flash.geom.Matrix3D;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;

    [SWF(width="465", height="465", frameRate="60", backgroundColor="#000000")]
    public class VolumetricLight extends Sprite
    {
        public static const NUM_SAMPLES:int = 30;
        
        // lightx, lighty
        private var lightPos:Vector.<Number> = Vector.<Number>( [ .5, .5, 1, 1 ]);
        
        // numsamples, density, numsamples * density, 1 / numsamples * density
        private var values1:Vector.<Number> = Vector.<Number>( [ 1, 1, 1, 1 ]);
        
        // weight, decay, exposure
        private var values2:Vector.<Number> = Vector.<Number>( [ 1, 1, 1, 1 ]);    
        
        private var context3d:Context3D;
        private var vertBuffer:VertexBuffer3D;
        private var indexBuffer:IndexBuffer3D; 
        private var program:Program3D;
        private var matrix:Matrix3D = new Matrix3D();
        private var textureData:BitmapData;
        
        public function VolumetricLight()
        {    
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);    
        }
        
        private function init(event:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            initStage();
            loadImage();
            
            addEventListener(Event.ENTER_FRAME, onTick);
        }
        
        
        private function loadImage():void {
            var l:Loader = new Loader();
            l.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
            l.load(new URLRequest("http://assets.wonderfl.net/images/related_images/5/5f/5f28/5f28b9db30e27c26c176844b8cf5ac1d3ba0286e"), new LoaderContext(true));
        }
        
        private function onImageLoad(event:Event = null):void
        {
            event.currentTarget.removeEventListener(Event.COMPLETE, onImageLoad);
            var l:Loader = (event.currentTarget as LoaderInfo).loader;
            this.textureData = (l.content as Bitmap).bitmapData;    
            stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initStage3d );
            stage.stage3Ds[0].requestContext3D();
        }
        
        private function initStage():void
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
        }
        
        private function initStage3d(event:Event):void
        {
            this.context3d = stage.stage3Ds[0].context3D;            
            this.context3d.configureBackBuffer(stage.stageWidth, stage.stageHeight, 1, true);
            
            var vertices:Vector.<Number> = Vector.<Number>([
            //      x         y        z            u  v
                -1.0,  -1.0,     0,          0, 0, 
                -1.0,      1.0,     0,             0, 1,
                 1.0,      1.0,     0,             1, 1,
                 1.0,  -1.0,     0,            1, 0  ]);
                 
            
            this.vertBuffer = this.context3d.createVertexBuffer(4, 5);
            this.vertBuffer.uploadFromVector(vertices, 0, 4);

            this.indexBuffer = this.context3d.createIndexBuffer(6);            
            this.indexBuffer.uploadFromVector (Vector.<uint>([0, 1, 2, 2, 3, 0]), 0, 6);
            
            this.texture = this.context3d.createTexture(this.textureData.width, this.textureData.height, Context3DTextureFormat.BGRA, true);
            this.texture.uploadFromBitmapData(this.textureData);
            
            this.context3d.setVertexBufferAt(0, this.vertBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
            this.context3d.setVertexBufferAt(1, this.vertBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
            
            generateProgram();
        }
        
        
        private function generateProgram():void
        {
            var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
            vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
                "m44 op, va0, vc0        \n" +
                "mov v0, va1              "
            );
            
            var frag:String = "";
            
            // Calculate vector from pixel to light source in screen space.
            frag += "sub ft0.xy, v0.xy, fc0.xy \n";
            
            // Divide by number of samples and scale by control factor.  
            frag += "mul ft0.xy, ft0.xy, fc1.ww \n";
            
            // Store initial sample.  
            frag += "tex ft1,  v0, fs0 <2d, clamp, linear, mipnone> \n";
            
            // Set up illumination decay factor.  
            frag += "mov ft2.x, fc0.w \n";
            
            // Store the texcoords
            frag += "mov ft4.xy, v0.xy \n";
            
            for (var i:int = 0; i < NUM_SAMPLES; i++)
            {
                // Step sample location along ray. 
                frag += "sub ft4.xy, ft4.xy, ft0.xy \n";
                
                // Retrieve sample at new location.  
                frag += "tex ft3,  ft4.xy, fs0 <2d, clamp, linear, mipnone> \n";
                
                // Apply sample attenuation scale/decay factors.  
                frag += "mul ft2.y, ft2.x, fc2.x \n";
                frag += "mul ft3.xyz, ft3.xyz, ft2.yyy \n";
                
                // Accumulate combined color.  
                frag += "add ft1.xyz, ft1.xyz, ft3.xyz \n";
                
                // Update exponential decay factor.  
                frag += "mul ft2.x, ft2.x, fc2.y \n";
            }
            
            // Output final color with a further scale control factor. 
            frag += "mul ft1.xyz, ft1.xyz, fc2.zzz \n";
            frag += "mov oc, ft1";

            var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
            fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT, frag);
            
            this.program = this.context3d.createProgram();
            this.program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
        }
        
        private var texture:Texture;
        private function onTick(event:Event):void
        {
            if ( !this.context3d ) 
                return;
            
            this.context3d.clear ( 0, 0, 0, 1 );
            this.context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, this.matrix, true);
            this.context3d.setTextureAt(0, this.texture);
            this.context3d.setProgram(this.program);
            
            // light position
            this.lightPos[0] = stage.mouseX / stage.stageWidth;
            this.lightPos[1] = (stage.stageHeight - stage.mouseY) / stage.stageHeight;
            
            // numsamples, density, numsamples * density, 1 / numsamples * density
            this.values1[0] = NUM_SAMPLES;
            this.values1[1] = 2.25;
            this.values1[2] = NUM_SAMPLES * values1[1];
            this.values1[3] = 1 / values1[2];
            
            // weight, decay, exposure
            this.values2[0] = .50;
            this.values2[1] = .87;
            this.values2[2] = .35;
            
            this.context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, this.lightPos, 1 );    
            this.context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, this.values1,  1 );
            this.context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, this.values2,  1 );
            
            this.context3d.drawTriangles(this.indexBuffer);
            this.context3d.present();
            
        }
    }
}