지난 2011년 2월말에 Flash Player 11 Incubator 버전이 배포됨에 따라서 3D GPU 가속 렌더링을 위한 Molehill을 미리 경험할 수 있게 되었다. 하지만 Molehill의 등장은 반드시 3D GPU 가속 및 3D 표현 향상만을 의미하지 않는다. 이 관점은 3D 렌더링 엔진을 이용해 2D 렌더링 형태로 활용해도 된다는 관점에서 이해될 수 있다. 기존(현재) Flash Player 10 이하에서 렌더링은 2D를 표현하기 위해 CPU를 이용한 계산으로 렌더링을 하지만 Molehill을 이용하면 이것 조차 GPU를 적용할 수 있으니 기존 2D 게임을 Molehill버전으로 바꿔주면 더욱 쾌적하고 빠른 게임이 될 수 있다.

이 글의 목적은 Molehill을 이용한 2D 그래픽 GPU 가속에 대한 내용을 소개하는데 있다. 그럼 먼저 Molehill에 대해서 간단하게 소개하고 2D 렌더링 속도에 어떤 장점이 있는지 살펴보도록 하겠다.

참고로 아래 글을 통해 Molehill관련 데모와 개발환경 구축방법을 볼 수 있겠다.

Adobe Flash Player 11 Incubator의 "Molehill" 3D를 활용하기 위한 개발환경 만들기http://blog.jidolstar.com/759 



1. Molehill을 이해하기
Flash Player 10.1 이후로 3D API가 공개되었다. 이것은 flash.display.Stage위에 flash.display.DisplayObject기반에서 3D를 다룰 수 있는 API를 추가적으로 제공해준 형태이다. 하지만 완벽한 GPU렌더링을 하지 않으며 z-sort, clipping등과 같은 3D 렌더링을 위한 주요 기능은 전부 배제되었기 때문에 몇몇사람들은 2.5D라고 불렀다.

Molehill에서 3D는 flash.display.Stage위에서 렌더링되는 것이 아니다. 전혀 다른 영역에서 그려지며 flash.display.Stage와는 철저하게 물리적으로 분리되어 있다. 즉 DisplayObject기반으로 돌아가는 것이 아니며 그것과 전혀 연관없는 다른 stage위에서 동작한다. 이와 비슷한 개념은 본인의 블로그의 StageVideo 개념에 대해서 설명하면서 이야기 한바 있다.(StageVideo는 이미 현재 공식배포중인 Flash Player 10.2 기반에서 동작되어지는 부분이다.) StageVideo처럼 별도의 영역에서 렌더링 되어진다는 것이 Molehill에서 지원하는 GPU가속의 비밀(?)인 것이다. 아래 링크를 통해 StageVideo에 대해서 이해할 수 있다.

Flash Player 10.2의 StageVideo에 대해http://blog.jidolstar.com/743

Molehill의 렌더링 영역과 기존 Stage 렌더링 영역이 물리적으로 분리되어 있다는 것은 결국 Molehill만의 별도의 API가 있다는 것을 의미한다. 구체적으로 flash.display.Stage 대신 flash.display.Stage3D 가 존재한다.


위 그림은 Stage3D의 Stage의 관계를 묘사하고 있다. Flash 2D graphics는 Stage 영역을 말하며 Stage3D[0], Stage3D[1]은 Stage3D 영역이다. Stage3D는 Stage위에 올라올 수 없으며 항상 아래에 있다. Stage가 투명인 경우에 Stage3D를 보여줄 수 있다는 점을 말하고 있다. Stage3D는 Stage속성의 stage3Ds.<Vector>를 참고하여 stage.stage3Ds[0] 형태로 접근이 가능하다. 이와는 별도로 StageVideo는 Stage3D 아래에 위치한다. Stage위에 존재할 수 있는 DisplayObject는 Stage3D위에 올라올 수 없다는 점도 기억해둘 필요 있다.

아래 그림은 Stage3D API문서의 일부분을 캡쳐한 것이다.



Stage3D는 flash.display3d.Context3D의 렌더링 영역이며 Stage3D로부터 Context3D를 참조할 수 있다.  Context3D는 일종의 메모리 공간으로 3D 객체를 다루는 데이터 영역이다. (Stage3D와 Context3D의 관계를 이해하기 위해 Bitmap과 BitmapData와의 관계처럼 해석해도 무방할 듯 싶다.) Context3D에서는 z-buffer, back buffer, Color, blend mode, pixel 및 vertex shader 프로그램, vertex stream, vertext indeices, texture 등과 같은 다양한 3D 기능이 담겨있다. 즉 진정한 3D 기능이 모두 주어진 셈이다. 참고로 아래그림은 flash.display3D 패키지에 포함되어있는 Context3D 관련 클래스들이다. 



Molehill API에 익숙해지기 위해 한글문서만큼 좋은 것은 없을 것이다. 아직 초반 기술이라 Adobe에서 제공하는 별도의 한글문서가 없지만 검색해보면 응용형태로 API를 이해해 볼 수 있도록 정리한 고마운 글도 있으니 참고바란다.

Molehill Getting Started :  http://blog.naver.com/q3korea/120126463667


위 문서에 있는 코드를 보면서 느끼겠지만 Molehill API는 매우 저수준의 언어로 구성되어 있다. 결국 학습하기가 어려울 뿐아니라 코딩하는 양도 만만치 않다. 이 때문에 Molehill API를 고수준의 언어로 랩핑(wapping)해준 새로운 3D 라이브러리를 찾기 마련이다. 이미 Away3D, Alternative3D와 같은 유명한 Flash 3D 라이브러리는 기존 Flash Player 9, 10 기반에서 제공하는 API의 인터페이스를 크게 변경하지 않고 Molehill을 경험할 수 있도록 제공하고 있다.


2. 기존 방식과 Molehill과의 2D 렌더링 속도 비교

제목에서 기존방식이라 함은 flash.display.Stage 기반의 렌더링 방식을 의미한다. 기존방식의 렌더링을 위해서는 GPU 도움을 받기 어렵다. 결국 이 기반에서 수많은 렌더링 대상 객체의 수는 제한적일 수 밖에 없었다. 물론 그 안에서도 적절한 최적화를 통해 훌륭한 게임이 많이 나올 수 있었지만 제약은 분명히 있었다.

Molehill을 2D로 표현한다는 것은 실제로는 3D지만 2D처럼 이용한다는 말과 동일하다. 


위 그림은 3D 상에서 사각형을 표현하기 위한 방법을 묘사하고 있다. 기본적으로 3D 객체는 Vertext 3개로 구성된 삼각형 모양의 조각들의 조합으로 이뤄진다. 위처럼 사각형을 하나 만든다면 삼각형 두 조각이 필요한 샘이다. 두개의 삼각형이 한개의 사각형을 이룬다는 점은 하나의 2D 객체 한개를 표현하기 위한 방법으로 사각형 하나면 되고 화면의 시선방향에 대해 사각형의 면이 직각을 이루면 그것이 2D인 것이다. 표현하는 방법은 약간 복잡하더라도 GPU 가속의 큰 도움을 받을 수 있기 때문에 그 정도는 감수할 수 있다.

2.1 기존방식 예제
작년에 필자는 Flash 어플의 속도 개선을 위한 다양한 실험을 했었다. 

Flash 속도 개선을 위한 실험 - 10만개 입자 유체 시뮬레이션 연장전!http://blog.jidolstar.com/671

