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

球体レイトレーシング

まだまだ未完成でしかもかなり遅いですが、
レイトレーシングです。
屈折はまだできません。
なお光源は球の中心となっています。
/**
 * Copyright hosepens ( http://wonderfl.net/user/hosepens )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/kw8v
 */

package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Vector3D;
    import flash.text.TextField;
    import flash.utils.getTimer;
    
    [SWF(width="465",height="465",backgroundColor="#000000",frameRate="60")]
    public class Main extends Sprite 
    {
        
        public function Main():void 
        {
            const W: uint = this.stage.stageWidth;
            const H: uint = this.stage.stageHeight;
            
            //light
            var light: BallLight = new BallLight(new Vector3D(100, 100, 1000));
            light.strength = 2.0;
            light.color = 0xFFFFFFFF;
            
            //ball
            var ball: Ball = new Ball(new Vector3D(-100, 100, 800));
            ball.radius = 100;
            ball.refractiveIndex = 1.5;
            ball.color = 0xFFFF0000;
            var ball2: Ball = new Ball(new Vector3D(300, 100, 1200));
            ball2.radius = 100;
            ball2.refractiveIndex = 1.5;
            ball2.color = 0xFF0000FF;
            
            //picture
            var picture: Picture = new Picture(new Vector3D(0, 200, 1200));
            picture.load("http://assets.wonderfl.net/images/related_images/4/4e/4e20/4e202e6c59444724301938ea72697188440a719b", 600, 600, complete);
            picture.refractiveIndex = 1.5;            
            picture.scaleX = 10;
            picture.scaleY = 10;
            picture.vector1 = new Vector3D(- 1, 0, - 1);
            picture.vector2 = new Vector3D(1, 0, - 1);
            
            //world
            var world: World = new World(stage, W, H, light, ball, ball2, picture);
            
            //fps counter
            var tf: TextField = new TextField();
            tf.x = tf.y = 0;
            tf.width = W;
            tf.height = 20;
            tf.autoSize = "right";
            tf.textColor = 0xFFFFFF;
            stage.addChild(tf);
            
            var time: uint = getTimer();
            
            function complete(e: Event): void {
                stage.addEventListener(Event.ENTER_FRAME, ef);
            }
            
            var i: uint = 0;            
            function ef(e: Event): void {
                world.render(i, i + 1);
                i += 1;
                if (i >= H) {
                    i = 0;
                    light.position.y -= 50;
                    light.position.z -= 50;
                    world.sample();
                }
                tf.text = Math.floor(1000 / (getTimer() - time)) + "fps";
                time = getTimer();
            }
        }
    }
}

import flash.geom.Vector3D;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.net.URLRequest;
import flash.system.LoaderContext;
class Model
{
    private var _position: Vector3D;
    private var _refractiveIndex: Number;
    private var _color: uint;
    private var _strength: Number;
    
    public function Model(_position: Vector3D = null, _refractiveIndex: Number = 1., _color: uint = 0xFFFFFFFF) {
        if(_position) {
            position = _position;
        }else {
            position = new Vector3D();
        }
        refractiveIndex = _refractiveIndex;
        color = _color;
        strength = 1.;
    }
    
    public function clone(): Model {
        return new Model(position, refractiveIndex, color);
    }
    
    public function get position():Vector3D { return _position; }
    
    public function set position(value:Vector3D):void 
    {
        _position = value;
    }
    
    public function get refractiveIndex():Number { return _refractiveIndex; }
    
    public function set refractiveIndex(value:Number):void 
    {
        _refractiveIndex = value;
    }
    
    public function get color():uint { return _color; }
    
    public function set color(value:uint):void 
    {
        _color = value;
    }
    
    public function get strength():Number { return _strength; }
    
    public function set strength(value:Number):void 
    {
        _strength = value;
    }
    
    public function intersection(ray: Ray): void {
        //setting of position, color and strength
        //if ray does not intersect, set strength 0
        ray.strength = 0;
    }
    
