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

Raytracer

A slow raytracer with reflections, soft shadows (not from area lights though), crappy bloom (not HDR),  and crappy ambient occlusion. It lacks any sort of spatial partitioning for speed.
It can only render spheres and planes, currently.

Click to toggle through the composite buffer, the ao buffer, and the glow buffer.
/**
 * Copyright marmph ( http://wonderfl.net/user/marmph )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jY0r
 */

package {
    //cam is the origin
    
    import flash.display.Sprite;
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import flash.display.BitmapDataChannel;
    import flash.events.Event;
    import flash.filters.BlurFilter;
    import flash.geom.Point;
    import flash.geom.ColorTransform;
    import flash.events.MouseEvent;
        
    [SWF(backgroundColor="0", width="400", height="400", frameRate="30")]
   
    public class RayTracer extends Sprite {
        //not quite accurate
        private const WIDTH:Number = 100;
        private const HEIGHT:Number = 100;
        private const IMAGE_WIDTH:Number = 150;
        private const IMAGE_HEIGHT:Number = 150;
        private const SCALE_X:Number = WIDTH/IMAGE_WIDTH;
        private const SCALE_Y:Number = HEIGHT/IMAGE_HEIGHT;
       
        private const CENTER_X:int = IMAGE_WIDTH/2;//image-wise
        private const CENTER_Y:int = IMAGE_HEIGHT/2;
        
        private const DRAW_DISTANCE:int = 500;
        private const RED_AMBIENT:Number = 0//0.05;
        private const BLUE_AMBIENT:Number = 0;
        private const GREEN_AMBIENT:Number = 0//0.02;
        private var backgroundColor:Color;
        
        private var bufferSwitch:int = 0; //0 composite, 1 AO, 2 Bloom
        private var buffer:BitmapData; //main color/diffuse buffer
        private var bitmap:Bitmap;
        private var redBuffer:BitmapData;
        private var greenBuffer:BitmapData;
        private var blueBuffer:BitmapData;
        private var AOBuffer:BitmapData;
        private var glowBuffer:BitmapData;
        
        private var cur:Vector3D;
        private var stop:Boolean = false;
        
        ///////////////////////////////////TEMP
        private var spheres:Array = [];
        private var lights:Array = [];
        ///////////////////////////////////
        
        private var cam:Vector3D;
        
        public function RayTracer() {
            
            buffer = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0);
            bitmap = new Bitmap(buffer);
            bitmap.scaleY = stage.stageHeight/IMAGE_HEIGHT;
            bitmap.scaleX = stage.stageWidth/IMAGE_WIDTH;
            
            redBuffer = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0);
            greenBuffer = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0);
            blueBuffer = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0);
            AOBuffer = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0xffffff);
            
            addChild(bitmap);
            backgroundColor = new Color(0, 0, 0);
            
            cur = new Vector3D(0, 0, 0);
            
            cam = new Vector3D(0, 0, -80);
            
            var specularColor:Color = new Color(1, 1, 1);
            
            var reddish:Material = new Material();
            reddish.diffuseColor = new Color(1, 0, 0);
            reddish.specularColor = specularColor;
            reddish.specularSize = 0;
            reddish.specularPower = 0;
            reddish.reflectivity = 0;
            
            var blueish:Material = new Material();
            blueish.diffuseColor = new Color(0, .5, 1);
            blueish.specularColor = specularColor;
            blueish.specularSize = .05;
            blueish.specularPower = 0;
            blueish.reflectivity = 0;
            
            var greenish:Material = new Material();
            greenish.diffuseColor = new Color(0, 1, 0);
            greenish.specularColor = specularColor;
            greenish.specularSize = .05;
            greenish.specularPower = 0;
            greenish.reflectivity = 0;
            
            var grey:Material = new Material();
            grey.diffuseColor = new Color(.3, .3, .3);
            grey.specularColor = specularColor;
            grey.specularSize = .05;
            grey.specularPower = 0;
            grey.reflectivity = 0;
            
            var reflectyGrey:Material = new Material();
            reflectyGrey.diffuseColor = new Color(1, 1, 1);
            reflectyGrey.specularColor = specularColor;
            reflectyGrey.specularSize = 0;
            reflectyGrey.specularPower = 0;
            reflectyGrey.reflectivity = .8;
            
            spheres = [new Sphere(new Vector3D(0, 0, 100), 25, reflectyGrey),
             new Sphere(new Vector3D(-60, 40, 130), 40, reflectyGrey),
             new Sphere(new Vector3D(50, -40, 100), 20, greenish),
             new Sphere(new Vector3D(50, 15, 80), 25, reddish), 
             new Sphere(new Vector3D(-25, -30, 50), 20, blueish),
             new Plane(new Vector3D(0, 40, 100), new Vector3D(0, -1, 0), grey),
             new Plane(new Vector3D(0, 40, 130), new Vector3D(0, 0, -1), grey),
             ];
             
            lights = [new Light(new Vector3D(-50, -50, 80), new Color(1, 1, 1), 3000), 
             new Light(new Vector3D(50, 0, 20), new Color(.8, .8, .8), 1000), 
             new Light(new Vector3D(-50, 0, 0), new Color(.8, .8, .8), 2000),
             new Light(new Vector3D(60, 0, 110), new Color(1, 1, 5), 500),
             new Light(new Vector3D(0, -150, 50), new Color(.5, .5, .5), 8000),
             new Light(new Vector3D(-50, -50, -40), new Color(.5, .5, .5), 8000)
             ];
             
            /*lights = [new Light(new Vector3D(50, -50, 0), new Color(1, 1, 1), 8000),
             new Light(new Vector3D(-50, -50, 80), new Color(1, 1, 1), 8000)];*/
            addEventListener(Event.ENTER_FRAME, draw);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            
        }
        
        public function onMouseDown(e:MouseEvent):void {
            
            stop = false;
            bufferSwitch++;
            if(bufferSwitch > 2) bufferSwitch = 0;
            
            switch(bufferSwitch) {
                case 0:
                bitmap.bitmapData = buffer;
                break;
                case 1:
                bitmap.bitmapData = AOBuffer;
                break;
                case 2:
                bitmap.bitmapData = glowBuffer;
                break;
            }
            
        }
        
        public function draw(e:Event):void {
            
            if(stop) return;
            
            while(cur.x < IMAGE_WIDTH) {
                
                var ray:Vector3D = toRealSpace(cur).minus(cam).normalized();
                var color:Color = getColorFromCastRay(ray, cam, 4);
               
                
                var pixelColor:int = color.hex();
                buffer.setPixel(cur.x, cur.y, pixelColor);
               // buffer.setPixel(cur.x, cur.y, 0xffffff*cam.dist(ray)*.02); //for trippiness
                cur.x++;
            }
            cur.x = 0;
            cur.y++;;
            if(cur.y == IMAGE_HEIGHT)  {
                
                var p:Point = new Point(0, 0);
                
                redBuffer.copyChannel(buffer, buffer.rect, p, BitmapDataChannel.RED, BitmapDataChannel.RED);
                greenBuffer.copyChannel(buffer, buffer.rect, p, BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);
                blueBuffer.copyChannel(buffer, buffer.rect, p, BitmapDataChannel.BLUE, BitmapDataChannel.BLUE);
                
                redBuffer.threshold(redBuffer, buffer.rect, p, "<", 0x880000, 0, 0xff0000, true);
                greenBuffer.threshold(greenBuffer, buffer.rect, p, "<", 0x8800, 0, 0xff00, true);
                blueBuffer.threshold(blueBuffer, buffer.rect, p, "<", 0x88, 0, 0xff, true);
                
                glowBuffer = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0);
                glowBuffer.copyChannel(redBuffer, buffer.rect, p, BitmapDataChannel.RED, BitmapDataChannel.RED);
                glowBuffer.copyChannel(greenBuffer, buffer.rect, p, BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);
                glowBuffer.copyChannel(blueBuffer, buffer.rect, p, BitmapDataChannel.BLUE, BitmapDataChannel.BLUE);
                
                glowBuffer.applyFilter(glowBuffer, glowBuffer.rect, p, new BlurFilter(24, 24, 2));
                
               // buffer.fillRect(buffer.rect, 0xffffff);
                AOBuffer.applyFilter(AOBuffer, AOBuffer.rect, p, new BlurFilter(4, 4, 2));
                buffer.draw(AOBuffer, null, new ColorTransform(1.2, 1.2, 1.2), "multiply");
                buffer.draw(glowBuffer, null, new ColorTransform(.6, .6, .6), "add");
                
                removeEventListener(Event.ENTER_FRAME, draw);

            }
            
        }
        ///////////////////////////////////////////////////////////////////-----raydepth/////
        public function getColorFromCastRay(ray:Vector3D, origin:Vector3D, rays:int=1):Color { //returns color of material/object this ray intersects
            
            var minDist:Number = DRAW_DISTANCE;
            var closestObject:RenderObject;
               
            for each(var s:RenderObject in spheres) { //no good occlusion yet
                
                var dist:Number = s.getIntersectionDistance(ray, origin);
                if(dist <= 0) continue;
                if(isNaN(dist)) continue;
                if(dist < minDist) {
                        
                    minDist = dist;
                    closestObject = s;
                        
                }
                    
            }
                
            var color:Color = new Color(RED_AMBIENT, GREEN_AMBIENT, BLUE_AMBIENT);
                
            if(closestObject != null) {
                    
                var objectMaterial:Material = closestObject.material();
                var pixelPosition:Vector3D = origin.plus(ray.normalized().multiply(minDist));
                var normal:Vector3D = closestObject.normalAtPoint(pixelPosition);
                    
                //lighting
                for each(var light:Light in lights) {
                        
                    var lightPosition:Vector3D = light.position;
                    var lightDir:Vector3D = pixelPosition.minus(lightPosition).normalized();
                    var lightMultiplier:Number = 1;
                    
                    //shadows
                    var shadowSamples:Number = 20;
                    var shadowHits:Number = 0;
                    var lightSize:Number = 5;
                    for(var z:int = 0; z < shadowSamples; z++) {
                        
                        var sampleRad:Vector3D = new Vector3D(Math.random()*2-1, Math.random()*2-1, Math.random()*2-1).normalized().multiply(lightSize);
                        if(sampleRad.dot(lightDir) < 0) sampleRad.multiply(-1);
                        var samplePoint:Vector3D = lightPosition.plus(sampleRad);
                        for each(var obj:RenderObject in spheres) {
                            
                            if(obj == closestObject) continue;
                            
                            var diff:Number = obj.getIntersectionDistance(samplePoint.minus(pixelPosition).normalized(), pixelPosition);
                            if(diff <= 0) continue;
                            if(isNaN(diff)) continue;
                            if(diff < pixelPosition.dist(lightPosition)) {
                                shadowHits++;
                                break;
                            }
                            
                        }

                        
                    }
                    lightMultiplier = 1-shadowHits/shadowSamples;
                        
                    var normalDifference:Number = lightDir.dot(normal)*-1;
                    
                    if(normalDifference > 0) { //diffuse
                        
                        var diffuseColor:Color = objectMaterial.diffuseColor;    
                        var intensity:Number = light.distance/(lightPosition.dist(pixelPosition)*lightPosition.dist(pixelPosition));
                        color.red += light.color.red*normalDifference*intensity*diffuseColor.red*lightMultiplier;
                        color.green += light.color.green*normalDifference*intensity*diffuseColor.green*lightMultiplier;
                        color.blue += light.color.blue*normalDifference*intensity*diffuseColor.blue*lightMultiplier;
                        
                    }
                        /*
                    var reflectedRay:Vector3D = lightDir.reflectAbout(normal).normalized();
                    var reflectedRayDifference:Number = reflectedRay.dot(ray)*-1;
                    var minusSpecularSize:Number = 1-objectMaterial.specularSize;
                        
                    if(reflectedRayDifference > minusSpecularSize) { //specularish, with set parameters
                        //crap specular
                        color.red += lightMultiplier*objectMaterial.specularPower*(reflectedRayDifference-minusSpecularSize);
                        color.green += lightMultiplier*objectMaterial.specularPower*(reflectedRayDifference-minusSpecularSize);
                        color.blue += lightMultiplier*objectMaterial.specularPower*(reflectedRayDifference-minusSpecularSize);
                            
                    }
                        */
                }
                
                //ambient occlusion (even works through reflections!)
                var hits:Number = 0;
                var casts:Number = 20; //samples
                var distanceThreshold:Number = 30;
                var AOColor:Color = new Color(1, 1, 1);
                for(var i:int = 0; i < casts; i++) {
                        
                    var subray:Vector3D = new Vector3D(Math.random()*2-1, Math.random()*2-1, Math.random()*2-1);
                    subray = subray.normalized();
                    if(subray.dot(normal) < 0) {
                        subray.multiply(-1);
                    }

                    for each(var bob:RenderObject in spheres) {
                            
                        if(bob == closestObject) continue;
                        var d:Number = bob.getIntersectionDistance(subray, pixelPosition)
                        if(isNaN(d)) continue;
                        if(d <= 0) continue;
                        if(d < distanceThreshold) {
                            hits++;
                            break; 
                        }
                            
                    }
                        
                }
                    
                if(hits > 0) {
                    var aohits:Number = 1-hits/casts;
                     AOColor.red *= aohits;
                     AOColor.green *= aohits;
                     AOColor.blue *= aohits;
                     AOBuffer.setPixel(cur.x, cur.y, AOColor.hex());
                }
                    
            } else {
               //doesn't really work 
                 color.red += backgroundColor.red;
                 color.green += backgroundColor.green;
                 color.blue += backgroundColor.blue;
                    
            }
            
            if(rays > 1 && closestObject != null && objectMaterial.reflectivity > 0) {
               var john:Color = getColorFromCastRay(ray.reflectAbout(normal).normalized(), pixelPosition, rays-1);
               var minusReflectivity:Number = 1-objectMaterial.reflectivity;
               john.red = john.red*objectMaterial.reflectivity+color.red*minusReflectivity;
               john.green = john.green*objectMaterial.reflectivity+color.green*minusReflectivity;
               john.blue = john.blue*objectMaterial.reflectivity+color.blue*minusReflectivity;
               return john;
            } else {
               return color;
            }
            
        }
                
        public function toRealSpace(v:Vector3D):Vector3D {
            
            return new Vector3D((v.x-CENTER_X)*SCALE_X+cam.x, (v.y-CENTER_Y)*SCALE_Y+cam.y, v.z);
            
        }
           
    }

}