어떤 언어든 당연한 이야기 이겠지만 ActionScript 3.0으로 만들어지는 코드 한줄 한줄이 Flash 어플 속도에 얼마나 영향을 미칠 수 있는가 알 수 있다. 이외에도 계산이든 렌더링이든지 속도를 향상시킬 수 있는 방법은 여전히 많다. 가령 Pixel Bender, Shader 등을 이용해서 더 빠른 속도를 만들어 낼 수 있다. 하지만 그것도 기존에 나와 있는 다양한 3D 게임들을 따라올법만한 속도는 아닐것이다. 

아래에 위 실험을 통해 사용된 코드를 볼 수 있다.

이 코드를 컴파일 하기 위해 아래 라이브러리 및 클래스가 미리 준비되어야 한다.

com.adobe.utils.AGALMiniAssembler : http://goo.gl/ITcrn 
net.hires.debug.Stats : http://goo.gl/EgZ1s


//출처 : http://wonderfl.net/c/c0Gi/read , http://clockmaker.jp
package 
{
	/**
	 * 수많은 화살표 데모
	 * 고속화를 위한 테스트 
	 * 화살표 1000개 
	 * @author Yasu
	 */
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.utils.*;
	
	import net.hires.debug.Stats;
	
	[SWF(width="465", height="465", backgroundColor="0xFFFFFF")]
	public class NoMolehillTest extends Sprite {
		private const NUM_PARTICLE:uint = 1000;
		private const ROT_STEPS:int = 120;
		
		private var forceMap:BitmapData = new BitmapData( 233, 233, false, 0x000000 );
		private var randomSeed:uint = Math.floor( Math.random() * 0xFFFF );
		private var particleList:Vector.<Arrow> = new Vector.<Arrow>(NUM_PARTICLE, true);
		private var rect:Rectangle = new Rectangle( 0, 0, 465, 465 );
		private var seed:Number = Math.floor( Math.random() * 0xFFFF );
		private var offset:Array = [new Point(), new Point()];
		private var timer:Timer;
		private var world:Sprite = new Sprite();
		private var rotArr:Vector.<BitmapData> = new Vector.<BitmapData>(ROT_STEPS, true);
		
		public function NoMolehillTest() {
			
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.BEST;
			stage.frameRate = 110;
			
			addChild(world);
			
			// 포스 맵의 초기화를 행합니다
			resetFunc();
			
			// 루프 처리
			addEventListener( Event.ENTER_FRAME, loop );
			
			//시간차이로 포스(force)맵과 색변화의 상태를 변경할 것임
			var timer:Timer = new Timer(1000)
			timer.addEventListener(TimerEvent.TIMER, resetFunc);
			timer.start();
			
			//화살표를 생성
			var dummy:Sprite = new Sprite();
			dummy.graphics.beginFill(0xFFFFFF, 1);
			dummy.graphics.lineStyle(1, 0x0, 1);
			dummy.graphics.moveTo(2, 4);
			dummy.graphics.lineTo(8, 4);
			dummy.graphics.lineTo(8, 0);
			dummy.graphics.lineTo(20, 7);
			dummy.graphics.lineTo(8, 14);
			dummy.graphics.lineTo(8, 10);
			dummy.graphics.lineTo(2, 10);
			dummy.graphics.lineTo(2, 4);
			
			var matrix:Matrix;
			var i:int = ROT_STEPS;
			while (i--)
			{
				matrix = new Matrix();
				matrix.translate( -11, -11);
				matrix.rotate( ( 360 / ROT_STEPS * i )* Math.PI / 180);
				matrix.translate( 11, 11);
				rotArr[i] = new BitmapData(22, 22, true, 0x0);
				rotArr[i].draw(dummy, matrix);
			}
			
			// 파티클을 생성
			for (i = 0; i < NUM_PARTICLE; i++) {
				var px:Number = Math.random() * 465;
				var py:Number = Math.random() * 465;
				particleList[i] = new Arrow(px, py);
				world.addChild(particleList[i]);
			}
			
			// 상태표시 
			addChild(new Stats);
		}
		
		private function loop( e:Event ):void {
			
			var len:uint = particleList.length;
			var col:Number;
			
			for (var i:uint = 0; i < len; i++) {
				
				var arrow:Arrow = particleList[i];
				
				var oldX:Number = arrow.x;
				var oldY:Number = arrow.y;
				
				col = forceMap.getPixel( arrow.x >> 1, arrow.y >> 1);
				arrow.ax += ( (col      & 0xff) - 0x80 ) * .0005;
				arrow.ay += ( (col >> 8 & 0xff) - 0x80 ) * .0005;
				arrow.vx += arrow.ax;
				arrow.vy += arrow.ay;
				arrow.x += arrow.vx;
				arrow.y += arrow.vy;
				
				var _posX:Number = arrow.x;
				var _posY:Number = arrow.y;
				
				var rot:Number = - Math.atan2((_posX - oldX), (_posY - oldY)) * 180 / Math.PI + 90;
				var angle:int = rot / 360 * ROT_STEPS | 0;
				// Math.abs보다 더 빠른 계산을 위해 
				angle = (angle ^ (angle >> 31)) - (angle >> 31);
				arrow.rot += (angle - arrow.rot) * 0.2;
				arrow.bitmapData = rotArr[arrow.rot];
				
				arrow.ax *= .96;
				arrow.ay *= .96;
				arrow.vx *= .92;
				arrow.vy *= .92;
				
				// 좌표 배치 좌표를 정수화해 둡니다
				arrow.x = arrow.x | 0;
				arrow.y = arrow.y | 0;
				
				( _posX > 465 ) ? arrow.x = 0 :
					( _posX < 0 ) ? arrow.x = 465 : 0;
				( _posY > 465 ) ? arrow.y = 0 :
					( _posY < 0 ) ? arrow.y = 465 : 0;
			}
		}
		
		private function resetFunc(e:Event = null):void{
			forceMap.perlinNoise(117, 117, 3, seed, false, true, 6, false, offset);
			
			offset[0].x += 1.5;
			offset[1].y += 1;
			seed = Math.floor( Math.random() * 0xFFFFFF );
		}
	}
}

import flash.display.*;

class Arrow extends Bitmap
{
	public var rot:int = 0;
	public var vx:Number = 0;
	public var vy:Number = 0;
	public var ax:Number = 0;
	public var ay:Number = 0;
	
	function Arrow( x:Number, y:Number) {
		this.x = x;
		this.y = y;
	}
}

위 코드는 아래 링크에서 실행해 볼 수 있다. 
http://wonderfl.net/c/c0Gi/read


아래 화면은 위 코드를 실행시에 CPU 사용율과 함께 스크린 캡쳐를 한 것이다.



