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

simple 3D 05 (not using PV3D)

PV3Dとかを使わない3D表現その5

フラットシェーディングやってみましたー。
陰面処理とか色の度合いの計算には内積を使ってます。
ライトの位置は固定です。カメラが動いています。   

Matrix3D & vector3Dは自作のやつ。

以下のサイトを参考にさせてもらいました。
http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3
Get Adobe Flash player
by sakef 28 Oct 2010
/**
 * Copyright sakef ( http://wonderfl.net/user/sakef )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/g4Yk
 */

/*
    PV3Dとかを使わない3D表現その5
    
    フラットシェーディングやってみましたー。
    陰面処理とか色の度合いの計算には内積を使ってます。
    ライトの位置は固定です。カメラが動いています。   
    
    Matrix3D & vector3Dは自作のやつ。

    以下のサイトを参考にさせてもらいました。
    http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3
*/
package
{
    import net.hires.debug.Stats;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    
    [SWF(width="465", height="465", backgroundColor="0x000000")]
    public class simple3d_05 extends Sprite
    {
        // 幅・高さ・中心・キューブのサイズ・半径
        private static const CENTER_X:int=465 >> 1;
        private static const CENTER_Y:int=465 >> 1;
        private static const RADIAN:Number=Math.PI / 180;
        private static const SIN2:Number=Math.sin(RADIAN * 2);
        private static const COS2:Number=Math.cos(RADIAN * 2);
        private static const SIZE:int=50;
        private static const CAMERA_RADIUS:int=550;
        
        // 上を示すベクトル・中心を示すベクトル・フォーカス 
        private var UP_VECTOR:MyVector3D=new MyVector3D(0, 1, 0);
        private var CENTER_VECTOR:MyVector3D=new MyVector3D(0, 0, 0);
        private var FOCUS:Number=400;
        
        // カメラ用
        private var camera:MyVector3D;
        private var isMouseDown:Boolean;
        private var oldX:Number;
        private var oldY:Number;
        private var targetRot:Number;
        private var targetPitch:Number;
        private var rot:Number;
        private var pitch:Number;
        
        // キューブとライト
        private var light:MyVector3D;
        private var container:Array;
        private var n_cube:int;
        
        // コンストラクタ
        public function simple3d_05()
        {
            // ステージ設定、マウス関係停止。
            stage.scaleMode=StageScaleMode.NO_SCALE;
            stage.align=StageAlign.TOP_LEFT;
            stage.quality=StageQuality.HIGH;
            stage.frameRate=60;
            mouseChildren=false;
            mouseEnabled=false;
            
            // Stats追加
            addChild(new Stats());
            
            // 変数の初期化
            oldX=oldY=rot=pitch=0;
            targetPitch=90;
            targetRot=180;
            isMouseDown=false;
            camera=new MyVector3D(0, 0, FOCUS);
            
            // キューブとライトの初期化
            light=new MyVector3D(100, 500, 600);
            n_cube=0;
            container=[];
            for(var i:int=-150; i <= 150; i+=150)
            {
                for(var j:int=-150; j <= 150; j+=150)
                {
                    for(var k:int=-150; k <= 150; k+=150)
                    {
                        container[n_cube]=new MyCube(i, j, k, SIZE, SIZE, SIZE);
                        n_cube ++;
                    }
                }
            }
            
            // 更新用関数追加
            addEventListener(Event.ENTER_FRAME, onFrame);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }
        
        // 計算更新用関数
        private function onFrame(e:Event):void
        {
            // カメラの移動
            if (isMouseDown)
            {
                targetRot+=(mouseX - oldX) * 0.2;
                targetPitch+=(mouseY - oldY) * 0.2;
                targetPitch=(targetPitch > -90) ? (targetPitch) : (-90);
                targetPitch=(targetPitch < 90) ? (targetPitch) : (90);
                
                oldX=mouseX;
                oldY=mouseY;
            }
            rot+=(targetRot - rot) * 0.1;
            pitch+=(targetPitch - pitch) * 0.1;
            pitch=(pitch > -90) ? (pitch) : (-90);
            pitch=(pitch < 90) ? (pitch) : (90);
            camera.x=CAMERA_RADIUS * Math.sin(rot * Math.PI / 180);
            camera.y=CAMERA_RADIUS * Math.sin(pitch * Math.PI / 180);
            camera.z=CAMERA_RADIUS * Math.cos(rot * Math.PI / 180);
            
            // ビューイング変換行列
            var Mview:MyMatrix3D=getViewingTransformMatrix(camera);
            
            // モデリング変換変換行列 (例によって何もしない)
            var Mmodel:MyMatrix3D = new MyMatrix3D;
            
            // 座標の計算
            for(var i:int=0; i < n_cube; i++) (container[i]as MyCube).render(Mmodel, Mview, light, CENTER_VECTOR);
            
            // ソート
            container.sortOn("sortNumber", Array.NUMERIC | Array.DESCENDING);
            
            // 描写
            graphics.clear();
            for(i=0; i < n_cube; i++) (container[i]as MyCube).draw(graphics, CENTER_X, CENTER_Y, CENTER_VECTOR, FOCUS);
        }
        
        
        // マウスダウン時の関数
        private function onMouseDown(e:MouseEvent):void
        {
            isMouseDown=true;
            oldX=mouseX;
            oldY=mouseY;
        }
        
        // マウスアップ時の関数
        private function onMouseUp(e:MouseEvent):void
        {
            isMouseDown=false;
        }
        
        // カメラ位置からビューイング変換行列を返す関数
        private function getViewingTransformMatrix(camera:MyVector3D):MyMatrix3D
        {
            // Center - Eye ベクトルの作成と正規化
            var n:MyVector3D=CENTER_VECTOR.subtract(camera);
            n.normalize();
            
            // up×nを計算 (外積)
            var u:MyVector3D=UP_VECTOR.crossProduct(n);
            u.normalize();
            
            // n×uを計算 (外積)
            var v:MyVector3D=n.crossProduct(u);
            v.normalize();
            
            // dの計算
            var dx:Number=-camera.innerProduct(u);
            var dy:Number=-camera.innerProduct(v);
            var dz:Number=-camera.innerProduct(n);
            
            // ビューイング変換行列
            var Mview:MyMatrix3D=new MyMatrix3D(u.x, u.y, u.z, dx, v.x, v.y, v.z, dy, n.x, n.y, n.z, dz, 0, 0, 0, 1);
            
            return Mview;
        }
    }
}