interface RenderObject {
    
    function getIntersectionDistance(ray:Vector3D, origin:Vector3D):Number;
    function normalAtPoint(p:Vector3D):Vector3D;
    
    function material():Material;
        
}

class Plane implements RenderObject {
    
    public var position:Vector3D; //any point really
    public var normal:Vector3D;
    public var _material:Material;
    
    public function Plane(sposition:Vector3D, snormal:Vector3D, material:Material):void {
        
        position = sposition;
        normal = snormal.normalized();
        _material = material;
        
    }

    
    public function getIntersectionDistance(ray:Vector3D, origin:Vector3D):Number {
        
       var dist:Number = position.minus(origin).dot(normal)/ray.dot(normal);
       return dist;
        
    }
    
    public function normalAtPoint(p:Vector3D):Vector3D {
        
        return normal;
        
    }
    
    public function material():Material {
        
        return _material;
        
    }
    
}

class Sphere implements RenderObject {
    
    public var position:Vector3D;
    public var radius:Number;
    
    public var _material:Material;

    public function Sphere(pos:Vector3D, rad:Number, material:Material):void {
        
        position = pos;
        radius = rad;
        _material = material;
        
    }
    
    public function normalAtPoint(p:Vector3D):Vector3D {
        //not normalized
        return p.minus(position).normalized();
        
    }

