3D 환경맵핑(Environment Mapping)의 한 기법으로 Cube Maps가 있다. 말그대로 직육면체의 각면에 대한 텍스쳐 이미지로 3D 환경을 조성하는 방법인데 아래 이미지를 보면 바로 이해할 수 있다.

 

Cube Map

출처 : developer.com

 

Cube Maps는 환경 맵핑 방법중 가장 빠르고 매우 사실적으로 묘사할 수 있는 방법으로 내가 알기로는 게임등에 자주 사용하는 것으로 알고  있다. 단점은 모서리 부분에서 약간 티가 난다는 점이다.

 

Flash Player 10부터 3D API를 제공한다. 이 API들을 적절히 활용하면 Papervision3D나 Away3D와 같은 라이브러리를 이용하지 않고도 어려움 없이 Cube Maps을 구현할 수 있다.

 

아래 프로그램은 Flash Player 10에서 제공하는 3D API만 이용해서 Cube Map을 구현한 것이다. 마우스 드래그로 회전이 가능하다.

 

마우스로 Drag하면 회전이 됩니다.
(Mac에서 화면이 작게 나오는데 아직 이유를 모르겠네요 ^^;)

 

위와 같은 프로그램을 만들기 위해 6면을 가지는 텍스쳐 이미지가 필요하다. 인터넷에 많이 있는데 아래처럼 Layout이 여러종류가 있다.

 

Cube Map Layout

Cube Map Layout 출처 : cgtextures.com

 

나는 가장 일반적이고 자주 쓰이는 Horizontal Cross방식 대신에 Horizontal Strip(NVidia DDS Exporter Layout)을 이용했다. 이 방식은 아래같이 구성된다.

 

Cube Map Layout 출처 : cgtextures.com

 

이러한 이미지가 어떻게 Cube Map을 만들 수 있는지 아래 화면을 보면 쉽게 알 수 있다.

 

마우스로 Drag하면 회전이 됩니다.

 

아래는 위 프로그램에 대한 소스이다.

 

 

