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

PathTracing

https://en.wikipedia.org/wiki/Path_tracing
// http://blog.weareglow.com/2011/11/path-tracing-in-flash/
Get Adobe Flash player
by YoupSolo 29 Sep 2015
    Embed
/**
 * Copyright YoupSolo ( http://wonderfl.net/user/YoupSolo )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/ljvD
 */


// https://en.wikipedia.org/wiki/Path_tracing

package
{
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    

    [SWF(backgroundColor="#000000", frameRate="60", width="465", height="465")]

    public class PathTracing extends Sprite
    {
        public static const AA:int = 1; // antialiasing
        public static const WIDTH:int = 465 * AA;
        public static const HEIGHT:int = 465 * AA;
        
        public static const CAM_NEAR:Number = 2;
        public static const CAM_FOV:Number = Math.PI * 0.3;
        public static const MAX_DEPTH:int = 5;
        
        private var scene_:Array;
        private var bmd_:BitmapData;
        private var buffer_:Vector.<Vec3>;
        private var counts_:Vector.<int>;

        public function PathTracing()
        {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            //stage.quality = StageQuality.LOW;
            stage.stageFocusRect = false;
            stage.tabChildren = false;    
            
            var i:int;
            const camera:Vec3 = new Vec3(0, 0, 2);
            scene_ = Scenes.scene00;
            
            // add random spheres
            for (i = 0; i < 2; ++i)
            {
                scene_.push(new Sphere(0.1 + 0.5 * Math.random(), new Vec3(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1), new Vec3(Math.random(), Math.random(), Math.random()), new Vec3()));
            }
            
            // transform scene to camera space
            for (i = 0; i < scene_.length; ++i)
            {
                var sphere:Sphere = scene_[i];
                sphere.center.x -= camera.x;
                sphere.center.y -= camera.y;
                sphere.center.z -= camera.z;
            }

            // display
            bmd_ = new BitmapData(WIDTH, HEIGHT, false, 0);
            // holds accumulated color            
            buffer_ = new Vector.<Vec3>(WIDTH * HEIGHT);
            // holds number of samples of that pixel
            counts_ = new Vector.<int>(WIDTH * HEIGHT);

            for (i = 0; i < WIDTH * HEIGHT; ++i)
            {
                buffer_[i] = new Vec3();
                counts_[i] = 0;
            }       

            // upside down (we're using OpenGl style coordinate system)
            var b:Bitmap = new Bitmap(bmd_);
            b.smoothing = true;
            b.scaleY = -1;
            b.y = b.height;
            b.scaleX *= 1 / AA;
            b.scaleY *= 1 / AA;
            addChild(b);            

            addEventListener(Event.ENTER_FRAME, onEnterFrame);
        }

        private function onEnterFrame(event:Event):void
        {
            var x:int, y:int;
            var c:int = 2000;
            
            while (c--)
            {
                shootRay(Math.random() * WIDTH, Math.random() * HEIGHT);
            }            
            
            bmd_.lock();
            
            for (y = 0; y < HEIGHT; ++y)
            {
                for (x = 0; x < WIDTH; ++x)
                {
                    const i:int = y * WIDTH + x;
                    if (counts_[i])
                    {
                        var color:Vec3 = buffer_[i];
                        const r:int = Utils.clampi(color.x * 255 / counts_[i], 0, 0xff);
                        const g:int = Utils.clampi(color.y * 255 / counts_[i], 0, 0xff);
                        const b:int = Utils.clampi(color.z * 255 / counts_[i], 0, 0xff);
                        bmd_.setPixel(x, y, (r << 16) | (g << 8) | b);
                    }
                }
            }            

            bmd_.unlock();

        }

        private function shootRay(x:Number, y:Number):void
        {
            var ray:Ray = new Ray();
            ray.o.x = 0; 
            ray.o.y = 0;
            ray.o.z = 0;
            
            const nx:Number = (2 * (x / WIDTH) - 1) * WIDTH / HEIGHT;
            const ny:Number = 2 * (y / HEIGHT) - 1;
            
            ray.d.x = CAM_NEAR * Math.tan(CAM_FOV * 0.5) * nx; 
            ray.d.y = CAM_NEAR * Math.tan(CAM_FOV * 0.5) * ny;
            ray.d.z = -CAM_NEAR; 
            
            ray.d = Utils.norm(ray.d);
            const i:int = int(y) * WIDTH + int(x);
            var c:Vec3 = radiance(ray, 0);
            buffer_[i].x += c.x;
            buffer_[i].y += c.y;
            buffer_[i].z += c.z;
            
            counts_[i]++;
        }

        private function radiance(ray:Ray, depth:int):Vec3
        {
            var i:int;
            var sphere:Sphere;
            var t:Number;
            
            if (depth > MAX_DEPTH)
                return new Vec3();
                
            var nearest:Number = Infinity;
            var collider:Sphere = null;
                        
            for (i = 0; i < scene_.length; ++i)
            {
                sphere = scene_[i];
                t = sphere.intersect(ray);
                if (t > 0 && t < nearest)
                {
                    nearest = t;
                    collider = sphere;
                }
            }           

            if (collider == null)
                return new Vec3();

            // compute new ray
            var out:Ray = new Ray();
            
            out.o.x = ray.o.x + nearest * ray.d.x;
            out.o.y = ray.o.y + nearest * ray.d.y;
            out.o.z = ray.o.z + nearest * ray.d.z;
            
            var normal:Vec3 = new Vec3();
            normal.x = out.o.x - collider.center.x; 
            normal.y = out.o.y - collider.center.y; 
            normal.z = out.o.z - collider.center.z; 
            normal = Utils.norm(normal);            

            var tangent:Vec3 = new Vec3();
            
            if (Math.abs(normal.x) < Utils.EPSILON)
            {
                tangent.x = 0;
                tangent.y = -normal.z;
                tangent.z = normal.y;
            }
            else
            {
                tangent.x = normal.y;
                tangent.y = -normal.x;
                tangent.z = 0;
            }            

            tangent = Utils.norm(tangent);         

            const bitangent:Vec3 = normal.cross(tangent);           

            // random ray shooting from the normal hemisphere
            const angle:Number = Math.random() * Math.PI * 2;
            const radius2:Number = Math.random();
            const radius:Number = Math.sqrt(radius2);            

            const cos:Number = Math.cos(angle) * radius;
            const sin:Number = Math.sin(angle) * radius;
            const sqrt:Number = Math.sqrt(1 - radius2);            

            out.d.x = tangent.x * cos + bitangent.x * sin + normal.x * sqrt;
            out.d.y = tangent.y * cos + bitangent.y * sin + normal.y * sqrt;
            out.d.z = tangent.z * cos + bitangent.z * sin + normal.z * sqrt;            

            out.d = Utils.norm(out.d);           

            // compute new color

            var color:Vec3 = radiance(out, depth + 1);
            color.x *= collider.color.x;
            color.y *= collider.color.y;
            color.z *= collider.color.z;

            color.x += collider.emission.x;
            color.y += collider.emission.y;
            color.z += collider.emission.z;           

            return color;
        }
    }
}