    //returns length along ray where intersection lies
    public function getIntersectionDistance(ray:Vector3D, origin:Vector3D):Number {
        
        var dist:Vector3D = position.minus(origin);
        var dot:Number = ray.dot(dist);
        var determinant:Number = dot*dot-dist.dot(dist)+radius*radius;
        return ray.dot(dist)-Math.sqrt(determinant);
        
    }
    
    public function material():Material {
        
        return _material;
        
    }

}

class Light {
    
    public var position:Vector3D;
    public var color:Color;
    public var distance:Number; //maximum distance at which intensity is maximum
    
    public function Light(pos:Vector3D, scolor:Color, sdistance:Number):void {
        
        position = pos;
        color = scolor;
        distance = sdistance;
        
    }

    
}

class Material {
    
    public var diffuseColor:Color;
    
    public var specularColor:Color;
    public var specularSize:Number;
    public var specularPower:Number;
    
    public var reflectivity:Number;
    
    public function Material():void {}

    
}


class Color {
    
    public var red:Number;
    public var green:Number;
    public var blue:Number;
    
    public function Color(r:Number, g:Number, b:Number):void {
        
        red = r;
        green = g;
        blue = b;
        
    }
    
    public function hex():Number { //clamped to [0, 255]
        
        return (red>1?1:red)*255 << 16 | (green>1?1:green)*255 << 8 | (blue>1?1:blue)*255;
        
    }
    
}