    public function normalVector(ray: Ray): Vector3D {
        return Vector3D.Z_AXIS;
    }
}
class Ball extends Model
{
    private var _radius: Number;
    
    public function Ball(_position: Vector3D = null) {
        super(_position);
    }
    
    public function get radius():Number { return _radius; }
    
    public function set radius(value:Number):void 
    {
        _radius = value;
    }
    
    public override function intersection(ray: Ray): void {
        var point: Vector3D;
        var p: Vector3D = ray.position;
        var v: Vector3D = ray.vector;
        var o: Vector3D = position;
        var r: Number = radius;
        var a: Number = v.lengthSquared;
        var b: Number = p.dotProduct(v) - o.dotProduct(v);
        var c: Number = p.subtract(o).lengthSquared - r * r;
        var t: Number = - b - Math.sqrt(b * b - a * c);
        if (!t || t < 0.001) {
            t = - b + Math.sqrt(b * b - a * c);
        }
        point = v.clone();
        point.scaleBy(t);
        point.incrementBy(p);
        if (!t || t < 0.001) {
            ray.strength = 0;
        }
        ray.position = point;
        ray.color = color;
    }
    
    public override function normalVector(ray: Ray): Vector3D {
        //set the position of rays before use this
        var normal: Vector3D = ray.position.subtract(position);
        normal.normalize();
        if (position.subtract(ray.position).dotProduct(ray.vector) < 0) {
            normal.negate();
        }
        return normal;
    }
}
class Light extends Model
{
    public function Light(_position: Vector3D = null) {
        super(_position);
    }
}
class BallLight extends Light 
{
    private var _radius: Number;
    
    public function BallLight(_position: Vector3D = null) {
        super(_position);
        radius = 100;
    }
    
    public function get radius():Number { return _radius; }
    
    public function set radius(value:Number):void 
    {
        _radius = value;
    }
    
    public override function intersection(ray: Ray): void {
        var point: Vector3D;
        var p: Vector3D = ray.position;
        var v: Vector3D = ray.vector;
        var o: Vector3D = position;
        var r: Number = radius;
        var a: Number = v.lengthSquared;
        var b: Number = p.dotProduct(v) - o.dotProduct(v);
        var c: Number = p.subtract(o).lengthSquared - r * r;
        var t: Number = - b - Math.sqrt(b * b - a * c);
        if (!t || t < 0.001) {
            t = - b + Math.sqrt(b * b - a * c);
        }
        point = v.clone();
        point.scaleBy(t);
        point.incrementBy(p);
        if (!t || t < 0.001) {
            ray.strength = 0;
        }
        ray.position = point;
        ray.color = color;
    }
    
    public override function normalVector(ray: Ray): Vector3D {
        //set the position of rays before use this
        var normal: Vector3D = ray.position.subtract(position);
        normal.normalize();
        if (position.subtract(ray.position).dotProduct(ray.vector) < 0) {
            normal.negate();
        }
        return normal;
    }
}
class Picture extends Model
{
    private var _image: BitmapData;
    private var _width: uint;
    private var _height: uint;
    private var _scaleX: Number;
    private var _scaleY: Number;
    private var _vector1: Vector3D;
    private var _vector2: Vector3D;
    
    public function Picture(_position: Vector3D = null) {
        super(_position);
        scaleX = 1.;
        scaleY = 1.;
        vector1 = Vector3D.X_AXIS;
        vector2 = Vector3D.Y_AXIS;
    }
    
    public function get image():BitmapData { return _image; }
    
    public function set image(value:BitmapData):void 
    {
        _image = value;
    }
    
    public function get width():uint { return _width; }
    
    public function set width(value:uint):void 
    {
        _width = value;
    }
    
    public function get height():uint { return _height; }
    
    public function set height(value:uint):void 
    {
        _height = value;
    }
    
    public function get vector1():Vector3D { return _vector1; }
    
