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

Ray tracing Demo

a simple demo of ray tracing
Get Adobe Flash player
by Jarvis.weng 19 Dec 2013
/**
 * Copyright Jarvis.weng ( http://wonderfl.net/user/Jarvis.weng )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/gbtz
 */

package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    public class RayTracing extends Sprite
    {
        private var bmpData:BitmapData = new BitmapData(256, 256);
        private var bmp:Bitmap = new Bitmap(bmpData);
        public function RayTracing()
        {
            bmp.x = (stage.stageWidth - bmp.width) / 2;
            bmp.y = (stage.stageHeight - bmp.height) / 2
            addChild(bmp);
            var plane:Plane = new Plane(new Vector3(0, 1, 0), 0);
            var sphere1:Sphere = new Sphere(new Vector3(-10, 10, -10), 10);
            var sphere2:Sphere = new Sphere(new Vector3(10, 10, -10), 10);
            plane.material = new CheckerMaterial(0.1, 0.5);
            sphere1.material = new PhongMaterial(Color.red, Color.white, 16, 0.25);
            sphere2.material = new PhongMaterial(Color.blue, Color.white, 16, 0.25);
            rayTraceReflection(new Scene(new <IGeometries>[ plane, sphere1, sphere2 ]),
                               new PerspectiveCamera(new Vector3(0, 5, 15), new Vector3(0, 0, -1), new Vector3(0, 1, 0), 90), 3);
        }
        private function rayTraceReflection(scene:Scene, camera:PerspectiveCamera, maxReflect:int):void
        {
            bmpData.lock();
           
            var h:Number = bmpData.height;
            var w:Number = bmpData.width;
            var i:int = 0;
            for (var y:Number = 0; y < h; y++)
            {
                var sy:Number = 1 - y / h;
                for (var x:Number = 0; x < w; x++)
                {
                    var sx:Number = x / w;
                    var ray:Ray3 = camera.generateRay(sx, sy);
                    var color:Color = rayTraceRecursive(scene, ray, maxReflect);
                    bmpData.setPixel32(x, y, argb2uint(255, color.r * 255, color.g * 255, color.b * 255));
                }
            }
           
            bmpData.unlock();
        }
        private function rayTraceRecursive(scene:Scene, ray:Ray3, maxReflect:int):Color
        {
            var result:IntersectResult = scene.intersect(ray);
           
            if (result.geometry)
            {
                var reflectiveness:Number = result.geometry.material.reflectiveness;
                var color:Color = result.geometry.material.sample(ray, result.position, result.normal);
                color = color.multiply(1 - reflectiveness);
               
                if (reflectiveness > 0 && maxReflect > 0)
                {
                    var r:Vector3 = result.normal.multiply(-2 * result.normal.dot(ray.direction)).add(ray.direction);
                    ray = new Ray3(result.position, r);
                    var reflectedColor:Color = rayTraceRecursive(scene, ray, maxReflect - 1);
                    color = color.add(reflectedColor.multiply(reflectiveness));
                }
                return color;
            }
            else
                return Color.black;
        }
        private function argb2uint(a:uint, r:uint, g:uint, b:uint):uint
        {
            return a << 24 | r << 16 | g << 8 | b;
        }
    }
}
class Vector3
{
    public static const zero:Vector3 = new Vector3(0, 0, 0);
    public var x:Number;
    public var y:Number;
    public var z:Number;
    public function Vector3(x:Number = 0, y:Number = 0, z:Number = 0)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    public function copy():Vector3
    {
        return new Vector3(this.x, this.y, this.z);
    }
    public function length():Number
    {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }
    public function sqrLength():Number
    {
        return this.x * this.x + this.y * this.y + this.z * this.z;
    }
    public function normalize():Vector3
    {
        var inv:Number = 1 / this.length();
        return new Vector3(this.x * inv, this.y * inv, this.z * inv);
    }
    public function negate():Vector3
    {
        return new Vector3(-this.x, -this.y, -this.z);
    }
    public function add(v:Vector3):Vector3
    {
        return new Vector3(this.x + v.x, this.y + v.y, this.z + v.z);
    }
    public function subtract(v:Vector3):Vector3
    {
        return new Vector3(this.x - v.x, this.y - v.y, this.z - v.z);
    }
    public function multiply(f:Number):Vector3
    {
        return new Vector3(this.x * f, this.y * f, this.z * f);
    }
    public function divide(f:Number):Vector3
    {
        var invf:Number = 1 / f;
        return new Vector3(this.x * invf, this.y * invf, this.z * invf);
    }
    public function dot(v:Vector3):Number
    {
        return this.x * v.x + this.y * v.y + this.z * v.z;
    }
    public function cross(v:Vector3):Vector3
    {
        return new Vector3(-this.z * v.y + this.y * v.z, this.z * v.x - this.x * v.z, -this.y * v.x + this.x * v.y);
    }
}
class Ray3
{
    public var origin:Vector3;
    public var direction:Vector3;
    public function Ray3(origin:Vector3, direction:Vector3)
    {
        this.origin = origin;
        this.direction = direction;
    }
    public function getPoint(t:Number):Vector3
    {
        return this.origin.add(this.direction.multiply(t));
    }
}
interface IGeometries
{
    function intersect(ray:Ray3):IntersectResult;
    function get material():IMaterial;
}
class Sphere implements IGeometries
{
    private var center:Vector3;
    private var radius:Number;
    private var sqrRadius:Number;
    private var _material:IMaterial;
    public function get material():IMaterial
    {
        return _material;
    }
    public function set material(value:IMaterial):void
    {
        _material = value;
    }
    public function Sphere(center:Vector3, radius:Number)
    {
        this.center = center;
        this.radius = radius;
        this.sqrRadius = this.radius * this.radius;
    }
    public function intersect(ray:Ray3):IntersectResult
    {
        var v:Vector3 = ray.origin.subtract(this.center);
        var a0:Number = v.sqrLength() - this.sqrRadius;
        var DdotV:Number = ray.direction.dot(v);
        if (DdotV <= 0)
        {
            var discr:Number = DdotV * DdotV - a0;
            if (discr >= 0)
            {
                var result:IntersectResult = new IntersectResult();
                result.geometry = this;
                result.distance = -DdotV - Math.sqrt(discr);
                result.position = ray.getPoint(result.distance);
                result.normal = result.position.subtract(this.center).normalize();
                return result;
            }
        }
        return IntersectResult.noHit;
    }
}
class Plane implements IGeometries
{
    private var position:Vector3;
    private var normal:Vector3;
    private var d:Number;
    private var _material:IMaterial;
    public function get material():IMaterial
    {
        return _material;
    }
    public function set material(value:IMaterial):void
    {
        _material = value;
    }
    public function Plane(normal, d:Number)
    {
        this.normal = normal;
        this.d = d;
        this.position = this.normal.multiply(this.d);
    }
    public function intersect(ray:Ray3):IntersectResult
    {
        var a:Number = ray.direction.dot(this.normal);
        if (a >= 0)
            return IntersectResult.noHit;
        var b:Number = this.normal.dot(ray.origin.subtract(this.position));
        var result:IntersectResult = new IntersectResult();
        result.geometry = this;
        result.distance = -b / a;
        result.position = ray.getPoint(result.distance);
        result.normal = this.normal;
        return result;
    }
}
class Scene
{
    public var geometries:Vector.<IGeometries>;
    public function Scene(geometries:Vector.<IGeometries>)
    {
        this.geometries = geometries;
    }
    public function intersect(ray):IntersectResult
    {
        var minDistance:Number = Number.MAX_VALUE;
        var minResult:IntersectResult = IntersectResult.noHit;
        for (var i:int = 0; i < this.geometries.length; ++i)
        {
            var result:IntersectResult = this.geometries[i].intersect(ray);
            if (result.geometry && result.distance < minDistance)
            {
                minDistance = result.distance;
                minResult = result;
            }
        }
        return minResult;
    }
}
class IntersectResult
{
    public static const noHit:IntersectResult = new IntersectResult();
    public var geometry:IGeometries;
    public var distance:Number;
    public var position:Vector3;
    public var normal:Vector3;
    public function IntersectResult()
    {
        this.geometry = null;
        this.distance = 0;
        this.position = Vector3.zero;
        this.normal = Vector3.zero;
    }
}
class PerspectiveCamera
{
    public var eye:Vector3;
    public var front:Vector3;
    public var refUp:Vector3;
    public var fov:Number;
    public var right:Vector3;
    public var up:Vector3;
    public var fovScale:Number;
    public function PerspectiveCamera(eye:Vector3, front:Vector3, up:Vector3, fov:Number)
    {
        this.eye = eye;
        this.front = front;
        this.refUp = up;
        this.fov = fov;
        initialize();
    }
    public function initialize():void
    {
        this.right = this.front.cross(this.refUp);
        this.up = this.right.cross(this.front);
        this.fovScale = Math.tan(this.fov * 0.5 * Math.PI / 180) * 2;
    }
    public function generateRay(x:Number, y:Number):Ray3
    {
        var r:Vector3 = this.right.multiply((x - 0.5) * this.fovScale);
        var u:Vector3 = this.up.multiply((y - 0.5) * this.fovScale);
        return new Ray3(this.eye, this.front.add(r).add(u).normalize());
    }
}
class Color
{
    public static const black:Color = new Color(0, 0, 0);
    public static const white:Color = new Color(1, 1, 1);
    public static const red:Color = new Color(1, 0, 0);
    public static const green:Color = new Color(0, 1, 0);
    public static const blue:Color = new Color(0, 0, 1);
    public var r:Number;
    public var g:Number;
    public var b:Number;
    public function Color(r, g, b)
    {
        this.r = Math.min(1, r);
        this.g = Math.min(1, g);
        this.b = Math.min(1, b);
    }
    public function copy():Color
    {
        return new Color(this.r, this.g, this.b);
    }
    public function add(c:Color):Color
    {
        return new Color(this.r + c.r, this.g + c.g, this.b + c.b);
    }
    public function multiply(s:Number):Color
    {
        return new Color(this.r * s, this.g * s, this.b * s);
    }
    public function modulate(c:Color):Color
    {
        return new Color(this.r * c.r, this.g * c.g, this.b * c.b);
    }
}
interface IMaterial
{
    function sample(ray:Ray3, position:Vector3, normal:Vector3):Color;
    function get reflectiveness():Number;
}
class CheckerMaterial implements IMaterial
{
    private var scale:Number;
    private var _reflectiveness:Number;
    public function get reflectiveness():Number
    {
        return _reflectiveness
    }
    public function CheckerMaterial(scale:Number, reflectiveness:Number)
    {
        this.scale = scale;
        this._reflectiveness = reflectiveness;
    }
    public function sample(ray:Ray3, position:Vector3, normal:Vector3):Color
    {
        return Math.abs((Math.floor(position.x * 0.1) + Math.floor(position.z * this.scale)) % 2) < 1 ? Color.black : Color.white;
    }
}
class PhongMaterial implements IMaterial
{
    // global light
    static private var lightDir:Vector3 = new Vector3(1, 1, 1).normalize();
   
    static private var lightColor:Color = Color.white;
   
    private var diffuse:Color;
    private var specular:Color;
    private var shininess:Number;
    private var _reflectiveness:Number;
    public function get reflectiveness():Number
    {
        return _reflectiveness
    }
    public function PhongMaterial(diffuse, specular, shininess, reflectiveness)
    {
        this.diffuse = diffuse;
        this.specular = specular;
        this.shininess = shininess;
        this._reflectiveness = reflectiveness;
    }
    public function sample(ray:Ray3, position:Vector3, normal:Vector3):Color
    {
        var NdotL:Number = normal.dot(lightDir);
        var H:Vector3 = (lightDir.subtract(ray.direction)).normalize();
        var NdotH:Number = normal.dot(H);
        var diffuseTerm:Color = this.diffuse.multiply(Math.max(NdotL, 0));
        var specularTerm:Color = this.specular.multiply(Math.pow(Math.max(NdotH, 0), this.shininess));
        return lightColor.modulate(diffuseTerm.add(specularTerm));
    }
}