Flash Player 10부터 3D를 구현할 수 있는 일련의 Native API 코드가 추가되었다. Papervision3D나 Away3D 처럼 멋진 3D 효과를 만들기에는 부족하긴 하지만 여러가지 시도는 해볼 수 있겠다.

 

Adobe에서 제공하는 Flash용 ActionScript 3.0 프로그래밍 가이드를 살펴보면 3차원(3D)에서 작업에 대한 내용이 있다. 이 문서만 보아도 3D 표현을 하는데 큰 도움을 받을 수 있다. 문서에는 몇가지 예제가 있는데 예제중에 UV맵핑에 올라온 예제를 가지고 몇가지 테스트 코드를 만들었다.

 

 

테스트에 참가할 나의 딸, 예진이의 사진이다. ^^

 

 

T값을 이용한 원근표현

 

UV 맵핑은 텍스쳐(Texture) 맵핑이다. 텍스쳐는 일반적인 2D 사진을 의미하며 이 사진을 삼각형의 형태로 쪼개서 여러가지 3D 표현을 할 수 있다. 사진가지고 깃발처럼 펄럭거리게 할 수 있고, 구의 형태로 만들수도 있는 것이다.

 

UV맵핑은 사진의 어느부분을 버텍스(Vertex, 꼭지점)으로 삼을것인가 지정한다. UV맵핑에서 U는 사진의 가로축을 말하며 V는 사진의 세로축을 의미한다. 가로축/세로축 범위는 0~1까지다. 그러므로 U값이 0이면 사진의 좌측맨끝부분을 의미하며 1이면 사진의 우측맨끝을 말한다. V값도 마찬가지로 0이면 사진의 위끝부분, 1이면 맨아래 부분을 의미한다. UV값은 3D 버텍스와 1:1 대응되도록 한다. 그래서 지정된 1개의 버텍스는 UV좌표에 지정된 사진의 좌표에 대응되어 맵핑이 이뤄지도록 하는 것이다. 이러한 맵핑을 할 수 있도록 하는 함수가 Graphics의 drawTriangle()이다. 이 함수의 1번째 인자가 버텍스값이고 3번째 인자가 UV데이타 값이다.

 

사진의 경우 이미지 그대로 화면에 표현하기 위해 삼각형이 최소 2개가 필요하다. 사진에서 표현되는 실제 버텍스는 4개이지만 각각 삼각형의 버텍스는 6개가 된다. 이중에 2개의 버텍스는 중복이 된다. 이 중복을 제거하기 위해 사용하는 것이 인덱스이다. 인덱스를 이용하면 삼각형을 그리기 위한 버텍스의 순서를 정의함으로서 중복된 버텍스를 없앤다. 이 값은 drawTriangle()의 2번째 인자값으로 지정할 수 있다.

 

여기서 사용한 T값은 버텍스의 위치가 3D 형태로 변하면서 사진도 이에 맞게 크기를 조절할 필요가 있기 때문에 사용하는 값이다. 이를 이용해 원근표현을 할 수 있게 된 것이다.

 

아래 코드를 보면서 UV데이타와 T값을 이용한 3D 표현법에 대한 기초를 공부할 수 있다.

 