/*
キューブクラス
*/
import flash.display.Graphics;
import flash.utils.Dictionary;

final class MyCube
{
    public var position:MyVector3D;
    public var sortNumber:Number;
    
    private var points:Array;
    private var eye_Coord_points:Array;
    private var eye_Coord_position:MyVector3D;
    private var eye_Coord_light:MyVector3D;
    
    // コンストラクタ
    public function MyCube(x:Number=0, y:Number=0, z:Number=0, widthX:Number=100, widthY:Number=100, widthZ:Number=100)
    {
        position=new MyVector3D(x, y, z);
        sortNumber=0;
        
        var wx:Number=widthX >> 1;
        var wy:Number=widthY >> 1;
        var wz:Number=widthZ >> 1;
        
        points=[new MyVector3D(wx, -wy, wz), new MyVector3D(wx, -wy, -wz), new MyVector3D(-wx, -wy, -wz), new MyVector3D(-wx, -wy, wz), new MyVector3D(wx, wy, wz), new MyVector3D(wx, wy, -wz), new MyVector3D(-wx, wy, -wz), new MyVector3D(-wx, wy, wz)];
        eye_Coord_points=[];
    }
    
    // 座標の更新
    public function render(Mmodel:MyMatrix3D, Mview:MyMatrix3D, light:MyVector3D, center:MyVector3D):void
    {
        // 各座標を更新
        position=Mmodel.productVector(position);
        for(var i:int=0; i < 8; i++) points[i]=Mmodel.productVector(points[i]);
        
        // アイ座標系を計算
        eye_Coord_position=Mview.productVector(position);
        for(i=0; i < 8; i++) eye_Coord_points[i]=Mview.productVector(points[i]);
        
        // ライトの座標を愛座標系に変換して保存
        eye_Coord_light=Mview.productVector(light);
        
        // ソート用の変数の更新
        sortNumber= center.distance(eye_Coord_position);
    }
    