class Vec3
{
    public var x:Number;
    public var y:Number;
    public var z:Number;    

    public function Vec3(x:Number = 0, y:Number = 0, z:Number = 0)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }    

    public function cross(v:Vec3):Vec3
    {
        var o:Vec3 = new Vec3();
        
        o.x = y * v.z - z * v.y;
        o.y = z * v.x - x * v.z;
        o.z = x * v.y - y * v.x;        

        return o;
    }
}

class Ray
{
    public var o:Vec3 = new Vec3();
    public var d:Vec3 = new Vec3();
}

class Sphere
{
    public var radius:Number;
    public var center:Vec3;
    public var color:Vec3;
    public var emission:Vec3;   

    public function Sphere(radius:Number, center:Vec3, color:Vec3, emission:Vec3)
    {
        this.radius = radius;    
        this.center = center;    
        this.color = color;    
        this.emission = emission;    
    }

    public function intersect(ray:Ray):Number
    {
        const px:Number = ray.o.x - center.x;
        const py:Number = ray.o.y - center.y;
        const pz:Number = ray.o.z - center.z;        

        const pp:Number = px * px + py * py + pz * pz;
        const pd:Number = px * ray.d.x + py * ray.d.y + pz * ray.d.z;        

        const A:Number = 1;
        const B:Number = 2 * pd;
        const C:Number = pp - radius * radius;       

        const det:Number = B * B - 4 * A * C;
     
        if (det < 0)

            return -1;        

        const det2:Number = Math.sqrt(det);       
        const sol:Number = (-B - det2) / (2 * A);       

        if (sol < 0)
            return -1;       

        return sol;
    }
}


class Utils
{
    public static const EPSILON:Number = 0.0001;   

    public static function clamp(v:Number, min:Number, max:Number):Number
    {
        return Math.max(Math.min(v, max), min);
    }
    
    public static function clampi(v:int, min:int, max:int):int
    {
        return Math.max(Math.min(v, max), min);
    }
  
    public static function norm(v:Vec3):Vec3
    {
        var o:Vec3 = new Vec3(v.x, v.y, v.z);
        const dd:Number = v.x * v.x + v.y * v.y + v.z * v.z;
        
        if (dd == 0) return o;

        const invd:Number = 1 / Math.sqrt(dd);
        o.x *= invd;        
        o.y *= invd;        
        o.z *= invd;               
        
        return o;
    }
}    

class Scenes
{
    public static const R:Number = 1e5;
    public static const G:Number = 0.5;   

    public static const scene00:Array = 
    [
      new Sphere(R, new Vec3(-R-1, 0, 0), new Vec3(G, G, G), new Vec3()),//left
      new Sphere(R, new Vec3(R+1, 0, 0), new Vec3(G, G, G), new Vec3()),//right
      new Sphere(R, new Vec3(0, 0, R+1), new Vec3(G, G, G), new Vec3()),//front
      new Sphere(R, new Vec3(0, 0, -R-1), new Vec3(G, G, G), new Vec3()),//back

      new Sphere(R, new Vec3(0, R+1, 0), new Vec3(G, G, G), new Vec3()),//top

      new Sphere(R, new Vec3(0, -R-1, 0), new Vec3(G, G, G), new Vec3()),//bottom
      new Sphere(0.5, new Vec3(0, 1+0.25, 0), new Vec3(), new Vec3(12,12,12))//light
    ];
}