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 psyark ( http://wonderfl.net/user/psyark )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/qJK6
 */

// 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;
		
		/**
		 * コンストラクタ
		 */
		public function Test21() {
    
                        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.5;
			viewport.y = 465 * 0.5;
			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(100, 100, 5, 0, true, true, 7);
			
			tmp1 = new BitmapData(512, 512, false);
			tmp2 = new BitmapData(512, 512, false);
			
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		/**
		 * 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.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));
			// テクスチャと光量を掛けて、ライティングが行われたテクスチャを得る
			tmp2.copyPixels(texture, texture.rect, texture.rect.topLeft);
			tmp2.draw(tmp1, null, null, BlendMode.MULTIPLY);
			
			// 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
			]);
		}
	}
}