    public function draw(g:Graphics, centerX:Number, centerY:Number, center:MyVector3D, focus:Number):void
    {
        // 各平面を準備 (Bottom, Top, Front, Back, Right, Left)
        var planes:Array=[];
        planes[0]=[eye_Coord_points[0], eye_Coord_points[1], eye_Coord_points[2], eye_Coord_points[3]];
        planes[1]=[eye_Coord_points[4], eye_Coord_points[7], eye_Coord_points[6], eye_Coord_points[5]];
        planes[2]=[eye_Coord_points[0], eye_Coord_points[3], eye_Coord_points[7], eye_Coord_points[4]];
        planes[3]=[eye_Coord_points[1], eye_Coord_points[5], eye_Coord_points[6], eye_Coord_points[2]];
        planes[4]=[eye_Coord_points[0], eye_Coord_points[4], eye_Coord_points[5], eye_Coord_points[1]];
        planes[5]=[eye_Coord_points[3], eye_Coord_points[2], eye_Coord_points[6], eye_Coord_points[7]];
        
        // 各面のソート (参考にしたサイト様:http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3)
        var zBuffer:Dictionary=new Dictionary();
        for(var i:int=0; i < 6; i++) zBuffer[planes[i]]=(planes[i][0].z + planes[i][1].z + planes[i][2].z + planes[i][3].z) * 0.25;
        planes.sort(function(a:Array, b:Array):Number{return zBuffer[a] - zBuffer[b];});
        
        // 描写
        var x:Number=eye_Coord_position.x;
        var y:Number=eye_Coord_position.y;
        var z:Number=eye_Coord_position.z;
        for(i=0; i < 6; i++)
        {
            // 面の外積を計算
            var plane:Array=planes[i];
            var P:MyVector3D=(plane[3]as MyVector3D).subtract(plane[0]as MyVector3D);
            var Q:MyVector3D=(plane[1]as MyVector3D).subtract(plane[0]as MyVector3D);
            var newVec:MyVector3D=P.crossProduct(Q);
            newVec.normalize();
            
            // カメラ位置までのベクトルを計算
            var cameraVec:MyVector3D = center.subtract(eye_Coord_position);
            cameraVec.normalize();
            
            // 内積とって隠面処理
            var isFace:Number = cameraVec.innerProduct(newVec);
            if(isFace > 0)
            {
                // ライトへのベクトルを計算
                var lightVec:MyVector3D=eye_Coord_light.subtract(eye_Coord_position);
                lightVec.normalize();
            
                // 内積から色の度合いを計算
                var degree:Number=newVec.innerProduct(lightVec);
                degree=(degree < 0.1) ? (0.1) : (degree);
                var color:uint=(0x00)<<16 || 0xFF*degree<<8 || 0x00;
            
                // 3D→2D変換 & 描写
                var point:MyVector3D=plane[0];
                var scale:Number=focus / point.distance(center);
                var screenX:Number=(point.x + x) * scale + centerX;
                var screenY:Number=-(point.y + y) * scale + centerY;
                g.beginFill(color, 1);
                g.moveTo(screenX, screenY);
                for(var j:int=1; j < 4; j++)
                {
                    point=plane[j];
                    scale=focus / point.distance(center);
                    screenX=(point.x + x) * scale + centerX;
                    screenY=-(point.y + y) * scale + centerY;
                    g.lineTo(screenX, screenY);
                }
                g.endFill();
            }
        }
    }
}

/*
行列演算用クラス
*/
final class MyMatrix3D
{
    // 行列の要素
    public var v11:Number, v12:Number, v13:Number, v14:Number;
    public var v21:Number, v22:Number, v23:Number, v24:Number;
    public var v31:Number, v32:Number, v33:Number, v34:Number;
    public var v41:Number, v42:Number, v43:Number, v44:Number;
    
    // コンストラクタ
    public function MyMatrix3D(v11:Number=1, v12:Number=0, v13:Number=0, v14:Number=0, 
                                 v21:Number=0, v22:Number=1, v23:Number=0, v24:Number=0, 
                                 v31:Number=0, v32:Number=0, v33:Number=1, v34:Number=0, 
                                 v41:Number=0, v42:Number=0, v43:Number=0, v44:Number=1)
    {
        this.v11=v11; this.v12=v12; this.v13=v13; this.v14=v14;
        this.v21=v21; this.v22=v22; this.v23=v23; this.v24=v24;
        this.v31=v31; this.v32=v32; this.v33=v33; this.v34=v34;
        this.v41=v41; this.v42=v42; this.v43=v43; this.v44=v44;
    }
    
