forked from: シェーディングを近づけます

by shy forked from シェーディングを近づけます (diff: 1)
このクラスでは、法線マップから簡単なライティングを行います。
♥0 | Line 163 | Modified 2011-01-13 17:04:53 | MIT License
play

ActionScript3 source code

/**
 * Copyright shy ( http://wonderfl.net/user/shy )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/8NeT
 */

// forked from psyark's シェーディングを近づけます
// forked from psyark's アングルを近づけます
// forked from psyark's 背景を近づけます
// forked from psyark's 色を近づけます
// forked from psyark's 形を近づけます
// forked from psyark's 隣にお手本を表示します
// forked from psyark's flash on 2009-12-9
package {
    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;
        import flash.system.*;
        import flash.net.*;
    
    [SWF(width=930,height=465,frameRate=60,backgroundColor=0x000000)]
    /**
     * このクラスでは、法線マップから簡単なライティングを行います。
     */
    public class Test21 extends Sprite {
        private var viewport:Shape;
        private var vertices:Vector.<Number>;
        private var uvtData:Vector.<Number>;
        private var indices:Vector.<int>;
        private var worldMatrix:Matrix3D;
        private var viewMatrix:Matrix3D;
        private var projection:PerspectiveProjection;
        private var texture:BitmapData;
        private var normalMap:BitmapData;
        
        // 一時計算用ビットマップ
        private var tmp1:BitmapData;
        private var tmp2:BitmapData;

                private var palette:Array;
        
        /**
         * コンストラクタ
         */
        public function Test21() {
                    var bg:BitmapData = new BitmapData(465, 465, false);
                    bg.perlinNoise(250, 250, 1, 0, false, true, 7, true);
                    bg.colorTransform(bg.rect, new ColorTransform(2.8, 2.8, 2.8, 1, -0x140, -0x150, -0x160));
                    addChild(new Bitmap(bg));

    
                        Security.loadPolicyFile("http://farm1.static.flickr.com/crossdomain.xml");
                        var loader:Loader = new Loader();
                        loader.load(new URLRequest("http://farm1.static.flickr.com/242/522495023_2a2423ecac.jpg"), new LoaderContext(true));
                        loader.x = 465;
                        addChild(loader);
            
            // 描画対象となるShapeを作成し、表示リストに追加します。
            viewport = new Shape();
            viewport.x = 465 * 0.53;
            viewport.y = 465 * 0.55;
            addChild(viewport);
            
            createMesh();
            createNormalMap();
            
            worldMatrix = new Matrix3D();
            viewMatrix = new Matrix3D();
            viewMatrix.appendTranslation(0, 0, 800);
            projection = new PerspectiveProjection();
            projection.fieldOfView = 60;
            
            texture = new BitmapData(512, 512, false);
            texture.perlinNoise(20, 100, 5, 0, true, true, 7, true);
                        texture.colorTransform(texture.rect, new ColorTransform(0.1, 0.1, 0.1, 1, 0x66, 0x99, 0x00));
            
            tmp1 = new BitmapData(512, 512, false);
            tmp2 = new BitmapData(512, 512, false);
            
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);

                        var paletteFactor:int = 1;
                        palette = [];
                        var spec:Number = 0.96;
                        for (var i:int=0; i<0x100; i++) {
                            var t:Number = i / 0xFF;
                            var value:Number = Math.pow(t, 6) * 0x99;
                            if (t > spec) {
                                value += Math.pow((t - spec) / (1 - spec), 8) * 0x22;
                            }
                            palette[i] = gs(value);
                        }
                        function gs(v:int):int {
                            return v << 16 | v << 8 | v;
                        }
        }
        
        /**
         * 3D形状を作成します
         */
        private function createMesh():void {
            vertices = new Vector.<Number>();
            uvtData = new Vector.<Number>();
            indices = new Vector.<int>();
            
            var hDiv:int = 32;
            var vDiv:int = 16;
            var hRadius:Number = 200;
            var vRadius:Number = 200;
            
            for (var i:uint=0; i<=hDiv; i++) {
                var s1:Number = Math.PI * 2 * i / hDiv;
                for (var j:uint=0; j<=vDiv; j++) {
                    var s2:Number = Math.PI * 2 * j / vDiv;
                    var r:Number = Math.cos(s2) * vRadius + hRadius;
                    vertices.push(Math.cos(s1) * r, Math.sin(s1) * r, Math.sin(s2) * vRadius * Math.SQRT2);
                    uvtData.push(i / hDiv, j / vDiv, 1);
                    if (j < vDiv && i < hDiv) {
                        var a:uint =  i      * (vDiv + 1) + j;
                        var b:uint = (i + 1) * (vDiv + 1) + j;
                        indices.push(b, a + 1, a, a + 1, b, b + 1);
                    }
                }
            }
        }
        
        /**
         * 法線マップを作成します
         */
        private function createNormalMap():void {
            normalMap = new BitmapData(512, 512, false, 0);
            var mtx:Matrix3D = new Matrix3D();
            for (var x:uint=0; x<normalMap.width; x++) {
                for (var y:uint=0; y<normalMap.height; y++) {
                    mtx.identity();
                    mtx.appendRotation(y / normalMap.height * -360, Vector3D.Y_AXIS);
                    mtx.appendRotation(x / normalMap.width  * +360, Vector3D.Z_AXIS);
                    var normal:Vector3D = mtx.transformVector(Vector3D.X_AXIS);
                    var color:uint = (normal.x / 2 + 0.5) * 0xFF << 16 |
                                     (normal.y / 2 + 0.5) * 0xFF << 8 |
                                     (normal.z / 2 + 0.5) * 0xFF;
                    normalMap.setPixel(x, y, color);
                }
            }
        }
        
        /**
         * フレームごとの処理
         */
        private function enterFrameHandler(event:Event):void {
            update();
            render();
        }
        
        /**
         * 更新
         */
        private function update():void {
            worldMatrix.identity();
                        worldMatrix.appendRotation(-18, Vector3D.X_AXIS);
            worldMatrix.appendRotation(5, Vector3D.Y_AXIS);
                //worldMatrix.appendRotation(0.27, Vector3D.X_AXIS);
            //worldMatrix.appendRotation(0.61, Vector3D.Y_AXIS);
        }
        
        /**
         * 描画
         */
        private function render():void {
            // ライティング
            var light:Vector3D = new Vector3D();
            light.x = 0;
            light.y = 0;
            light.z = -Math.sqrt(1 - light.lengthSquared);
            var invertWorld:Matrix3D = worldMatrix.clone();
            invertWorld.invert();
            light = invertWorld.transformVector(light);
            // 法線マップと光線ベクトルを掛けて光量を得る
            tmp1.applyFilter(normalMap, normalMap.rect, new Point(), createLightingFilter(light));
                        tmp1.paletteMap(tmp1, tmp1.rect, tmp1.rect.topLeft, palette, [], []);
                        tmp1.applyFilter(tmp1, tmp1.rect, tmp1.rect.topLeft, new BlurFilter(8, 8, 3));
            // テクスチャと光量をハードライトブレンドして、ライティングが行われたテクスチャを得る
            tmp2.copyPixels(texture, texture.rect, texture.rect.topLeft);
            tmp2.draw(tmp1, null, null, BlendMode.HARDLIGHT);
            
            // World行列、View行列、Projection行列を結合して一つの行列にする
            var m:Matrix3D = new Matrix3D();
            m.append(worldMatrix);
            m.append(viewMatrix);
            m.append(projection.toMatrix3D());
            
            // 上記の行列を使って頂点座標を投影する
            var projected:Vector.<Number> = new Vector.<Number>();
            Utils3D.projectVectors(m, vertices, projected, uvtData);
            
            viewport.graphics.clear();
            viewport.graphics.beginBitmapFill(tmp2, null, false, true);
            viewport.graphics.drawTriangles(projected, getSortedIndices(), uvtData, TriangleCulling.POSITIVE);
            viewport.graphics.endFill();
        }
        
        /**
         * Zソートされたインデックスを返す
         */
        private function getSortedIndices():Vector.<int> {
            var triangles:Array = [];
            for (var i:int=0; i<indices.length; i+=3) {
                var i1:int = indices[i+0];
                var i2:int = indices[i+1];
                var i3:int = indices[i+2];
                var z:Number = Math.min(uvtData[i1 * 3 + 2], uvtData[i2 * 3 + 2], uvtData[i3 * 3 + 2]);
                if (z > 0) {
                    triangles.push({i1:i1, i2:i2, i3:i3, z:z});
                }
            }
            triangles = triangles.sortOn("z", Array.NUMERIC);
            
            var sortedIndices:Vector.<int> = new Vector.<int>(0, false);
            for each (var triangle:Object in triangles) {
                sortedIndices.push(triangle.i1, triangle.i2, triangle.i3);
            }
            return sortedIndices;
        }
        
        /**
         * 光の方向ベクトルからライティング用フィルタを作成
         */
        private function createLightingFilter(light:Vector3D):ColorMatrixFilter {
            return new ColorMatrixFilter([
                2 * light.x, 2 * light.y, 2 * light.z, 0, (light.x + light.y + light.z) * -0xFF,
                2 * light.x, 2 * light.y, 2 * light.z, 0, (light.x + light.y + light.z) * -0xFF,
                2 * light.x, 2 * light.y, 2 * light.z, 0, (light.x + light.y + light.z) * -0xFF,
                0,           0,           0,           1, 0
            ]);
        }
    }
}