1000개의 화살표를 렌더링하는데 40~50 FPS 정도의 속도를 유지하면서 CPU 사용율이 70~80 정도 되었다.(필자 PC 스펙이 너무 구형이라 그럴 수도... ㅡㅡ) 분명 속도 자체는 문제 없겠지만 CPU 사용율이 너무 커서 다른 업무에 방해가 될 수 있다. 결국 CPU 사용율을 줄이기 위해 stage.frameRate를 20~30으로 줄일 수 밖에 없다. 또는 성능에 따른 렌더링 주기 조정(http://diebuster.com/flash/170)을 통해 CPU 점유율을 줄일 수 있다. 



2.2 Molehill을 이용한 2D 렌더링 예제

기존방식으로 렌더링하는 것은 속도 개선 및 CPU 사용율 줄이기에 대한 노하우가 크게 필요하다는 사실을 알았다. 물론 Molehill을 쓴다고 이런 노하우는 필요하다. 그래서 같은 방법으로 노하우를 적용하되 Molehill을 함께 사용한다면 사용자의 경험을 더욱 극대화 시킬 수 있을 것이다.

좋다. 그럼 위 예제를 Molehill 버전으로 변경한 코드를 보자. html에 embed시에 wmode는 반드시 direct로 지정되어야 한다. Flash Builder에서 테스트 하시려면 http://blog.jidolstar.com/759 에서 그 방법을 알 수 있다.

이 코드는 아래 코드가 미리 있어야 하겠다.

com.adobe.utils.AGALMiniAssembler : http://goo.gl/ITcrn 
net.hires.debug.Stats : http://goo.gl/EgZ1s
minimalcomps : https://github.com/minimalcomps


//출처 : http://wonderfl.net/c/4VE6
package 
{
	/**
	 * FP11로의 테스트.5000 파티클.빠르지는 되지만 범용성 없음
	 */
	
	import flash.display.*;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DBlendFactor;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DRenderMode;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.display3D.VertexBuffer3D;
	import flash.geom.*;
	import flash.events.*;
	import flash.text.TextField;
	import flash.utils.*;
	import flash.geom.*;
	import net.hires.debug.Stats;
	import com.bit101.components.ComboBox;
	
	[SWF(width="465", height="465", backgroundColor="0xFFFFFF")]
	public class MolehillTest extends Sprite {
		private const TEXTURE_WIDTH:int = 2048;
		private const NUM_PARTICLE:uint = 16380; // 파티클 최고수 
		private var ROT_STEPS:int = 0;
		
		private var num_limit:uint = 5000; // 렌더링 하는 수
		
		private var forceMap:BitmapData = new BitmapData( 233, 233, false, 0x000000 );
		private var randomSeed:uint = Math.floor( Math.random() * 0xFFFF );
		private var particleList:Vector.<Arrow> = new Vector.<Arrow>(NUM_PARTICLE, true);
		private var rect:Rectangle = new Rectangle( 0, 0, 465, 465 );
		private var seed:Number = Math.floor( Math.random() * 0xFFFF );
		private var offset:Array = [new Point(), new Point()];
		private var timer:Timer;
		private var world:Sprite = new Sprite();
		private var rotBmp: BitmapData;
		private var text : TextField;
		
		private var combobox : ComboBox;
		
		
		private var context : Context3D;
		private var program : ShaderProgram;
		private var iBuffer : IndexBuffer3D;
		private var vBuffer : VertexBuffer3D;
		private var uvBuffer : VertexBuffer3D;
		private var texture : Texture;
		private var ortho : Matrix3D = new Matrix3D();
		private var r_rot_steps : Vector.<Number> = Vector.<Number>([0,0,0,0]);
		
		private var vb : Vector.<Number> = new Vector.<Number>();
		private var uvb : Vector.<Number> = new Vector.<Number>();
		private var ib : Vector.<uint> = new Vector.<uint>();
		private const vunit : int = 4;
		private const uvunit : int = 2;
		
		private var uirect : Sprite = new Sprite;
		
		public function MolehillTest() {
			
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.BEST;
			stage.frameRate = 110;
			
			addChild(world);
			
			// 포스 맵의 초기화를 행합니다
			resetFunc();
			
			// 루프 처리
			//addEventListener( Event.ENTER_FRAME, loop );
			
			//시간차이로 포스(force)맵과 색변화의 상태를 변경할 것임
			var timer:Timer = new Timer(1000)
			timer.addEventListener(TimerEvent.TIMER, resetFunc);
			timer.start();
			
			//화살표를 생성
			var dummy:Sprite = new Sprite();
			dummy.graphics.beginFill(0xFFFFFF, 1);
			dummy.graphics.lineStyle(1, 0x0, 1);
			
			dummy.graphics.moveTo(2, 4);
			dummy.graphics.lineTo(8, 4);
			dummy.graphics.lineTo(8, 0);
			dummy.graphics.lineTo(20, 7);
			dummy.graphics.lineTo(8, 14);
			dummy.graphics.lineTo(8, 10);
			dummy.graphics.lineTo(2, 10);
			dummy.graphics.lineTo(2, 4);
			
			var bmpw : int = TEXTURE_WIDTH;
			ROT_STEPS = bmpw / 16; 
			var matrix:Matrix;
			rotBmp = new BitmapData(bmpw, 16, true, 0x0);
			var i:int = ROT_STEPS;
			while (i--)
			{
				matrix = new Matrix();
				matrix.translate( -11, -7);
				matrix.rotate( ( 360 / ROT_STEPS * i )* Math.PI / 180);
				matrix.scale(0.75, 0.75); // 크기를 줄였어요.
				matrix.translate( 8+i*16, 8);
				rotBmp.draw(dummy, matrix);
			}
			
			// 파티클을 생성합니다
			for (i = 0; i < NUM_PARTICLE; i++) {
				var px:Number = Math.random() * 465;
				var py:Number = Math.random() * 465;
				particleList[i] = new Arrow(px, py);
				//world.addChild(particleList[i]);
			}
			
			// ui
			uirect.graphics.clear();
			uirect.graphics.beginFill(0x000000, 0.8);
			uirect.graphics.drawRoundRect(0, 0, 200, 100, 16, 16);
			uirect.graphics.endFill();
			
			combobox = new ComboBox(uirect, 80, 8, "5000", new Array(500, 1000, 5000, 10000, 16380));
			combobox.selectedIndex = 2;
			num_limit = 5000;
			
			addChild(uirect);
			
			// 디버그용의 스탓트를 표시하고 있습니다
			addChild(new Stats);
			
			//
			stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, createContext3D);
			stage.stage3Ds[0].requestContext3D();
			stage.stage3Ds[0].viewPort = new Rectangle(0, 0, rect.width, rect.height);
			
		}
		
		private function createContext3D(e:Event):void 
		{
			context = (e.target as Stage3D).context3D;
			context.configureBackBuffer(465, 465, 0, false);
			context.setRenderToBackBuffer();
			context.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
			context.enableErrorChecking = true;
			
			text = new TextField();
			text.textColor = 0xffffff;
			text.text = context.driverInfo;
			text.width = 465;
			text.y = 450;
			addChild(text);
			
			program = new ShaderProgram(context, new VertexShader(), new FragmentShader());
			ortho = MatrixUtil.ortho(rect.width, rect.height, false);        
			r_rot_steps[0] = 1/ROT_STEPS;
			
			for (var i : int = 0; i < NUM_PARTICLE; i++) {
				// 시험 삼아 정적인 다각형 정보와 매프레임 갱신하는 위치 정보를 다른 vertexBuffer로 해 보았지만, 속도적으로는 변화 없음
				vb.push( -8, -8,  0, 0);
				vb.push(  8, -8,  0 ,0);
				vb.push(  8,  8,  0 ,0);
				vb.push( -8,  8,  0, 0);
				
				uvb.push( 0,          0);
				uvb.push( 1/ROT_STEPS,0);
				uvb.push( 1/ROT_STEPS,1);
				uvb.push( 0,          1);
				
				
				ib.push( i*4+0, i*4+1, i*4+2, i*4+0, i*4+2, i*4+3 );
			}
			vBuffer = context.createVertexBuffer(vb.length / vunit, vunit);
			vBuffer.uploadFromVector(vb, 0, vb.length / vunit);
			
			uvBuffer = context.createVertexBuffer(uvb.length / uvunit, uvunit);
			uvBuffer.uploadFromVector(uvb, 0, uvb.length / uvunit);
			
			iBuffer = context.createIndexBuffer(ib.length);
			iBuffer.uploadFromVector(ib,0,ib.length);
			
			try {
				texture = context.createTexture(TEXTURE_WIDTH, 16, Context3DTextureFormat.BGRA, false);
				texture.uploadFromBitmapData(rotBmp);
				context.setTextureAt( 1, texture );
			} catch (e:Error) {
				text.text = e.message;
			}
			
			addEventListener(Event.ENTER_FRAME, loop);
		}
		
		private function loop( e:Event ):void {
			
			context.clear(0.4, 0.4, 0.5, 1); //이 정도치에 clear가 없으면 texture가 사라진다
			
			num_limit = parseInt(combobox.items[combobox.selectedIndex]);
			
			var len:uint = num_limit < particleList.length ? num_limit : particleList.length ;
			var col:Number;
			var index : int = 0;
			for (var i:uint = 0; i < len; i++) {
				var arrow:Arrow = particleList[i];
				
				var oldX:Number = arrow.x;
				var oldY:Number = arrow.y;
				
				col = forceMap.getPixel( arrow.x >> 1, arrow.y >> 1);
				arrow.ax += ( (col      & 0xff) - 0x80 ) * .0005;
				arrow.ay += ( (col >> 8 & 0xff) - 0x80 ) * .0005;
				arrow.vx += arrow.ax;
				arrow.vy += arrow.ay;
				arrow.x += arrow.vx;
				arrow.y += arrow.vy;
				
				var _posX:Number = arrow.x;
				var _posY:Number = arrow.y;
				var rot:Number = - Math.atan2((_posX - oldX), (_posY - oldY)) * 180 / Math.PI + 90;
				var angle:int = rot / 360 * ROT_STEPS | 0;
				// Math.abs의 고속화군요
				angle = (angle ^ (angle >> 31)) - (angle >> 31);
				//arrow.rot += (angle - arrow.rot) * 0.2;
				//arrow.bitmapData = rotBmp;
				
				arrow.ax *= .96;
				arrow.ay *= .96;
				arrow.vx *= .92;
				arrow.vy *= .92;
				
				// 배치 좌표를 정수화해 둡니다
				//arrow.x = arrow.x | 0;
				//arrow.y = arrow.y | 0;
				
				( _posX > 465 ) ? arrow.x = 0 :
					( _posX < 0 ) ? arrow.x = 465 : 0;
				( _posY > 465 ) ? arrow.y = 0 :
					( _posY < 0 ) ? arrow.y = 465 : 0;
				
				vb[index++] = (_posX - 465.0/2)-8;
				vb[index++] = (_posY - 465.0/2)-8;
				vb[index++] = angle >> 0;
				index++;
				
				vb[index++] = (_posX - 465.0/2)+8;
				vb[index++] = (_posY - 465.0/2)-8;
				vb[index++] = angle >> 0;
				index++;
				
				vb[index++] = (_posX - 465.0/2)+8;
				vb[index++] = (_posY - 465.0/2)+8;
				vb[index++] = angle >> 0;
				index++;
				
				vb[index++] = (_posX - 465.0/2)-8;
				vb[index++] = (_posY - 465.0/2)+8;
				vb[index++] = angle >> 0;
				index++;
			}
			vBuffer.uploadFromVector(vb, 0, num_limit*4); 
			
			context.setProgram(program.program);
			
			context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, ortho, true);
			context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, r_rot_steps);
			
			context.setVertexBufferAt(0, vBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
			context.setVertexBufferAt(1, vBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
			context.setVertexBufferAt(2, uvBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
			
			context.drawTriangles(iBuffer, 0, 2*num_limit);
			context.present();
		}
		
		private function resetFunc(e:Event = null):void{
			forceMap.perlinNoise(117, 117, 3, seed, false, true, 6, false, offset);
			
			offset[0].x += 1.5;
			offset[1].y += 1;
			seed = Math.floor( Math.random() * 0xFFFFFF );
		}
	}
}

import com.adobe.utils.AGALMiniAssembler;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Program3D;
import flash.geom.Matrix3D;

import flash.display.*;

class Arrow// extends Bitmap
{
	public var rot:int = 0;
	public var vx:Number = 0;
	public var vy:Number = 0;
	public var ax:Number = 0;
	public var ay:Number = 0;
	public var x:Number = 0;
	public var y:Number = 0;
	
	function Arrow( x:Number, y:Number) {
		this.x = x;
		this.y = y;
	}
}

class ShaderProgram
{
	public var program : Program3D = null;
	
	public function ShaderProgram(context : Context3D, vsh : AGALMiniAssembler, fsh : AGALMiniAssembler)
	{
		program = context.createProgram();
		program.upload(vsh.agalcode, fsh.agalcode);
	}
}

class VertexShader  extends AGALMiniAssembler
{
	//
	// 
	//  vBuffer0(x,y)     --> attribute(0) : va0.xy
	//  vBuffer1(rot,rsv) --> attribute(1) : va1.x, va1.y
	//  uvBuffer2(u,v)    --> attribute(2) : va2.xy
	//
	// 
	//  projmatrix(transposed)   --> | vc0.x  vc1.x  vc2.x  vc3.x |
	//                               | vc0.y  vc1.y  vc2.y  vc3.y |
	//                               | vc0.z  vc1.z  vc2.z  vc3.z |
	//                               | vc0.w  vc1.w  vc2.w  vc3.w |
	//
	//  texture coord step       --> vc4.x   (1/rotation steps)
	//
	// 
	//  position                 --> op.xyzw
	//
	// 
	//  texture coord            --> v0.uv
	//
	//  m44 op, va0, vc0             position = (x,y) * projmatrix
	//  mov v0, va2                  uv = (u,v)
	//  mul vt0.x, va1.x, vc4.x      
	//  add v0.x, va2.x, vt0.x       u = u + (rot*texture_coord_step)
	//
	private var src : String =
		"m44 op, va0, vc0 \n" + // m33라도 좋을지도
		"mov v0, va2 \n" +
		"mul vt0.x, va1.x, vc4.x \n" +
		"add v0.x, va2.x, vt0.x \n" +
		"";    
	
	public function VertexShader()
	{
		assemble(Context3DProgramType.VERTEX, src);
	}
}

class FragmentShader  extends AGALMiniAssembler
{
	private var src : String =
		"mov ft0, v0\n" +
		"tex ft1, ft0.xy, fs1 <2d,repeat,nearest>\n" + 
		"mov oc, ft1\n";
	
	public function FragmentShader()
	{
		assemble(Context3DProgramType.FRAGMENT, src);
	}
}
class MatrixUtil
{
	public static function ortho(w : int, h : int, rev : Boolean) : Matrix3D
	{
		return new Matrix3D(Vector.<Number>([2/w, 0, 0, 0,  0, rev?-2/h:2/h, 0, 0,  0, 0, 1, 0,  0, 0, 0, 1]));
	}
	
}


위 코드는 아래 링크를 통해 실행해 볼 수 있다.
http://escargot.la.coocan.jp/molehill_fast/index.html


아래 화면은 화살표를 5000개로 맞춰놓고 렌더링한 캡쳐화면이다. 기존방식보다 4000개 이상 처리함에도 불구하고CPU 점유율과는 비교가 안될 정도로 떨어졌고 FPS도 50~60사이이다. 


아래 화면은 16380개로 올린 결과 화면이다. CPU 점유율은 24%로 전보다 다소 올라갔으나 FPS는 변함이 거의 없다. CPU 점유율이 올라간것은 as3 코드 loop 부분이 더 복잡해서일 뿐이다. 


두가지 결과만 보더라도 Molehill을 통한 GPU 가속은 꽤나 유용함을 금방 알 수 있다. 
 
아래 링크의 예제를 통해 같은 방법으로 비교해보는 것도 괜찮을 것 같다.

기존방법 Clear Water: http://wonderfl.net/c/9R8a
Molehill이용 Clear Water  : http://wonderfl.net/c/enj6
관련 기술 설명 : http://goo.gl/eySSG


3. Molehill 기반 2D framework 소개 

Molehill을 이용해 2D를 렌더링하는 것이 얼마나 이득이 될 수 있는가 알아보았다. 이러한 이득은 곧바로 필요성으로 이어져 관련 framework 개발로 이어질 수 있다. 찾아보니 실제로 그런 프로젝트가 있었다.

소개하고자 하는 M2D라는 오픈소스 프로젝트는 Molehill을 이용해 2D을 표현하도록 하도록 하는데 목적이 있다. Molehill의 저수준의 언어 접근성을 고수준으로 만들어주어 개발이 용이하게 만든것이 특징이다. 이는 iOS기반 GPU가속 2D라이브러리인 Cocos2D와 같은 필요성에서 탄생된 프로젝트이다.



아직 진행중인 프로젝트이기 때문에 관련된 문서도 없고 기능도 제약이 있다. 

ND2D라는 라이브러리도 있다. 


ND3D를 만든 개발자가 Molehill 버전의 2D 엔진인 ND2D를 만든것이다.
위 사진은 아래 링크에서 실행해 볼 수 있다.
http://www.nulldesign.de/2011/03/10/nd2d-molehill-2d-engine/ 

소스는 아래 링크에서 다운받을 수 있겠다.
https://github.com/nulldesign/nd2d

M2D보다는 ND2D가 더 좋아보인다. ^^


4. 정리하며 

지금까지 내용을 통해 Flash Player 차세대 렌더링 엔진 Molehill을 이용해 2D 표현시 어떤 장점이 있을까 객관적인 자료를 찾아보고 분석해보았다. Molehill을 이용해 GPU 가속을 할 수 있다는 것 자체는 큰 장점이긴 하나 그만큼 연구해야할 내용은 더 많아진 것은 사실이다. 앞으로 Flash Player가 모바일에서 활약하는 날이 올때 그 빛은 엄청 발할 것이라 판단한다. 그러므로 우리 개발자들은 미리 준비할 필요가 있겠다.

Flash Player 11 Incubator를 통해 차세대 3D 엔진인 Molehill을 미리 경험할 수 있다는 것은 큰 혜택이다. 새롭게 연구해야하는 과정은 참 어렵다. 필자도 이런 어려운 과정을 겪고 있으며 혼자 이런 연구를 하는게 무척이나 힘들다. 뭔가 이러한 일을 함께 하는 사람들이 많았으면 하는 바램이다.


5. 관련글


 글쓴이 : 지돌스타(http://blog.jidolstar.com/763)

어도비는 2011년 2월 말에 Flash Player Incubator를 배포했다. 이것은 Flash Player 11이 정식 배포되기 전 개발자들이 테스트할 수 있도록 Adobe에서 실험용 버전으로 배포한 것이다. 다음 링크는 Flash Player Incubator 공식 페이지이다.

Adobe AIR and Adobe Flash Player Incubator : http://goo.gl/aFqZH

이 글은 Flash Player Incubator에 대한 간략한 소개와 함께 Flash Builder 개발환경을 만들어 테스트하는 방법을 다룬다.(처음 해보는 사람을 위해서 자세하게 쓰려고 노력했다.)

1. Flash Player Incubator 에 대해

이번에 개발용으로 배포된 Flash Player Incubator는 다음 두 가지 feature가 추가되었다.

1.1 "Molehill" 3D API
GPU가속을 위한 low-level 3D API를 지원한다.  기존 Flash Player 10.2에서 지원하지 못했던 z-buffering, stencil color buffer, fragmentvertex shader, 큐브 텍스처 등을 기본으로 지원한다. 이 API를 사용하면 윈도우에서는 DirectX 9, 리눅스와 MacOS에서는 OpenGL 1.3, 그리고 모바일에서는 OpenGL ES 2.0 기반에서 렌더링이 될 수 있도록 한다. 만약 여기에 상관되어 있지 않다면 기존처럼 CPU 렌더링한다. 기존 Flash Player 10.1 에서는 3D API가 있긴 하지만 z-buffering을 지원하지 않으면서도 수백 개의 폴리곤을 CPU 렌더링하는데 30fps가 나왔던 반면, Molehill에서는 z-buffering이 적용된 수십만 개의 폴리곤을 HD급으로 렌더링하는데 60fps가 나온다. 단, 웹브라우져에서 이를 실행하는 경우, GPU가속을 하기 위해서는 wmode가 direct로 설정되어야 한다. 다음 페이지는 Flash Incubator의 Molehill 3D API에 대한 Adobe 공식 페이지이다.

3D APIs for Adobe Flash Player and Adobe AIR : http://goo.gl/yLdvx

3D APIs는 매우 low-level API이기 때문에 직접 다루면 개발 생산성과 효율성에 문제가 있을 수 있으므로 Alternative3D, Away3d, CopperCube, Flare3D, Minko, Sophie3D, Yogurt3D와 같은 ActionScript 3D 프레임워크들로 좀더 쉽게 개발할 수 있는 환경이 조성될 것이다. 이들 중에 조사한바 다음 프레임워크는 Molehill 3D APIs를 지원하고 있다.

1.2 Cubic Bezier Curves
flash.display.Graphic에 cubicCurveTo 메서드가 추가되었다. 이 메서드는 커스텀 ActionScript 코드를 사용하지 않고 쉽게 Cubic Bezier Curve를 그릴 수 있게 해준다. 어떤 것인지 궁금하면 다음 동영상으로 보자.

http://www.vimeo.com/10187896
http://goo.gl/yooER

이 글은 위 두 가지 feature중 Flash Player Incubator 환경에서 "Molehill" 3D APIs를 테스트할 수 있는 개발환경을 구성하는 방법을 소개한다. 특별히 Flash Builder Burrito를 기반으로 설명할 것이다. 아쉽지만 Flash CS5 IDE 사용방법을 몰라서 이에 대한 설명은 하지 않겠다. 대신 Flash CS5에서 테스트 환경 구성은 다음 글을 통해 공부할 수 있다.

http://blog.naver.com/q3korea/120125249427


2. Flash Player Incubator 버전 설치하기 

Flash Player Incubator는 현재 MS Windows, MacOSX, Linux용으로 배포하고 있다. 
http://labs.adobe.com/downloads/flashplatformruntimes_incubator.html

설치를 할 때 문서 아래에 Uninstaller를 먼저 실행하고 설치하기 바란다. 이 버전은 다 개발용이기 때문에 Debugging 기능이 내장되어 있다. 설치를 하지 않으면 아래 예제들을 실행할 수 없다.

참고로 Google Chrome을 사용하면 구동되는 Flash Player를 선택할 수 있다. 기존 Flash Player를 지우지 않고 전환해서 테스트 해볼 수 있기 때문에 유용하다. 다음 글을 참고한다.

Google Chrome 에서 Flash Player 10과 11(Molehill)을 전환하여 사용하는 방법 : http://ufx.kr/blog/142 


3. Molehill 3D 경험하기

Molehill 3D API의 결과물을 한번에 볼 수 있는 동영상을 경험해보길 바란다. 
http://blog.theflashblog.com/?p=2638

다음 링크를 통해 Molehill 3D APIs 를 이용한 다양한 Flash 어플들을 실행해 볼 수 있다. 단, Flash Player Incubator 버전이 여러분의 브라우저에 설치 되어야만 제대로 볼 수 있다.
 

http://molehill.zombietycoon.com/


http://alternativaplatform.com/en/demos/maxracer/


http://alternativaplatform.com/ru/demos/metro2033online/


http://www.lidev.com.ar/demos/fractals/tree/broomstick/



http://www.mcfunkypants.com/2011/molehill-terrain-demo/



http://infiniteturtles.co.uk/projects/away3d/broomstick/ShallowWaterDemo.html


http://www.swfgeek.net/2011/02/28/cubewall-using-away3d-4-0broomstick-jiglibflash-on-molehill/



http://not-so-stupid.com/clients/not-so-stupid/away4/duck/




http://aerys.in/minko-quake-3



http://infiniteturtles.co.uk/projects/away3d/broomstick/LoaderOBJTest.html




http://infiniteturtles.co.uk/projects/away3d/broomstick/EnvMapTest.html




http://infiniteturtles.co.uk/projects/away3d/broomstick/AnimBlendTest.html




http://infiniteturtles.co.uk/projects/away3d/broomstick/EnvMapDiffuseTest.html



http://acemobe.com/dungeon/demo.php



http://www.ambiera.com/coppercube/demo.php?demo=backyard



http://iflash3d.com/performance/how-fast-is-molehill/



http://ryanspeets.com/uncategorized/adobe-releases-molehill/



http://www.nulldesign.de/wp-content/uploads/2011/nd2d_spritesheets/Main.html



http://www.ringo.nl/projects/jiglibflash/Away3DGridSystem.html
http://www.ringo.nl/projects/jiglibflash/Away3DTriangleMesh.html
http://www.ringo.nl/projects/jiglibflash/Away3DStackingTest.html
http://www.ringo.nl/projects/jiglibflash/Away3DTerrainTest.html
http://www.ringo.nl/projects/jiglibflash/Away3DCarDrive.html

더 많은 데모 : http://blog.theflashblog.com/?p=2607


4. Flash Player Incubator용 API 문서 보기

Flash Player Incubator에 적용된 API를 학습하기 위한 문서이다. 다운로드 받아 보면 좋겠다.
Download the documentation for Flash Player 11,0,0,58 

API 문서를 보기 전에 아래 글을 선행적으로 본다면 이해하는데 도움이 될 것이다.
Digging more into the Molehill APIs
MoleHill Getting Started


5. Flash Builder Burrito에 Flash Player Incubator 개발환경 만들기

Flash Builder Burrito는 beta 버전으로 Flex Hero 및 모바일 개발을 할 수 있는 환경을 제공한다. Flash Builder Burrito에 함께 포함되어 있는 Flex SDK는 Flash Player 10.1 기반으로만 개발할 수 있기 때문에 Flash Player Incubator 환경에서 개발할 수 있는 환경을 따로 구축할 필요가 있다. 다음 과정을 통해 개발 환경을 구축 할 수 있다.

5.1. 다운로드
개발 환경을 구성하기 전에 다음 링크를 통해 해당 자료를 다운로드 받는다.

- Flash Player 11,0,0,58 Incubator : http://goo.gl/yXySu
- Flash Builder Burrito(로그인 필요) : http://goo.gl/iM7g
- playerglobal.swc : http://goo.gl/NnEyx
- Flex SDK 4.5.0.19786 : http://goo.gl/2lAIb

5.2. 설치

설치하는 과정은 아래 순서대로 따라 하면 되겠다. 복잡하지 않으니 천천히 하도록 하자.

첫 번째로 Flash Player 11,0,0,58 Incubator를 설치한다. 이 글대로 진행했다면 이미 설치가 되었을 것이다. 설치가 정상적으로 이루어졌다면 해당 브라우저에서 Flash Player가 구동이 될 때 화면 아래 Flash Player 11,0,0,58 문구가 고정되어 새겨져 있을 것이다.

두 번째로 Flash Builder Burrito를 설치한다. Flash Builder Burrito는 일종의 Flash관련 어플을 개발하는 Adobe공식 통합개발도구(IDE)이다. Flash Develop, FDT등 다양한 툴이 있긴 하지만 여기서는 Flash Builder Burrito만 다룬다. 개인적으로는 Eclipse plug-in 버전을 다운받아 설치할 것을 권장 하지만 Flash Builder 에 제작 환경에 경험이 없다면 그냥 All-in-one 버전으로 설치한다.

세 번째로 Flex SDK 4.5.0.19786를 압축을 풀어 {Flash Builder Burrito 설치 경로}/sdks/에 복사해 넣는다. 윈도우라면 /Program Files/Adobe/Adobe Flash Builder Burrito/sdks/flex_sdk_4.5.0.19786 이 될 것이다. 이 SDK는 Flash Player 10.2에 대응하도록 되어 있다. 

네 번째로 다운받은 flashplayer_inc_playerglobal_022711.swc를 {Flash Builder Burrito 설치 경로}/sdks/frameworks/libs/player에 11.0 폴더를 만들고 복사한다. 이름을 playerglobal.swc로 수정한다. playerglobal.swc 는 Flash Player 11.0 이상의 API를 이용해 개발하고 컴파일할 수 있도록 하기 위해 필요하다. 

다섯 번째로 Flash Builder Burrito를 실행한다. 중간에 워크스페이스 경로를 설정하는 부분이 있는데 원하는 경로로 설정하면 되겠다. 이 경로상에 여러분의 Flash, AIR 프로젝트 들이 생성될 것이다.


5.3. 설정

Flash Builder를 이용해 몇 가지 설정을 통해 개발환경을 만들어보자.

첫 번째로 메뉴에서 Window > Preference로 들어간다. 창이 뜨면 왼쪽에 Flash Builder를 펼치고 그 안에 Installed Flex SDKs를 선택한다. 아래 화면과 같이 나올 것이다. 
 


여기에서 아까 설치했던 Flex SDKs를 추가한다. 좌측에 Add 버튼을 누르고 {Adobe Flash Builder Burrito 설치경로}/sdks/flex_sdk_4.5.0.19786 를 선택한다. 이때 Name은 Flex 4.5(FP11)로 정하자. 그리고 추가된 SDK를 위 그림처럼 Default SDK로 체크한 뒤 OK를 누른다.

두 번째로 ActionScript 3.0 프로젝트를 생성하고 몇 가지 설정을 하겠다. 메뉴에서 File > New > ActionScript Project 를 선택한다. 그럼 아래와 같은 창이 뜬다. Project name에 아무 이름이나 넣는다. 우리는 FP11Test로 했다. 참고로 Flex SDK version이 이전에 선택한 SDK가 나옴을 확인할 수 있다. 필요하다면 여기서 설치된 SDK를 선택할 수 있음을 의미한다. 우리는 기본 SDK를 사용할 것이므로 그대로 두고 Finish 버튼을 누른다. 



세 번째로 Flash builder의 Package Explorer 창에 FP11Test 프로젝트가 만들어졌을 것이다. 

프로젝트는 src와 html-template, bin-debug 폴더가 있다. src는 여러분이 만들 actionscript 3.0 기반 코드이며 여기서 기본적으로 만들어진 FP11Test.as는 FP11Test 클래스이며 flash.display.Sprite 기반으로 만들어졌다. 일종의 Main 클래스이다. html-template는 배포를 위해 사용하는 것으로 컴파일된 swf를 웹브라우져상에 배포하는데 있어서 필요한 html, javascript 코드가 담겨 있다. 개발자는 필요하다면 이 코드를 수정하면 컴파일 시 반영된다. bin-debug는 html-template를 기반으로 컴파일 된 swf와 함께 컴파일 된 결과물이다. 이는 실제배포 소스는 아니며 디버깅용도로 사용된다. flash builder상에서 이 어플을 실행하면 FP11Test.html이 웹브라우저상에 실행되며 그 안에 FP11Test.swf가 포함되어 실행된다. 

Flex 4.5(FP11) SDK를 사용하고 있음을 보여주고 있다. 이전에 Flex 4.5(FP11) SDK에 playerglobal.swc를 복사해뒀던 것을 기억할 것이다. 하지만 여기서 보여지고 있는 playerglobal.swc는 Flash Player 10.2 버전의 API를 담은 SWC이다. 우리는 아까 복사해둔 11.0 버전을 지원하는 playerglobal.swc가 선택이 되도록 해야 한다. 


위 그림상에서 보는 데로 프로젝트(FP11Test)를 선택한 상태에서 메뉴의 Project > Properties를 선택하자. 아래 그림과 같은 창이 나올 것이다. 오른쪽 메뉴에서 ActionScript Compiler를 선택한 뒤 Adobe Flash Player options에 Use a specific version을 11.0.0으로 바꾼다. 또한 Compiler Options에 -swf-version=13를 넣는다. HTML wrapper는 그대로 둬도 되지만 Check target player version과 Enable integration with browser navigation은 의미가 없으므로 체크를 하지 않도록 한다. 그리고 OK 버튼을 누르면 되겠다. 



마지막으로 Flash Player 11환경에서 GPU 가속을 시키려면 wmode가 direct로 설정되어 있어야만 한다. 프로젝트상에 html-template내 index.template.html을 연다. 참고로 이때 더블 클릭하면 웹브라우저가 실행되어 버린다. 더블 클릭하지 말고 마우스 오른쪽 버튼을 눌러 Open With > Text Editor로 열면 html 소스코드를 볼 수 있다. (더블클릭때마다 웹브라우저를 열지 않는 방법이 있다. 메뉴 > Window > Preference > General > Editors > File Associations를 참고하자.) index.template.html 내에 스크립트 상에 var params = {}; 부분이 있다. 그 아래 params.wmode = "direct";를 추가하자. 
 

5.3. 실행 
이제 실행해볼 수 있게 되었다. Ctrl+F11(Run) 또는 F11(Debug)를 눌러 실행해보자. 아래 화면처럼 브라우저 창에 Adobe Flash Player 11 (11,0,0,58d) (Incubator build)가 뜨면서 실행되었으면 성공이다. 만약 실행이 안되거나 컴파일 에러가 난다면 위 과정을 다시 살펴보길 바란다.



5.4. 간단한 코드 짜보기 

이제 Molehill 3D API를 직접 적용해보자. 여기서는 소스 코드를 분석해서 설명하기 보다 그저 실행해보는데 목적이 있음을 알아두자.

아래 링크로 가면 Molehill 3D API에 대한 소개와 간단한 테스트 코드가 있다. 

MoleHill Getting Started : http://labs.jam3.ca/2011/03/molehill-getting-started/ 

이 소스는 AGAL 어셈블러인 AGALMiniAssembler 소스가 필요하다. 
AGALMiniAssembler.as : http://goo.gl/ITcrn


아래 링크로부터 이 두 개 소스를 받자.


이것을 압축을 풀어 프로젝트 src에 복사하자. 그리고 Ctrl+F11이나 F11로 실행하면 다음과 같은 화면을 볼 수 있다. 



다른 소스도 테스트 해보자.
먼저 프로젝트 내에 src/(Defaul package)를 선택한 다음 오른쪽 마우스 버튼을 눌러 New > ActionScript Class를 선택한다.

두 번째로 창이 뜨면 Name에 Cube를 넣고 Finish를 한다.

세 번째로 아래 링크에서 샘플 소스를 복사한 뒤 아까 만든 Cube.as를 선택해 이전 코드를 삭제하고 붙여넣자.
http://goo.gl/ITcrn

네 번째로 아래 그림처럼 Cube.as를 선택하고 마우스 오른쪽 버튼을 눌러 Set as Default Application을 선택한다. 이렇게 하면 이 프로젝트의 기본 Main 실행 클래스가 Cube.as가 된다. 이 상태에서 Ctrl+F11이나 F11를 눌러 실행해보자. 


실행 시 Bad input size라는 Error가 발생한다면 IE내에서 실행하는 Flash Player의 버그이다(참고 : http://blog.jidolstar.com/656). 이 때는 아래 코드로 대체해보자.


성공적으로 실행하면 다음과 같은 화면을 볼 수 있을 것이다. 


이 소스에 대한 설명은 http://ltslashgt.com/2011/02/28/molehill-spinning-cube/ 를 참고한다.


6. 정리하며
지금까지 Flash Player Incubator 환경에서 Molehill 3D를 테스트 해볼 수 있는 개발환경을 구축하고 테스트하는 방법을 알아보았다. 하지만 이 글은 Molehill 3D API에 대한 자세한 설명을 배제했다. 이 글 다음에는 Molehill 3D API에 대한 내용을 정리할 계획을 가지고 있다. 

Molehill 3D API를 이용한 마지막 2개의 테스트 소스를 보면 알겠지만 굉장히 접근하기 형태의 Native API이기 때문에 코딩으로 직접 3D를 표현하는 것은 질 좋은 3D Flash 어플을 만드는데 한계가 있다. 그렇기 때문에 Away3D나 Alternativa3D와 같은 프레임워크를 도입해야 하는 이유가 여기에 있는 것이다. 이 부분에 대해서도 이후에 글을 적어보려고 한다. 

앞선 테스트 어플들에 3D가 아닌 2D화면도 봤을 것이다. Molehill이 무조건 3D에서만 GPU를 이용하는 것은 아니다는 것을 보여준다. 기존에 많은 2D Flash 컨텐츠도 Molehill을 적극 활용하면 생각지도 못했던 양질의 퍼포먼스를 기대할 수 있다는 것을 보여주는 셈이다. 일종에 아이폰의 OpenGL ES기반으로 2D를 표현하기 위한 Cocos2d가 있듯이 Flash에서도 이러한 형태로 표현할 수 있음을 알 수 있다. 아래 링크의 동영상을 보면 그 이유를 알 수 있을 것이다.
http://clockmaker.jp/blog/2011/03/molehill_video/ 

Flash Player에 3D 기능을 추가됨에 따라 Unity3D에 Flash Player에서도 동작할 수 있도록 해주는 기능도 포함되었다. 이것은 Unity3D 개발자나 Flash 개발자 양쪽에 꽤 흥미로운 사실이다. 

 
아직 Flash Player 11이 정식오픈 되려면 시간이 필요할 것이다. 버그 수정을 거치면서 안정화를 시켜야 하기 때문이다. 

Flash 3D가 표현된다는 것은 무조건 좋은 일만은 아니다. Flash는 웹브라우저 및 크로스 플랫폼에서 운용되기 때문에 접속할 때마다 새로 자원을 로드 해야 한다. 이는 자원을 로컬에 저장해두는데 제약이 있기 때문인데 결과적으로 접속할때마다 필요한 자원을 로드함에 있어서 2D자원보다 3D자원이 훨씬 크고 관리하기 어렵다. 이는 위에 이미 소개한 Flash 3D 예제를 보면서 느꼈을 것이다. 멋진 3D 화면을 보기 위해 자원을 로드하는데 꽤 오랫동안 기다려야 한다. 물론 AIR로 개발한다면 상관없겠지만 웹기반에서 소셜게임을 만든다면 이야기가 다르다. 이 말은 자원관리가 그만큼 어려워지고 전문적으로 해야함을 의미한다.

그럼에도 불구하고 Flash가 3D를 표현하는데 GPU를 지원한다는 사실만으로도 많은 장점이 있기에 3D를 준비해야 차기 Flash 시장의 미래를 선도할 수 있을 것이다. 미리 준비할 필요가 있다.
 

7. 관련사이트 
Adobe AIR and Adobe Flash Player Incubator : http://goo.gl/pvwzd
Flash Player 11,0,0,58 Incubator Release Notes : http://goo.gl/FlH7C
Flash Player 11,0,0,58 Incubator 다운로드 : http://goo.gl/yXySu
Digging more into the Molehill APIs : http://goo.gl/d2WYu
Away3d 4.0 Alpha : http://goo.gl/bZBav
Alternativa 3D  : http://goo.gl/jqfE
Flare3D 2.0 Pre-release : http://goo.gl/1vgCA 
Flash CS5 환경에서 테스트 : http://blog.naver.com/q3korea/120125249427
Molehill Getting Start : http://blog.naver.com/q3korea/120126463667 
Molehill spinning cube : http://ltslashgt.com/2011/02/28/molehill-spinning-cube/
Unity, Flash & 3D on the web : http://goo.gl/OAY1q
 
글쓴이 : 지돌스타(http://blog.jidolstar.com/759
지난 Adobe MAX 2010에서 가장 이슈가 되었던 것중에 하나가 GPU 가속을 상시 지원해줄 수 있는 Molehill이라는 Flash Player 3D API 가 아닌가 싶다. Molehill은 Flash Player 3D APIs의 코드네임이다.

지금까지 Flash Player가 일부 GPU 가속을 지원해줬기 때문에 테스트 정도로만 가능했지만, 미래에 GPU를 상시 지원하게 되는 Molehill 3D API가 차세대 Flash Player에 장착하게 되면 파급효과가 클 것으로 생각된다. 

CPU에 의존해 왔던 3D 기존 프레임워크인 Away3D나 Alternativa3D등은 Molehill로 변환되어 배포될 것이고 많은 Flash 컨텐츠 제작사, 소셜게임제작사들이 새로운 Flash Player 3D API를 이용한 다양한 어플들을 내놓을 것이다. 더불어 이들 어플들은 고스란히 모바일에도 접목되게 될 것으로 예상이되어 파급효과가 클 것으로 전망이 된다.

지속적으로 Molehill에 대해서 관심을 가지고 지켜볼 필요가 있겠다.



위에 두 영상화면을 보고 Flash Player 에서 동작하는 모습이다라면 누가 믿겠는가? ㅎㅎ

읽을만한 글
Adobe Labs - 3D APIs for Adobe Flash Player and Adobe AIR
Adobe MAX 2010에서 소개된 차세대 GPU 가속 Flash API - Molehill
Molehill Flash Player 3D APIs 소개
Flash Player 3D APIs 간단한 소개글 - 잼있음
[동영상][at MAX 2010] GPU Acceleration on Adobe AIR "Molehill" - 우야꼬 군이 직접 미국에 MAX 행사에가서 찍은 영상입니다.
[동영상][at MAX 2010] Alternativa 3D in Adobe MAX 2010 - 우야꼬 군이 직접 미국에서 Molehill을 체험했군요.
[동영상] Adobe MAX 2010 - Alternativa 3D 시연 - 땡굴이 님께서 Alternativa의 시연모습을 동영상으로 담았습니다.
Molehill Programming Tutorial
ActionScript의 언어 순위는 몇 위일까?

글쓴이 : 지돌스타(http://blog.jidolstar.com/733)

Mac에서도 Flash Player가 H.264 비디오 디코딩 하드웨어 가속을 지원한다. 공개된 Flash Player는 코드네임이 "Gala"로 명칭되며 현재 Flash Player 다운로드 페이지에서 받을 수 있도록 되어 있다. 정식버전은 아니다.

현재 공개된 Flash Player에서는 Mac OS X 10.6.3 이상의 OS에 NVIDA GeForce 9400M, GeForce 320M, GeForce GT 330M등의 GPU 환경을 지원한다. 

Mac에서 H.264 비디오 코덱은 이미 Flash Player에서 지원하고 있었으나 하드웨어 가속은 아니였다. 그렇게 됨에 따라 많은 CPU 자원을 소비할 수 밖에 없었다. 이미 Flash Player 10.1 베타가 출시되면서 윈도우 PC나 x86기반 넷북에 설치된 Flash Player을 H.264 하드웨어 가속을 지원했다. 이제 "Gala"로 Mac 사용자도 Flash Player에서 H.264 비디오 디코딩시 하드웨어 가속을 된 것이다. 

하드웨어 가속을 지원한다는 것은 CPU를 통해 비디오를 디코딩하지 않고 GPU로 하기 때문에 훨씬 더 쾌적한 H.264 비디오를 감상할 수 있다는 것을 의미한다. 

이는 Open Screen Project의 일환으로 가능한 것이라 판단한다. GPU 가속을 하기 위해 그래픽 카드 제작회사인 Nvida와 Adobe가 이 프로젝트를 통해 Flash Player에 대한 하드웨어 가속을 지원받을 수 있게 된 것이다.
이는 Open Screen Project와는 전혀 무관한 일이였군요. 지금껏 Apple이 자신의 OS API를 열어주지 않아서였습니다. 결국 Adobe는 완전 피해자가 된 셈이네요. 역시 Apple이 플랫폼을 개방안한것을 Adobe에 덤탱이 씌운거라는...

앞으로 Open Screen Project를 통해 더욱 다양한 기기에서 이런 협력이 많이 일어날 것으로 보이며 Flash Player는 더욱 Cross Device 세계로 나갈 것으로 기대한다.

Flash Player "Gala" Preview Release : http://labs.adobe.com/technologies/flashplayer10/gala/
Adobe Labs - Flash Player 10.1 : http://labs.adobe.com/technologies/flashplayer10/

 
글쓴이 : 지돌스타(http://blog.jidolstar.com/683

+ Recent posts