class Vector3D { //I know that there is a built in vector 3d class; I did this just for fun :D
        
        public var x:Number;
        public var y:Number;
        public var z:Number;
        
        public function Vector3D(sx:Number, sy:Number, sz:Number):void {
            
            x = sx;
            y = sy;
            z = sz;
            
        }
        
        public function normalized():Vector3D {
            
            var l:Number = Math.sqrt(x*x+y*y+z*z);
            return new Vector3D(x/l, y/l, z/l);
            
        }
        
        public function magnitude():Number {
            
            return Math.sqrt(x*x+y*y+z*z);
            
        }
        
        public function reflectAbout(v:Vector3D):Vector3D {
            
            return minus(v.multiply(2*v.dot(this)));
            
        }
        
        public function dist(v:Vector3D):Number {
            
            var dx:Number = x-v.x;
            var dy:Number = y-v.y;
            var dz:Number = z-v.z;                
            
            return Math.sqrt(dx*dx+dy*dy+dz*dz);
            
        }

        public function minus(v:Vector3D):Vector3D {
            
            return new Vector3D(x-v.x, y-v.y, z-v.z);
            
        }
        
        public function plus(v:Vector3D):Vector3D {
            
            return new Vector3D(x+v.x, y+v.y, z+v.z);
            
        }
        
        public function multiply(n:Number):Vector3D {
            
            return new Vector3D(x*n, y*n, z*n);
            
        }


        public function dot(v:Vector3D):Number {
            
            return x*v.x+y*v.y+z*v.z;
            
        }

        public function cross(v:Vector3D):Vector3D {
            
            return new Vector3D(y*v.z-v.y*z, z*v.x-x*v.z, x*v.y-v.x*y);
            
        }

        
    }