(소스를 복사할때는 이 소스를 직접복사하지 말고 소스 위에 마우스를 올려 소스보기 아이콘이 뜰때 그것을 눌러 새창이 뜨면 소스를 긁어다가 사용하면 되겠다.)

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	import flash.utils.getTimer;
	
	/**
	 * 3D 회전. T값을 이용해 원근 적용 
	 * @author Yongho, Ji
	 * @since 2009.06.30
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WS509D19BB-239B-4489-B965-844DDA611AE7.html
	 */ 
	public class Baby3D extends Sprite
	{
		[Embed(source="mybaby.png")]
		private var ImageClass:Class;
		
		//평면 버택스 좌표와 t값 
		private var x1:Number = -100, y1:Number = -100, z1:Number =0, t1:Number = 0;
		private var x2:Number = 100, y2:Number = -100, z2:Number =0, t2:Number = 0;
		private var x3:Number = 100, y3:Number = 100, z3:Number =0, t3:Number = 0;
		private var x4:Number = -100, y4:Number = 100, z4:Number =0, t4:Number = 0;
		
		//초점거리 
		private var focalLength:Number = 200;
		
		//버텍스에 대한 인덱스 
		private var indices:Vector.<int>;
		
		private var container:Sprite;
		
		private var bitmapData:BitmapData;

		public function Baby3D()
		{
			super();
			
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			
			//1평면당 2개의 삼각형을 가진다. 
			indices = new Vector.<int>;
			indices.push( 0,1,3, 2,3,1 );
			
			container = new Sprite();
			container.x = 200;
			container.y = 200;
			addChild( container );
			
			var bitmap:Bitmap = new ImageClass();
			bitmapData = bitmap.bitmapData;
			this.addEventListener( Event.ENTER_FRAME, rotatePlane );	
		}
		
		private function rotatePlane( event:Event = null ):void
		{
			//버텍스 회전 
			var ticker:Number = getTimer() / 400;
			z2 = z3 = -(z1 = z4 = 100 * Math.sin( ticker ) );
			x2 = x3 = -(x1 = x4 = 100 * Math.cos( ticker ) );
			
			//t값 계산 
			t1 = focalLength / ( focalLength + z1 );
			t2 = focalLength / ( focalLength + z2 );
			t3 = focalLength / ( focalLength + z3 );
			t4 = focalLength / ( focalLength + z4 );
			
			//삼각형 버택스
			//t값을 이용해서 버텍스 좌표에 원근값을 주도록 한다.  
			var vertices:Vector.<Number> = new Vector.<Number>;
			vertices.push( 
					x1*t1,y1*t1, 
					x2*t2,y2*t2, 
					x3*t3,y3*t3, 
					x4*t4,y4*t4 
			);
			
			// UV 맵핑 
			// T값을 이용해  원근값이 적용된 맵핑이미지를 만들기 위해  
			var uvtData:Vector.<Number> = new Vector.<Number>;
			uvtData.push( 
					0,0,t1,
					1,0,t2,
					1,1,t3,
					0,1,t4
			);	
			
			//그리기 
			container.graphics.clear();
			container.graphics.lineStyle( 1, 0xff0000, 1.0 );
			container.graphics.beginBitmapFill( bitmapData );
			container.graphics.drawTriangles( vertices, indices, uvtData, TriangleCulling.NONE );
			container.graphics.endFill();							
		}
	}
}

 

 

위 코드는 Adobe에서 제공하는 예제를 약간 변형한 것이다. 실행결과는 다음과 같다.

 

소개한 코드는 그냥 실행하는데 그치는 것은 좋지 못하다. 값을 적절하게 변경해가면서 어떻게 변경되는가 알아보는 것도 좋은 학습법이라 생각한다.

 

 

DisplayObject의 Y축을 이용한 회전. 원근투영 이용

 

위 예제와 UV맵핑은 동일하지만 T값은 1로 고정한다. 그리고 Y축만 회전하고 원근표현은 T값 대신에 perspectiveProjection 속성을 이용한다.

 

T값을 1로 한다는 것은 T값을 가지고 원근표현을 하지 않겠다는 의미이다. 아래 예제를 보면 알겠지만 T값이 변하지 않기 때문에 EnterFrame 이벤트 발생시마다 그려주는 대신 생성자에서 한번만 그렸다.

 

원근투영(Perspective Projection)은 Flash Player 10에서 기본적으로 지원해준다. 가장 단순한 투영방식으로 하나의 소실점을 가지도록 하는 Linear한 투영방식이다. 만약 이 투영방식을 사용하지 않는다면 Matrix3D를 이용해 다른 방식의 원근투영방식을 적용해야 한다.

 