    public function set vector1(value:Vector3D):void 
    {
        _vector1 = value;
        _vector1.normalize();
    }
    
    public function get vector2():Vector3D { return _vector2; }
    
    public function set vector2(value:Vector3D):void 
    {
        _vector2 = value;
        _vector2.normalize();
    }
    
    public function get scaleX():Number { return _scaleX; }
    
    public function set scaleX(value:Number):void 
    {
        _scaleX = value;
    }
    
    public function get scaleY():Number { return _scaleY; }
    
    public function set scaleY(value:Number):void 
    {
        _scaleY = value;
    }
    
    public function load(url: String, _width: uint, _height: uint, callback: Function): void {
        var loader: Loader = new Loader();
        var request: URLRequest = new URLRequest(url);
        var context: LoaderContext = new LoaderContext(true);
        width = _width;
        height = _height;
        image = new BitmapData(width, height, true);
        loader.load(request, context);
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, callback);
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, complete);
        function complete(e: Event): void {
            image.draw(loader);
        }
    }
    
    public override function intersection(ray: Ray): void {
        var point: Vector3D;
        var p: Vector3D = ray.position;
        var v: Vector3D = ray.vector;
        var q: Vector3D = position;
        var u: Vector3D = vector1;
        var w: Vector3D = vector2;
        var matrixA: Matrix3D, matrixB: Matrix3D;
        var k: Number, s: Number, t: Number;
        matrixA = new Matrix3D(Vector.<Number>(new Array(1, 0, 0, 0, 0, v.x, - u.x, - w.x, 0, v.y, - u.y, - w.y, 0, v.z, - u.z, - w.z)));
        matrixA.invert();
        matrixB = new Matrix3D(Vector.<Number>(new Array(1, 0, 0, 0, 0, 1, 0, q.x - p.x, 0, 0, 1, q.y - p.y, 0, 0, 0, q.z - p.z)));
        matrixA.append(matrixB);
        k = matrixA.rawData[7];
        s = matrixA.rawData[11];
        t = matrixA.rawData[15];
        point = v.clone();
        point.scaleBy(k);
        point.incrementBy(p);
        if (k < 0.001 || s > width / 2 * scaleX || s < - width / 2 * scaleX || t > height / 2 * scaleY || t < - height / 2 * scaleY) {
            //point = new Vector3D();
            ray.strength = 0;
        }
        ray.position = point;
        ray.color = image.getPixel32(Math.round(s / scaleX + width / 2), Math.round(t / scaleY + height / 2));
    }
    
    public override function normalVector(ray: Ray): Vector3D {
        var normal: Vector3D = vector1.crossProduct(vector2);
        normal.normalize();
        return normal;
    }
}
class Pixel 
{
    private var _rays: Vector.<Ray>;
    
    public function Pixel(_ray: Ray = null) {
        rays = new Vector.<Ray>();
        if(_ray) {
            rays[0] = _ray;
        }
    }
    
    public function get rays():Vector.<Ray> { return _rays; }
    
    public function set rays(value:Vector.<Ray>):void 
    {
        _rays = value;
    }
    
    public function color(): uint {
        var i: uint, l: uint = rays.length,
        red: uint, green: uint, blue: uint;
        red = green = blue = 0;
        for (i = 0; i < l; i ++ ) {
            red += ((0xFF0000 & rays[i].color) >> 16) * rays[i].strength;
            green += ((0xFF00 & rays[i].color) >> 8) * rays[i].strength;
            blue += (0xFF & rays[i].color) * rays[i].strength;
        }
        
        if (red > 0xFF) {
            red = 0xFF;
        }
        if (green > 0xFF) {
            green = 0xFF;
        }
        if (blue > 0xFF) {
            blue = 0xFF;
        }
        return 0xFF000000 | (red << 16) | (green << 8) | (blue);
    }
    
