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

Real Time Ray Tracing (forked and optimized)

Forked and optimized. Pay tribute to the original author of the code!

Changes:
* draw() - no more setPixel, it now uses setVector
* draw() -tow ln parts rewritten
* sphere() - exchanged trig functions to inlined approximate polynomials

Result:
Original peaks at ~60
This peaks at ~70
(when target fps = 120)
/**
 * Copyright antalg ( http://wonderfl.net/user/antalg )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/heXJ
 */

// forked from keim_at_Si's Real Time Ray Tracing
package {
    import flash.events.*;
    import flash.display.*;
    import flash.utils.getTimer;
    import net.hires.debug.Stats;
    
    [SWF(frameRate='60')]
    public class main extends Sprite {
        function main() {
            addEventListener(Event.ADDED_TO_STAGE, _onAddedToStage);
        }
        
        private function _onAddedToStage(e:Event) : void {
            removeEventListener(Event.ADDED_TO_STAGE, _onAddedToStage);
            var bitmap:Bitmap = new Bitmap(screen = new BitmapData(size, size, false, 0));
            if (size<232) bitmap.scaleX = bitmap.scaleY = 2;
            bitmap.x = bitmap.y = 232 - size * bitmap.scaleX * 0.5;
            addChild(bitmap);
            addChild(new Stats());
            setup();
            addEventListener(Event.ENTER_FRAME, _onEnterFrame);
            
            startTime = getTimer();
        }
        
        // Added
        private var startTime:Number;        
        //
        
        private function _onEnterFrame(e:Event) : void {
            mx = (mouseX - 232.5) * 0.00390625;
            my = (232.5 - mouseY) * 0.00390625;
            
            // Changed
            time = (getTimer() - startTime) * 0.001;
            //
            
            draw();
        }
    }
}


// parameters, you can modify them.
var size:int = 192;
var ambient:Number = 0.1;
var diffusion:Number = 0.7;
var specular:Number = 0.6;
var power:Number = 12;


import flash.display.*;
import flash.geom.*

class Material {
    public var lmap:Vector.<uint> = new Vector.<uint>(512, true);
    function Material(col:uint, amb:Number, dif:Number) {
        var i:int, a:Number, r:int=col>>16, g:int=(col>>8)&255, b:int=col&255;
        for (i=0; i<512; i++) {
            a = ((i<256) ? amb : ((((i-256) * (dif - amb)) * 0.00390625) + amb)) * 2;
            if (a<1) {
                lmap[i] = ((r*a)<<16)|((g*a)<<8)|((b*a)<<0);
            } else {
                lmap[i] = ((255-(255-r)*(2-a))<<16)|((255-(255-g)*(2-a))<<8)|(((255-(255-b)*(2-a)))<<0);
            }
        }
    }
}

var t:Boolean = true;

class Sphere {
    public var x:Number, y:Number, z:Number, r2:Number, mat:Material;
    public var cx:Number, cy:Number, cz:Number, cr:Number, omg:Number, pha:Number;
    function Sphere(cx:Number, cy:Number, cz:Number, cr:Number, omg:Number, pha:Number, r:Number, mat:Material) {
        this.cx = cx;
        this.cy = cy;
        this.cz = cz;
        this.cr = cr;
        this.omg = omg;
        this.pha = pha;
        this.r2 = r * r;
        this.mat = mat;
    }
    
    public function update() : void {
        var v:Number = time * omg + pha;
        
        while(v > Math.PI) {
            v -= 2*Math.PI;
        }
        
        if(v>0) z = v*(B + C*v)*cr + cz;
        else    z = v*(B - C*v)*cr + cz;
        
        y = cy;
        
        v += HALFPI;
        if(v>Math.PI) v -= TWOPI;
        if(v>0) x = v*(B + C*v)*cr + cx;
        else    x = v*(B - C*v)*cr + cx;
        
    }
    
    private const B:Number = 4.0/Math.PI;
    private const C:Number = -4.0/(Math.PI*Math.PI);
    private const TWOPI:Number = 2*Math.PI;
    private const HALFPI:Number = .5*Math.PI;
}

var focusZ:Number = size;
var floorY:Number = 100;
var screen:BitmapData;
var initialDir:Vector.<Number> = new Vector.<Number>(size*size*3, true);
var spheres:Vector.<Sphere> = new Vector.<Sphere>();
var ldir:Vector3D = new Vector3D();
var light:Vector3D = new Vector3D(100,100,100);
var smap:Vector.<uint> = new Vector.<uint>(512, true);
var fcol:Vector.<uint> = new Vector.<uint>(1024, true);
var refs:Vector.<uint> = Vector.<uint>([0, 1, 2, 3, 3]);
var reff:Vector.<uint> = Vector.<uint>([0xffffff, 0x7f7f7f, 0x3f3f3f, 0x1f1f1f, 0x1f1f1f]);
var time:Number, mx:Number, my:Number, camX:Number, camY:Number, camZ:Number, tcamX:Number, tcamY:Number, tcamZ:Number;