package
{
	import flash.display.*;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.*;
	import flash.utils.getTimer;
	
	[SWF(frameRate=60, backgroundColor=0x000000)]

	/**
	 * Cube Map 예제 
	 * @author Yongho, Ji (http://blog.jidolstar.com/574)
	 * @since 2009.08.05
	 */
	public class CubeSky extends Sprite
	{
		[Embed(source="sky.jpg")]
		private const SKY:Class;
		
		//투영된 Vectex 정보
		private var projected:Vector.<Number>;
		
		//World 변환 행렬 
		private var world:Matrix3D;
		
		//투영을 위한 변환행렬 
		private var projection:Matrix3D;
		
		//Mesh 데이터 
		private var mesh:GraphicsTrianglePath
				
		//Texture
		private var texture:BitmapData = (new SKY as Bitmap).bitmapData;
		
		//Viewport (3D 렌더링 대상)
		private var viewport:Shape;
		
		//Viewport의 Z축 위치 
		private var viewPortZAxis:Number = 0;		
		
		public function CubeSky()
		{
			//Viewport 
			viewport = new Shape;
			addChild( viewport );
			
			//투영 변환 행렬 
			var p:PerspectiveProjection = new PerspectiveProjection()
			p.fieldOfView = 30;
			projection = p.toMatrix3D();

			//World 변환 행렬
			world = new Matrix3D();
			
			var check3D:Vector3D = Utils3D.projectVector(
										projection, 
										new Vector3D(1.0,0,1.0)
									);			
									
			//Mesh 데이타 
			mesh = createCubeMesh( check3D.x );
			projected = new Vector.<Number>(0,false);
			viewPortZAxis = check3D.x;// * 4;
			
			//이벤트 핸들러 등록 
			stage.addEventListener( Event.RESIZE, resize );
			stage.addEventListener( Event.ENTER_FRAME, render );		
			//stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );	
			stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseEvent );
			resize();
		}
		
		private function resize( event:Event = null ):void
		{
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;	
		}		
		
		private function render( event:Event = null ):void
		{
            world.identity(); //단위행렬로 전환 
			world.appendRotation( zRotation, Vector3D.Z_AXIS );
			world.appendRotation( 90+xRotation, Vector3D.X_AXIS );
            world.appendTranslation(0, 0, viewPortZAxis); //이동 
            world.append(projection); //투영 변환 적용 
            
            // mesh 데이터를  투영하여  projected 생성 
            // uvtData도 갱신된다. 갱신되는 데이터는 T값이다.             
            Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
  
            // texture를 이용해 렌더링
            viewport.graphics.clear();
            //viewport.graphics.lineStyle( 1, 0xff0000 );
        	viewport.graphics.beginBitmapFill( texture, null, false, true );
            viewport.graphics.drawTriangles( projected, mesh.indices, mesh.uvtData, mesh.culling );
        	//Cube는  z축을 굳이 정렬할 필요 없다.
            //viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );            	
		}
		
		/**
		 * 마우스 휠 처리 
		 */ 
		private function onMouseWheel( event:MouseEvent ):void
		{
			viewPortZAxis += event.delta * 10;
		}	

		private var xRotation:Number = 0;
		private var zRotation:Number = 0;
		private var prevX:Number;
		private var prevY:Number;		
		/**
		 * 마우스 이벤트 - x,y축 회전
		 */ 
		private function onMouseEvent( event:MouseEvent ):void
		{
			switch( event.type )
			{
				case MouseEvent.MOUSE_DOWN:
					stage.addEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.addEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_MOVE:
					if( event.buttonDown == false )
					{
						stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
						stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
						break;
					}
					var dx:Number = mouseX - prevX;
					var dy:Number = mouseY - prevY;
					zRotation += dx;
					xRotation += dy;
					if( xRotation > 90 ) xRotation = 90;
					if( xRotation < -90 ) xRotation = -90;
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_UP:
					stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					break;
			}	
		}
	}
}

import flash.display.GraphicsTrianglePath;
import flash.display.TriangleCulling;

/**
 * Cube Mesh 데이타 작성 
 * @param size 한변의 사이즈
 * @return mesh 데이터 
 */ 
function createCubeMesh( size:Number = 512 ):GraphicsTrianglePath
{
	var vertices:Vector.<Number> = new Vector.<Number>( 0, false );
	var indices:Vector.<int> = new Vector.<int>( 0, false );
	var uvtData:Vector.<Number> = new Vector.<Number>( 0, false );
	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.POSITIVE );
	var s:Number = size/2;
	var w:Number = size * 6;
	var u00:Number = 0;
	var u01:Number = (size-1)/w;
	var u10:Number = size/w;
	var u11:Number = (size*2-1)/w;
	var u20:Number = (size*2)/w;
	var u21:Number = (size*3-1)/w;
	var u30:Number = (size*3)/w;
	var u31:Number = (size*4-1)/w;
	var u40:Number = (size*4)/w;
	var u41:Number = (size*5-1)/w;
	var u50:Number = (size*5)/w;
	var u51:Number = (size*6-1)/w;
	
	mesh.vertices.push( 
		//Right		
		s,-s,-s,	//0
		s,-s,s,		//1
		s,s,s,		//2
		s,s,-s, 	//3

		//Left
		-s,s,-s, 	//4
		-s,s,s, 	//5
		-s,-s,s, 	//6
		-s,-s,-s, 	//7
		
	
		//Top
		-s,-s,s, 	//8
		-s,s,s, 	//9
		s,s,s, 		//10
		s,-s,s, 	//11
		
		//Bottom
		-s,s,-s, 	//12
		-s,-s,-s, 	//13
 		s,-s,-s, 	//14
		s,s,-s, 	//15
		
		//Front
		-s,-s,-s, 	//16
		-s,-s,s, 	//17
		s,-s,s, 	//18
		s,-s,-s, 	//19

		//Back
		s,s,-s, 	//20
		s,s,s, 		//21
		-s,s,s, 	//22
		-s,s,-s 	//23
	);
	
	
	mesh.indices.push( 
		0,1,2, 0,2,3, 	//Right
		4,5,6, 4,6,7,  	//Left
		8,9,10, 8,10,11, //Top
		12,13,14, 12,14,15, //Bottom
		16,17,18, 16,18,19, //Front
		20,21,22, 20,22,23 //Back
	 );
		
	mesh.uvtData.push( 
		//Right
		u00, 1, 1,
		u00, 0, 1,
		u01, 0, 1,
		u01, 1, 1,

		//Left
		u10, 1, 1,
		u10, 0, 1,
		u11, 0, 1,
		u11, 1, 1,

		//Top
		u20, 1, 1,
		u20, 0, 1,
		u21, 0, 1,
		u21, 1, 1,

		//Bottom
		u30, 1, 1,
		u30, 0, 1,
		u31, 0, 1,
		u31, 1, 1,

		//Front
		u40, 1, 1,
		u40, 0, 1,
		u41, 0, 1,
		u41, 1, 1,

		//Back
		u50, 1, 1,
		u50, 0, 1,
		u51, 0, 1,
		u51, 1, 1
	 );

	
	return mesh;
}