아래 코드에서 root.transform.perspectiveProjection.fieldOfView는 100으로 설정되어 있다. 이 값은 T값으로 원근표현할때와 비슷한 느낌을 주기 위한 값이다. fieldOfView 0~180까지 적용할 수 있다. 0은 말그대로 매우 먼곳에서 대상을 바라보는 것과 같다. 즉, 왜곡이 없는 원통투영이 된다. 하지만 180에 가까워 질 수록 대상과 가까이서 보는 느낌을 주게 된다. 한개의 소실점을 가지는 원근투영이 된다. 이 값을 적절하게 조절하면서 실행해보자 무슨 의미인지 알 수 있을 것이다.

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	
	/**
	 * 3D 회전. 원근 투영을 이용
	 * @author Yongho, Ji
	 * @since 2009.06.30
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WS36223081-8938-4b45-BB89-F1F8B1A52E4E.html
	 */ 
	public class Baby3D_2 extends Sprite
	{
		[Embed(source="mybaby.png")]
		private var ImageClass:Class;		
		
		private var container:Sprite;
				
		public function Baby3D_2()
		{
			super();
			
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			
			container = new Sprite();
			container.x = 200;
			container.y = 200;
			addChild( container );
			
			//원근투영의 FiendOfView 설정. 기본값은 55임 
			root.transform.perspectiveProjection.fieldOfView = 100;
			
			//비트맵 데이타 생성 얻어오기 
			var bitmap:Bitmap = new ImageClass();
			var bitmapData:BitmapData = bitmap.bitmapData;

			//버텍스 
			var vertices:Vector.<Number> = new Vector.<Number>;
			vertices.push( 
					-100,-100, 
					100,-100, 
					100,100, 
					-100,100 
			);
			
			//위 베텍스에 대한 인덱스 , 1평면당 2개의 삼각형을 가지고 있다. 
			var indices:Vector.<int> = new Vector.<int>;
			indices.push( 0,1,3, 2,3,1 );

			//UV 맵핑 데이타  
			var uvtData:Vector.<Number> = new Vector.<Number>;
			uvtData.push( 
					0,0,1,
					1,0,1,
					1,1,1,
					0,1,1
			);	
			
			//그리기 
			container.graphics.clear();
			container.graphics.lineStyle( 1, 0xff0000, 1.0 );
			container.graphics.beginBitmapFill( bitmapData );
			container.graphics.drawTriangles( vertices, indices, uvtData, TriangleCulling.NONE );
			container.graphics.endFill();				
				
			this.addEventListener( Event.ENTER_FRAME, rotatePlane );
		}

		private function rotatePlane( event:Event = null ):void
		{
			//컨테이너 회전 
			container.rotationY -= 5;
		}
	}
}

 

 

 

실행된 이미지가 조금 다르게 보일 것이다. T값을 이용한 원근표현 방식의 경우 렌더링을 계속하기 때문에 삼각형 선(빨간색)이 왜곡되지 않는다. 하지만 원근투영방식을 이용하면 한번 렌더링을 해주고 그것 자체를 회전시키는 방식이기 때문에 삼각형 선이 왜곡되어 보인다.

 

위 예제들은 쉬운 코드들이긴 하지만 UV맵핑, T값, 원근투영, 버텍스 등에 대한 이해가 없다면 어려울 수 밖에 없다. 3D 프로그래밍을 한번도 안한 분이라면 다른 서적이나 인터넷을 통해 학습자료를 찾아 공부하면 좋겠다는 생각이 든다.

 

Flash Player 10의 3D 기법은 아주 기초 수준이기 때문에 Papervision3D, Away3D처럼 화려한 3D를 만드는 것은 기대하기 어렵다. 하지만 Native수준에서 기능을 제공하므로 빠른 연산이 가능해서 간단한 3D 구현하겠다면 위 예제 처럼 만들자. 앞으로 Papervision3D, Away3D도 Flash Player 10 기반으로 제공한다고 하니깐 조금더 향상된 퍼포먼스를 가진 Flash 3D 애플리케이션을 기대해도 좋을 것 같다.

 

참고글

3차원(3D)에서 작업

 

+ Recent posts