    public function rayTrace(models: Vector.<Model>): void {
        var i: uint, j: uint, l: uint = models.length,
        temporaryRay: Ray, nearestRay: Ray, reflectRay: Ray, refractRay: Ray, normal: Vector3D, n: uint,
        distance: Number, object: Number, reflectance: Number;
        for (i = 0; i < rays.length && i < 8; i ++ ) {
            object = Infinity;
            n = l;
            for (j = 0; j < l; j ++ ) {
                temporaryRay = rays[i].clone();
                models[j].intersection(temporaryRay);
                if (temporaryRay.strength > 0) {
                    distance = rays[i].position.subtract(temporaryRay.position).length;
                    if (distance < object) {
                        object = distance;
                        nearestRay = temporaryRay;
                        n = j;
                    }
                }
            }
            if (n < l) {
                rays[i] = nearestRay;
                //attenuation by distance
                rays[i].strength *= 1 / Math.pow(object / 2000 + 1, 2);
                normal = models[n].normalVector(rays[i]);
                if (models[n] is Light) {
                    rays[i].strength *= models[n].strength;
                }else {
                    reflectRay = rays[i].clone();
                    reflectance = reflectRay.strength;
                    reflectRay.reflect(normal, 1, models[n].refractiveIndex);
                    reflectRay.strength *= 1;
                    reflectance = reflectRay.strength / reflectance;
                    rays.push(reflectRay);
                    rays[i].strength *= 1 - reflectance;
                    rays[i].vector = models[0].position.subtract(rays[i].position);
                    //lighting
                    if(rays[i].vector.dotProduct(normal) > 0 && rays[i].strength > 0) {
                        object = Infinity;
                        n = l;
                        for (j = 0; j < l; j ++ ) {
                            temporaryRay = rays[i].clone();
                            models[j].intersection(temporaryRay);
                            if (temporaryRay.strength > 0) {
                                distance = rays[i].position.subtract(temporaryRay.position).length;
                                if (distance < object) {
                                    object = distance;
                                    nearestRay = temporaryRay;
                                    n = j;
                                }
                            }
                        }
                        if (models[n] is Light) {
                            rays[i].strength *= models[n].strength;
                            //attenuation by polygon
                            rays[i].strength *= rays[i].vector.dotProduct(normal);
                            //attenuation by distance
                            rays[i].strength *= 1 / Math.pow(object / 2000 + 1, 2);
                        }else {
                            rays[i].strength = 0;
                        }
                    }else {
                        rays[i].strength = 0;
                    }
                }
            }else {
                rays[i].strength = 0;
            }
        }
    }
}
class Ray
{
    private var _position: Vector3D;
    private var _vector: Vector3D;
    private var _strength: Number;
    private var _color: uint;
    
    public function Ray(_position: Vector3D = null, _vector: Vector3D = null, _strength: Number = 1., _color: uint = 0x0) {
        if(_position) {
            position = _position;
        }else {
            position = new Vector3D();
        }
        if(_vector) { 
            vector = _vector;
        }else {
            vector = new Vector3D();
        }
        strength = _strength;
        color = _color;
    }
    
    public function clone(): Ray {
        return new Ray(position.clone(), vector.clone(), strength, color);
    }
    
    public function get position():Vector3D { return _position; }
    
    public function set position(value:Vector3D):void 
    {
        _position = value;
    }
    
    public function get vector():Vector3D { return _vector; }
    
    public function set vector(value:Vector3D):void 
    {
        _vector = value;
        if (_vector) {
            _vector.normalize();
        }
    }
    
    public function get strength():Number { return _strength; }
    
    public function set strength(value:Number):void 
    {
        _strength = value;
    }
    
    public function get color():uint { return _color; }
    
    public function set color(value:uint):void 
    {
        _color = value;
    }
    