/**
 * GraphicsTrianglePath를 기반으로, Z축으로 sort된 인덱스를 돌려준다.
 * 이 작업을 해주어야 z축 깊이에 따라 Triangle이 제대로 그려진다. 
 * @param mesh 정보 
 * @return sort된 index 데이터 
 */
function getSortedIndices( mesh:GraphicsTrianglePath ):Vector.<int> 
{
    var triangles:Array = [];
    var length:uint = mesh.indices.length;
    
    //z축 sort를 위한 기반 제작 
    for ( var i:uint=0; i < length; i += 3 ) 
    {
        var i1:uint = mesh.indices[ i+0 ];
        var i2:uint = mesh.indices[ i+1 ];
        var i3:uint = mesh.indices[ i+2 ];
        var z:Number = Math.min( mesh.uvtData[i1 * 3 + 2], mesh.uvtData[i2 * 3 + 2], mesh.uvtData[i3 * 3 + 2] );
        if (z > 0) 
        { 
        	triangles.push({i1:i1, i2:i2, i3:i3, z:z}); 
        }
    }
    
    //z축으로 sort
    triangles = triangles.sortOn("z", Array.NUMERIC);
    
    //sort된 값을 이용해 Vector값 만듬 
    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;
}

 

 

아래 이미지는 위 프로그램에서 사용한 이미지이다.

 

위 프로그램에서는 Graphics.drawTriangle() 메소드를 이용했다. 하지만 이 함수를 사용하지 않고도 만들수 있다. 왜냐하면 Flash Player 10부터는 DisplayObject에 대해 x,y,z축 회전 및 이동 API가 추가되었기 때문이다. 그러면 위 프로그램 소스처럼 힘들게 vertex, index, uvt 데이타를 만들지 않아도 될 것이다.

 

내 생각에 Flash에서 3D 개발을 위해 Graphics.drawTriangle()와 DisplayObject의 3D API, Matrix3D 등을 서로 섞어가면서 만드는 것이 좋을 것 같다.

 

개발환경 : Flash Builder 4 Beta 1 (Flash CS4 에서도 개발할 수 있음.)

 

참고내용

 

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

 

이전에 [Flash 3D]우리 아가 회전시키기-UV맵핑,원근투영 이용 라는 제목으로 3D Texture 맵핑을 이용하는 기초적인 예제를 설명했다. 이 글은 이를 응용해 실제 3D 모양을 만들어 Texture를 입히는 예제 보여준다.

 

Adobe Flash Player 10이상 부터 3D 관련 API를 제공하고 있다. 그러므로 Papervision3D, Away3D와 같은 기존 3D라이브러리를 이용하지 않아도 충분히 3D 구현이 가능해졌다.

 