    // 行列との積 (自分をA、引数をBとするとA*B)
    public function productMatrix(m:MyMatrix3D):MyMatrix3D
    {
        var newMat:MyMatrix3D=new MyMatrix3D();
        newMat.v11=v11 * m.v11 + v12 * m.v21 + v13 * m.v31 + v14 * m.v41;
        newMat.v12=v11 * m.v12 + v12 * m.v22 + v13 * m.v32 + v14 * m.v42;
        newMat.v13=v11 * m.v13 + v12 * m.v23 + v13 * m.v33 + v14 * m.v43;
        newMat.v14=v11 * m.v14 + v12 * m.v24 + v13 * m.v34 + v14 * m.v44;
        newMat.v21=v21 * m.v11 + v22 * m.v21 + v23 * m.v31 + v24 * m.v41;
        newMat.v22=v21 * m.v12 + v22 * m.v22 + v23 * m.v32 + v24 * m.v42;
        newMat.v23=v21 * m.v13 + v22 * m.v23 + v23 * m.v33 + v24 * m.v43;
        newMat.v24=v21 * m.v14 + v22 * m.v24 + v23 * m.v34 + v24 * m.v44;
        newMat.v31=v31 * m.v11 + v32 * m.v21 + v33 * m.v31 + v34 * m.v41;
        newMat.v32=v31 * m.v12 + v32 * m.v22 + v33 * m.v32 + v34 * m.v42;
        newMat.v33=v31 * m.v13 + v32 * m.v23 + v33 * m.v33 + v34 * m.v43;
        newMat.v34=v31 * m.v14 + v32 * m.v24 + v33 * m.v34 + v34 * m.v44;
        newMat.v41=v41 * m.v11 + v42 * m.v21 + v43 * m.v31 + v44 * m.v41;
        newMat.v42=v41 * m.v12 + v42 * m.v22 + v43 * m.v32 + v44 * m.v42;
        newMat.v43=v41 * m.v13 + v42 * m.v23 + v43 * m.v33 + v44 * m.v43;
        newMat.v44=v41 * m.v14 + v42 * m.v24 + v43 * m.v34 + v44 * m.v44;
        return newMat;
    }
    
    // ベクトルとの積
    public function productVector(v:MyVector3D):MyVector3D
    {
        var newVec:MyVector3D=new MyVector3D();
        newVec.x=v11 * v.x + v12 * v.y + v13 * v.z + v14;
        newVec.y=v21 * v.x + v22 * v.y + v23 * v.z + v24;
        newVec.z=v31 * v.x + v32 * v.y + v33 * v.z + v34;
        return newVec;
    }
}

/*
ベクトル演算用クラス
*/
final class MyVector3D
{
    // x、y、z座標を保存する変数
    public var x:Number;
    public var y:Number;
    public var z:Number;
    public var w:Number;
    
    // コンストラクタ
    public function MyVector3D(x:Number=0, y:Number=0, z:Number=0)
    {
        this.x=x;
        this.y=y;
        this.z=z;
        this.w=1;
    }
    
    // 距離
    public function distance(v:MyVector3D):Number
    {
        return Math.sqrt((v.x - x) * (v.x - x) + (v.y - y) * (v.y - y) + (v.z - z) * (v.z - z));
    }
    
    // 差
    public function subtract(v:MyVector3D):MyVector3D
    {
        var newVec:MyVector3D=new MyVector3D();
        newVec.x=x - v.x;
        newVec.y=y - v.y;
        newVec.z=z - v.z;
        return newVec;
    }
    
    // 正規化
    public function normalize():void
    {
        var len:Number=Math.sqrt(x * x + y * y + z * z);
        x/=len;
        y/=len;
        z/=len;
    }
    
    // 内積
    public function innerProduct(v:MyVector3D):Number
    {
        return (x * v.x + y * v.y + z * v.z);
    }
    
    // 外積(クロス積) (自分をA、引数をBとすると、A×Bを計算)
    public function crossProduct(v:MyVector3D):MyVector3D
    {
        var newVec:MyVector3D=new MyVector3D();
        newVec.x=y * v.z - z * v.y;
        newVec.y=z * v.x - x * v.z;
        newVec.z=x * v.y - y * v.x;
        return newVec;
    }
}