    public function reflect(_normal: Vector3D, refractiveIndex1: Number, refractiveIndex2: Number): void {
        var normal: Vector3D = _normal.clone();
        var r1: Number = refractiveIndex1;
        var r2: Number = refractiveIndex2;
        var cosI: Number = normal.dotProduct(vector) * - 1;
        var sinI: Number = Math.sqrt(1 - cosI * cosI);
        var sinR: Number = (r1 / r2) * sinI;
        var cosR: Number = Math.sqrt(1 - sinR * sinR);
        var rvector: Vector3D = normal.clone();
        rvector.scaleBy(normal.dotProduct(vector) * - 2);
        vector.incrementBy(rvector);
        //reflectance
        strength *= Math.pow((r1 * cosI - r2 * cosR) / (r1 * cosI + r2 * cosR), 2);
    }

}
class World 
{
    private var _models: Vector.<Model>;
    private var _width: uint;
    private var _height: uint;
    private var _depth: Number;
    private var _bmpData: BitmapData;
    
    public function World(display: DisplayObjectContainer, _width: uint, _height: uint, ..._models: Array) {
        models = Vector.<Model>(_models);
        width = _width;
        height = _height;
        bmpData = new BitmapData(width, height, true, 0x0);
        display.addChild(new Bitmap(bmpData));
    }
    
    public function get models():Vector.<Model> { return _models; }
    
    public function set models(value:Vector.<Model>):void 
    {
        _models = value;
    }
    
    public function get width():uint { return _width; }
    
    public function set width(value:uint):void 
    {
        _width = value;
        depth = _width / 2 / Math.tan(Math.PI / 360 * 60);
    }
    
    public function get height():uint { return _height; }
    
    public function set height(value:uint):void 
    {
        _height = value;
    }
    
    public function get depth():Number { return _depth; }
    
    public function set depth(value:Number):void 
    {
        _depth = value;
    }
    
    public function get bmpData():BitmapData { return _bmpData; }
    
    public function set bmpData(value:BitmapData):void 
    {
        _bmpData = value;
    }
    
    public function render(start: uint = 0, end: uint = 0): void {
        var i: uint, j: uint, l: uint = width;
        var ray: Ray = new Ray();
        var pixel: Pixel = new Pixel();
        if (!end || end > height) {
            end = height;
        }
        bmpData.lock();
        for (j = start; j < end; j ++ ) {
            for (i = 0; i < l; i ++ ) {            
                ray.position = new Vector3D();
                ray.vector = new Vector3D(i - width / 2, j - height / 2, depth);
                ray.strength = 1.;
                ray.color = 0x0;
                pixel.rays = new Vector.<Ray>();
                pixel.rays[0] = ray;
                pixel.rayTrace(models);
                bmpData.setPixel32(i, j, pixel.color());
            }
        }
        bmpData.unlock();
    }
    
    public function sample(): void {
        var i: uint, j: uint;
        var red: uint, green: uint, blue: uint, color: uint;
        for (i = 0; i < width / 2; i ++ ) {
            for (j = 0; j < height / 2; j ++ ) {
                red = green = blue = 0;
                color = bmpData.getPixel32(i * 2, j * 2);
                red += ((0xFF0000 & color) >> 16) / 4;
                green += ((0xFF00 & color) >> 8) / 4;
                blue += (0xFF & color) / 4;
                color = bmpData.getPixel32(i * 2 + 1, j * 2);
                red += ((0xFF0000 & color) >> 16) / 4;
                green += ((0xFF00 & color) >> 8) / 4;
                blue += (0xFF & color) / 4;
                color = bmpData.getPixel32(i * 2 + 1, j * 2 + 1);
                red += ((0xFF0000 & color) >> 16) / 4;
                green += ((0xFF00 & color) >> 8) / 4;
                blue += (0xFF & color) / 4;
                color = bmpData.getPixel32(i * 2, j * 2 + 1);
                red += ((0xFF0000 & color) >> 16) / 4;
                green += ((0xFF00 & color) >> 8) / 4;
                blue += (0xFF & color) / 4;
                bmpData.setPixel32(i, j, 0xFF000000 | (red << 16) | (green << 8) | (blue));
            }
        }
    }
}