이번에도 예진이가 모델이 되어주었다. 3D Texture 모델 이쁜 공쥬~~ 훗~

 

하지만 모델의 운명은 가혹하다는....

 

아래는 예제 프로그램의 캡쳐화면이다.

 

원기둥과 원환체(도너츠 모양)를 Mesh 데이터로 삼았다.

프로그램 상에서 키보드 1을 누르면 원기둥으로 키보드 2를 누르면 원환체로 바뀐다.

 

원기둥 모양

 

원환체 모양

 

화면위에 마우스를 클릭하면 렌더링된 삼각형이 그려진다. 이것으로 Texture가 어떻게 입혀졌는지 한눈에 확인할 수 있다.

 

원기둥 모양

원환체

 

키보드 T를 누르면 Texture가  입혀지지 않고 아래처럼 형태만 보여준다.

 

마우스 휠을 이용하면 z축으로 확대/축소가 가능하다.

 

Flash Player 10 API 만으로도 이런 것을 만들 수 있다는데 어쩌면 감동을 받을지도.... 나 또한 그랬다.

 

다음은 위 예제 프로그램이다. Flash Builder 4나 Flash CS4에서 ActionScript 3.0 프로젝트를 생성해서 만들면 되겠다.

 

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.GraphicsTrianglePath;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Matrix3D;
	import flash.geom.PerspectiveProjection;
	import flash.geom.Utils3D;
	import flash.geom.Vector3D;
	import flash.utils.getTimer;
	
    [SWF(frameRate=60, backgroundColor=0x000000)]
	
	/**
	 * 3D Texture 예제.
	 * @author Yongho, Ji
	 * @since 2009.07.01
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
	 */ 
	public class Texture3D extends Sprite
	{
		[Embed(source="mybaby.png")]
		private var ImageClass:Class;
		
		// 투영된  Vertex 정보 
		private var projected:Vector.<Number>;

        // 투영
        private var projection:PerspectiveProjection = new PerspectiveProjection();
        
        // World 변환 행렬 
        private var world:Matrix3D = new Matrix3D();
		
		// Viewport (3D 렌더링 대상)
		private var viewport:Shape = new Shape();
		
		// Mesh 데이터 
		private var mesh:GraphicsTrianglePath
		
		// Texture
		private var texture:BitmapData = (new ImageClass() as Bitmap).bitmapData;
		
		//triangle을 보여줄지 여부  
		private var visibleTriangle:Boolean = false;
		
		//Texture를 보여줄지 여부 
		private var visibleTexture:Boolean = true;
		
		//실린더  Mesh번호 
		private const MESH_CYLINDER:int = 1;
		
		//원형체  Mesh번호  
		private const MESH_TORUS:int = 2;
		
		//선택한 Mesh 번호 
		private var selectedMesh:int;
		
		//Viewport의 Z축 위치 
		private var viewPortZAxis:Number = 300;
		
		/**
		 * 생성자 
		 */ 
		public function Texture3D()
		{
			super();
			
			//화면 설정 
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.BEST;
			
			//viewport를 화면의 중심으로 
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;
			addChild(viewport);
			
			//projection의 fieldOfView를  60으로 지정 
			projection.fieldOfView = 60;
			
			//mesh 데이터 설정 
			selectMesh( MESH_CYLINDER );
			
			//이벤트 처리 							
			stage.addEventListener( Event.RESIZE, onResize );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
			stage.addEventListener( MouseEvent.CLICK, onMouseClick );
			stage.addEventListener( KeyboardEvent.KEY_DOWN, onKey );
			stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );
		}
		
		/**
		 * Mesh를 선택한다.
		 */ 
		private function selectMesh( meshNumber:int ):void
		{
			if( selectedMesh == meshNumber ) return;
			selectedMesh = meshNumber;
			
			//투영결과 Vertex 데이타를 초기화시킨다. 
			//이것을 해야 Mesh데이터가 바뀔때마다 drawTriangle에서 에러 발생 안함 
			projected  = new Vector.<Number>(0, false);
			switch( selectedMesh )
			{
				//실린더 
				case MESH_CYLINDER:
					mesh = createCylinderMesh( 50, 100, 20, 5 );
					break;
				//원형체 
				case MESH_TORUS:
					mesh = createTorusMesh( 50, 25, 32, 16 );
					break;
			}			
		}
		
		/**
		 * 사이즈 변경시 처리  
		 */ 
		private function onResize( event:Event ):void
		{
			//viewport는 항상 화면의 중심에 위치하도록 처리 
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;	
		}
		
		/**
		 * 프레임 마다 처리. 
		 */ 
		private function onEnterFrame( event:Event ):void
		{
            world.identity(); //단위행렬로 전환 
            world.appendRotation( getTimer() * 0.027, Vector3D.X_AXIS ); //X축 회전
            world.appendRotation( getTimer() * 0.061, Vector3D.Y_AXIS ); //Y축 회전 
            world.appendTranslation(0, 0, viewPortZAxis); //이동 
            world.append(projection.toMatrix3D()); //투영 변환 적용 
            
            // mesh 데이터를  투영하여  projected 생성 
            // uvtData도 갱신된다. 갱신되는 데이터는 T값이다. 
            Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
            
            // texture를 이용해 렌더링
            viewport.graphics.clear();
            
            // Triangle 라인을 그림 
            if( visibleTriangle )
            {
            	viewport.graphics.lineStyle( 1, 0xff0000, 1.0 );
            }
            
            //Texture 입힌다.
            if( visibleTexture )
            {
            	viewport.graphics.beginBitmapFill( texture, null, false, true );
                viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );            	
            }
            else
            {
            	viewport.graphics.beginFill( 0x00ccff, 1.0 );
                viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), null, mesh.culling );            	
	            viewport.graphics.endFill();			
            }
		}
		
		/**
		 * 마우스 클릭 처리 
		 */ 
		private function onMouseClick( event:MouseEvent ):void
		{
			//삼각형  선 Visible 바꿈 
			visibleTriangle = !visibleTriangle;	
		}
		
		/**
		 * 키보드 이벤츠 처리  
		 */ 
		private function onKey( event:KeyboardEvent ):void
		{
			if( event.charCode == 116 )
			{
				visibleTexture = !visibleTexture;	
			}
			else
			{
				selectMesh( event.charCode - 48 ); //1,2...
			}
		}
		
		/**
		 * 마우스 휠 처리 
		 */ 
		private function onMouseWheel( event:MouseEvent ):void
		{
			viewPortZAxis += event.delta * 10;
		}
	}
}