function setup() : void {
    var i:int, j:int, idx:int, l:Number, s:int, hs:Number = (size - 1) * 0.5;
    for (j=0,idx=0; j<size; j++) for (i=0; i<size; i++) {
        l = 1/Math.sqrt((i - hs)*(i - hs) + (j - hs)*(j - hs) + focusZ*focusZ);
        initialDir[idx] = (i-hs) * l; idx++;
        initialDir[idx] = (j-hs) * l; idx++;
        initialDir[idx] = focusZ   * l; idx++;
    }
    for (i=0; i<512; i++) {
        s = (i<56) ? 64 : int(Math.pow((i-256) * 0.00390625, power) * (power + 2) * specular * 0.15915494309189534 * 192 + 64);
        if (s > 255) s = 255;
        smap[i] = 0x10101 * s;
        s = (i<256) ? (255-i) : (i-256);
        fcol[i] = 0x10101 * (s - (s>>3) + 31);
        fcol[i+512] = 0x10101 * ((s>>2) - (s>>5) + 31);
    }
    spheres.push(new Sphere(100, 40, 600, 200, 0.3, 1.0, 60, new Material(0x8080ff,ambient,diffusion)));
    spheres.push(new Sphere(  0, 50, 300, 100, 0.8, 0.8, 50, new Material(0x80ff80,ambient,diffusion)));
    spheres.push(new Sphere( 50, 60, 200, 200, 0.6, 2.0, 40, new Material(0xff8080,ambient,diffusion)));
    spheres.push(new Sphere(-50, 70, 500, 300, 0.4, 1.4, 30, new Material(0xc0c080,ambient,diffusion)));
    spheres.push(new Sphere(-90, 30, 600, 400, 0.2, 1.5, 70, new Material(0xc080c0,ambient,diffusion)));
    spheres.push(new Sphere( 70, 80, 400, 100, 0.7, 1.2, 20, new Material(0x80c0c0,ambient,diffusion)));
    camX = camY = camZ = tcamX = tcamY = tcamZ = 0;
}

// Added
var pixels:Vector.<uint> = new Vector.<uint>(192*192, true);
//

function draw() : void {
    screen.lock();
    
    var i:int, j:int, k:int, l:int, idx:int, t:Number, tmin:Number, n:Number, s:Sphere, 
        ln:int, pixel:uint, hit:Sphere, a:int, kmax:int, 
        ox:Number, oy:Number, oz:Number, dx:Number, dy:Number, dz:Number, nx:Number, ny:Number, nz:Number,
        dsx:Number, dsy:Number, dsz:Number, B:Number, C:Number, D:Number;
    light.x = Math.cos(time*0.6)*100;
    light.y = Math.sin(time*1.1)*25+100;
    light.z = Math.sin(time*0.9)*100-100;
    ldir.x = -light.x;
    ldir.y = -light.y;
    ldir.z = -light.z;
    ldir.normalize();
    
    tcamX = mx * 400;
    tcamY = my * 150 - 50;
    tcamZ = my * 400 - 200;
    camX += (tcamX - camX) * 0.02;
    camY += (tcamY - camY) * 0.02;
    camZ += (tcamZ - camZ) * 0.02;
    
    kmax = spheres.length;
    for (k=0; k<kmax; k++) spheres[k].update();
    
    // Added  
    var wdt:int = screen.width;
    //
    
    for (j=0,idx=0; j<size; j++) {
        // Added
        var rowStart:int = j*wdt;
        //   
        
        for (i=0; i<size; i++) {
            ox = camX;
            oy = camY;
            oz = camZ;
            dx = initialDir[idx]; idx++;
            dy = initialDir[idx]; idx++;
            dz = initialDir[idx]; idx++;
            
            pixel = 0;
            for (l=1; l<5; l++) {
                tmin = 99999;
                hit = null;
                for (k=0; k<kmax; k++) {
                    s = spheres[k];
                    dsx = ox - s.x;
                    dsy = oy - s.y;
                    dsz = oz - s.z;
                    B = dsx * dx + dsy * dy + dsz * dz;
                    C = dsx * dsx + dsy * dsy + dsz * dsz - s.r2;
                    D = B * B - C;
                    if (D > 0) {
                        t = - B - Math.sqrt(D);
                        if ((t > 0) && (t < tmin)) {
                            tmin = t;
                            hit = s;
                        }
                    }
                }

                if (hit) {
                    ox += dx * tmin;
                    oy += dy * tmin;
                    oz += dz * tmin;
                    nx = ox - hit.x;
                    ny = oy - hit.y;
                    nz = oz - hit.z;
                    n = 1/Math.sqrt(nx*nx + ny*ny + nz*nz);
                    nx *= n;
                    ny *= n;
                    nz *= n;
                    n = -(nx*dx + ny*dy + nz*dz) * 2;
                    dx += nx * n;
                    dy += ny * n;
                    dz += nz * n;
                    // Changed
                    ln = (ldir.x * nx + ldir.y * ny + ldir.z * nz + 1) * 256;
                    //
                    a = hit.mat.lmap[ln];
                    a >>= refs[l];
                    a &= reff[l];
                    pixel += a;
                } else {
                    if (dy < 0) {
                        // Changed
                        ln = (ldir.x * dx + ldir.y * dy + ldir.z * dz + 1) * 256;
                        //      
                        a = smap[ln];
                        ln = l - 1;
                        a >>= refs[ln];
                        a &= reff[ln];
                        pixel += a;
                        break;
                    } else {
                        tmin = (floorY-oy)/dy;
                        ox += dx * tmin;
                        oy += dy * tmin;
                        oz += dz * tmin;
                        dy = -dy;
                        ln = (dy*256) + (((((ox+oz)>>7)+((ox-oz)>>7))&1)<<9) + 256;
                        a = fcol[ln];
                        a >>= refs[l];
                        a &= reff[l];
                        pixel += a;
                    }
                }
            }
            
            // Changed
            pixels[rowStart + i] = pixel;
            //
        }
    }
    
    // Added
    screen.setVector(screen.rect, pixels);
    screen.unlock();
    //
}