import flash.display.GraphicsTrianglePath;
import flash.display.TriangleCulling;

/**
 * GraphicsTrianglePath를 기반으로, Z축으로 sort된 인덱스를 돌려준다.
 * 이 작업을 해주어야 z축 깊이에 따라 Triangle이 제대로 그려진다. 
 * @param mesh 정보 
 * @return sort된 index 데이터 
 */
function getSortedIndices( mesh:GraphicsTrianglePath ):Vector.<int> 
{
    var triangles:Array = [];
    var length:uint = mesh.indices.length;
    
    //z축 sort를 위한 기반 제작 
    for ( var i:uint=0; i < length; i += 3 ) 
    {
        var i1:uint = mesh.indices[ i+0 ];
        var i2:uint = mesh.indices[ i+1 ];
        var i3:uint = mesh.indices[ i+2 ];
        var z:Number = Math.min( mesh.uvtData[i1 * 3 + 2], mesh.uvtData[i2 * 3 + 2], mesh.uvtData[i3 * 3 + 2]);
        if (z > 0) 
        { 
        	triangles.push({i1:i1, i2:i2, i3:i3, z:z}); 
        }
    }
    
    //z축으로 sort
    triangles = triangles.sortOn("z", Array.NUMERIC);
    
    //sort된 값을 이용해 Vector값 만듬 
    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;
}

/**
 * 원통모양의 Mesh 데이터 작성 
 * @param radius 원통의 반지름
 * @param height 원통의 높이 
 * @param hDiv 반지름 방향으로 조각 수 
 * @param vDiv 높이 방향의 조각수 
 * @return mesh 데이터 
 */ 
function createCylinderMesh( radius:Number, height:Number, hDiv:uint, vDiv:uint ):GraphicsTrianglePath
{
	var vertices:Vector.<Number> = new Vector.<Number>( 0, false );
	var indices:Vector.<int> = new Vector.<int>( 0, false );
	var uvtData:Vector.<Number> = new Vector.<Number>( 0, false );
	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.NONE );
	
	for( var i:uint = 0; i <= hDiv; i++ )
	{
		var theta:Number = Math.PI * 2 * i / hDiv; //z축에서 y축으로 잰각 
		for( var j:uint = 0; j <= vDiv; j++ )
		{
			var x:Number = -height/2 + j * height/vDiv;
			var y:Number = radius * Math.sin( theta );
			var z:Number = radius * Math.cos( theta );
			mesh.vertices.push( x, y, z );				//하나의 Vertex 데이타 
			mesh.uvtData.push( i / hDiv, j / vDiv, 1 ); //하나의 UVT 데이타 . Texture의 점과 Vectex를 일치시켜 Texture를 입히기 위한 데이타이다.
			if( j < vDiv && i < hDiv )
			{
                var a:uint =  i      * (vDiv + 1) + j;
                var b:uint = (i + 1) * (vDiv + 1) + j;
                mesh.indices.push(a, a + 1, b, b + 1, b, a + 1); //삼각형의 index이다. culling방향 고려 
			} 
		}
	}
	return mesh;
}

/**
 * 원환체(도너츠모양) Mesh 데이터 작성 
 * @param hRadius 원환체의 수평축 반지름
 * @param vRadius 원환체의 수직축 반지름  
 * @param hDiv 수평 방향의 조각 수 
 * @param vDiv 높이 방향의 조각수 
 * @return mesh 데이터 
 */
function createTorusMesh( hRadius:Number, vRadius:Number, hDiv:uint, vDiv:uint ):GraphicsTrianglePath 
{
	var vertices:Vector.<Number> = new Vector.<Number>( 0, false );
	var indices:Vector.<int> = new Vector.<int>( 0, false );
	var uvtData:Vector.<Number> = new Vector.<Number>( 0, false );
	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.NONE );
	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;
			var x:Number = Math.cos(s1) * r;
			var y:Number = Math.sin(s1) * r;
			var z:Number = Math.sin(s2) * vRadius;            
            mesh.vertices.push( x, y, z );	
            mesh.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;
                mesh.indices.push(b, a + 1, a, a + 1, b, b + 1);
            }
        }
    }
    return mesh;
}

 

아래는 위 프로그램을 실행한 것이다. Flash Player 10이 브라우져에 설치되어야 제대로 볼 수 있겠다.

 

 

키 1, 2 - 형태바꾸기

키 T(영문) - Texture 입히기 여부

마우스 클릭 - 삼각형 보이기 여부

마우스 휠 - 확대/축소

 

위 프로그램에서 사용된 GraphicsTrianglePath는 2D 공간에 3D 기하 도형 렌더링을 지원하기 위해 사용되는 클래스이다. 특별한 기능은 없지만 3D 데이터를 다룰 때 index, vertex, uvt 등의 데이타를 모두 담을 수 있기 때문에 사용하면 유용하다.

 

위 프로그램에서 원뿔, 구에 대한 mesh 함수를 추가하면 더 좋은 예제가 될 것이라 생각한다. 또한 광원(light)처리까지 곁들이면 금상첨화일 것이다.

 

 

참고글

 

 

+ Recent posts