네오위즈 공채 포트폴리오 ppt 자료

Posted by 시경이 Neowiz Project : 2010/06/07 23:53

길찾기 알고리즘

Posted by 시경이 Neowiz Project : 2008/10/02 17:39
길찾기 알고리즘에 대한 자료들이다.


Site : http://memolog.blog.naver.com/pkhuman/55

Picking(피킹)

Posted by 시경이 Neowiz Project : 2008/07/23 01:19

피킹( Picking ) 은 마우스 등으로 화면에 특정 위치를 지정 하면 그 위치에 있는

객체를 선택할수 있게끔 하는 기법이다.


예를 들면 전략 시뮬레이션에서 마우스를 이용해 유닛을 클릭하면 유닛이

활성화 되는 방식이 아마도 이 기법을 이용할것으로 예상된다.


2D 게임이야 상당히 간단하다. 마우스로 클릭한 지점, 즉 픽킹한 지점이 화면 좌표계이고

게임상의 객체( 선택이 될 ) 또한 이 화면 좌표계를 사용하므로 피킹한 지점이 캐릭터의

경계 범위( 보통 사각형을 사용 )안에 들어 있다면 선택됐다고 판별하면 되기 때문이다.


사실상 피킹은 또 다른 의미의 충돌 처리라고도 볼수 있다.

보통은 객체와 객체에 대해 충돌 처리가  진행돼지만 피킹의 경우 피킹한 지점( 점 또는 사각형 영역 )과 선택할 객체와의 충돌처리를 한다.


위에서 언급했다시피 2D에서의 피킹, 즉 선택한 지점과 선택될 객체와의 충돌 처리가 간단하지만

3D 에서 그리 간단하진 않다.

- 뭐 그렇다고 그렇게 복잡하지도 않으니 쫄지 말길 바란다.


3D 가 2D 보다 복잡하게되는 이유는 피킹하는 방법에서 과정이 하나 더 추가되기 때문이다.

그건 바로 좌표계의 일치화 이다.


마우스를 이용해 화면의 특정위치를 클릭하면 거기서 얻을수 있는 좌표는

화면( 윈도우 )좌표 기준의 위치이다.

근데 문제는 3D 객체의 기준 좌표는 보통 월드 좌표이다.

간단히 말해 두 좌표계가 같지 않다면 충돌처리 자체를 할수 없다는 것이다.


그럼 할일은 정해진 셈이다. 훗...

둘 중 하나를 한쪽의 기준 좌표계로 변환해주고 동일한 좌표계 상에서 충돌처리를 하면 된다.



개인적으로는 월드 좌표계 기준으로 정의된 3D 객체를 뷰포트( 윈도우 ) 기준의 좌표계로

변환하는 방법을 선택했다.

즉, 화면 좌표계 기준으로 피킹한 지점( 점 )과 3D 메쉬( 화면 좌표계 기준으로 변환된) 간에

충돌을 채크 할것이다.





그럼 피킹 하는 전체 과정을 몇가지 단계로 요약해 보겄다.



1. 로컬(모델) 좌표계 상에서 정의된 3D 객체의 정점들을 화면 좌표계 기준으로 변환한다.


2. 마우스의 위치를 얻는다.( 물론 화면 좌표계 기준으로 )  


3. 변환된 3D 객체의 화면 좌표계 상의 범위( 다각형들 )와 마우스 의 위치( 점 )를 비교해

  점이 다각형 안에 포함됐는지를 판별 한다.






이제부터 각 단계 별에 대해 친절한 설명 들어간다.



1. 3D 객체의 정점들을 화면 좌표계 기준으로 변환


이젠 지겨울때도된 렌더링 파이프라인( Rendering Pipe Line )얘기를 또 끄집어내야겄다.

혹시나 렌더링 파이프 라인에 대한 이해가 부족한 인간들은 이점을 명심하길 바란다.


렌더링 파이프라인만 완벽히 이해해도 3D Programming의 반은 먹고 들어간다.


P' = P * W * V * Proj


위의 식이 바로 로컬 좌표계 정점( P )가 투영 좌표계 기준( P' )되기까지의  

아름다운 여정인 렌더링 파이프라인 과정을 나타내는 것이다.


로컬 좌표계 기준으로 정의된 정점 PWorld, View, Projection 등의 여러 명의 손길을 거쳐

궁극적으로는 투영 좌표계 기준 정점 P'로 새롭게 거듭나는 것이다. 그냥 각각 곱하면 된다. 쩝.

- 정점이 화면에 나타나기 위해서는 하나의 과정을 더 거쳐야한다. 그건 나중에 설명 하겠다.


그럼 로컬 좌표계 기준으로 정의된 3D 객체를 화면 좌표계 기준으로 바꾼다고 할때

3D 객체의 각 정점들을 모두 P 에다 넣으면 궁극적으론 화면 좌표계 기준으로 변환된 위치값을

알수 있을것이다.



사실, 어차피 저 과정은  3D 객체( 로컬 좌표계 기준 )를 화면에 렌더링하기 위해 반드시

행해져야 돼는 일이다. Direct3D 에서는 다음과 같이 각각의 파이프라인 변환들을

다음과 같이 지정한다.


...

g_pd3dDevice->SetTransform( D3DTS_VIEW, matView );

...


위와 같은 방식으로 나머지 변환들에 대해서도 적절한 변환 행렬을 지정한 뒤에

DrawPrimitive...() 함수를 호출하면 자동적으로 정점들은 변환이 돼 화면에 출력돼게 된다.


단, 문제는 각 과정에서의 변환된 결과 값들을 우리는 알수 있는 방법이 없다.

다시 말해 우리는 변환된 정점의 위치 값을 알고 싶단 말이다!!!

왜냐면 마우스로 피킹된 지점이 3D객체에 포함됐는지 검사( 충돌 처리 )해야 하므로.


뭐 본인의 경우 특별한 방법은 모르겠고 해서 직접 행렬을 곱해버렸다.


MATRIXA16 matResult;                                        // 결과 행렬

matResult = g_matWorld * g_matView * g_matProj;  // 곱하는 순서대로 월드, 뷰, 투영 변환


평소에 이쁜짓(...)을 많이 하는 MS 가 행렬( MATRIXA16 )에게 연산자를

다중 정의( Operator OverLoading )해준 덕분에 구지 별도의 함수를 사용하지 않고

직접 곱할수 있다.

*한가지 주위 할점은 반드시 위처럼 곱해진 순서를 지켜야한다는 것이다.



자, 이제 변환 행렬은 완성 됐고 이제 남은건 로컬 좌표계 정점 P를 방금

구한 변환 행렬로 변환하는것이다.

마우스 스크룰이 귀찮은 인간들을 위해 아래에 한번 더 붙여넣었다.

 

P' = P * W * V * Proj


-> P' = P * matResult


안타깝게도 벡터( 정점 )과 행렬의 곱셈은 정의가 안된 관계로 바로 곱셈 연산자를 사용해 곱하면

안되고 벡터와 행렬을 곱해주는 전용 함수를 사용해야 한다.


D3DXVec3TransformCoord( &vertex, &vertex, &matResult );


그냥 인자로 주어진 벡터와 행렬을 곱해 결과( 벡터 )를 레퍼런스로 넘겨 준다.

드디어 변환된 정점( P' )의 위치값을 구했다.

그럼 이제 피킹 지점( 마우스 포인트 )과 비교할수 있는 준비가 된겄인가?


아까 잠깐 언급한대로 하나의 과정이 더 남았다.

바로 화면 좌표계 기준으로 정점을 한번 더 변환하는 것인데 왠일인지 렌더링

파이프 라인 과정에서는 빠져 있으므로 직접 이 과정을 구현해야 한다.

그러나 그 과정은 상당히 쉬우니 긴장하지 말길 바란다.


위에서 얻은 P'는 투영 좌표계로 기준으로 변환된 정점이다.

투영 변환은 직교 투영이던, 원근 투영이던간에 무조건 카메라 절두체를 ( -1,-1,-1 ) 과 ( 1, 1, 1 )

범위의 정 육면체로 만들어 버리는 과정이다. 절두체가 -1.0f 과 1.0f 범위를 갖게 돼니

그것에( 절두체 ) 포함된 모든 정점 또한 투영 변환뒤에는 당연히 -1.0f 와 1.0f 의 범위를 갖게 된다.


뭐 유도 할것도 없이 바로 공식 들어간다.



스크린 위치 좌표 = (( - 1.0f - 투영 좌표 ) / 2.0f ) * 윈도우의 길이


가령 윈도우 상의 X 좌표를 알고 싶다면 다음과 같이 구하면 된다.


ScreenPos.x =  ( -1.0f - vertex.x ) / ( - 2.0f ) * WindowWidth;


하여간 이렇게 해서 피킹 작업의 1 번 과정은 끝났다.




2. 마우스의 위치


다음으로 구할것은 마우스 포인트 위치를 화면 좌표계 기준으로 얻어오는 것이다.

사실 별로 할건 없고 API 함수를 통해 바로 받아올수 있다.



//---------------------------------------------------------------
// * 전역( 전체 화면 ) 좌표를 윈도우 기준의 좌표로 변환한다
//---------------------------------------------------------------
POINT pt;

RECT Rect;


GetCursorPos( &pt );                    // 마우스 커서의 화면 좌표를 얻는다.
GetWindowRect( g_hwnd,&Rect);   // 윈도우의 사이즈 및 위치를 얻어온다.


float DX = pt.x - Rect.left;               // 전체 화면 기준에서 윈도우 기준으로 바꾼다. 
float DY = pt.y - Rect.top;            


pt.x = DX;
pt.y = DY;



3. 점이 다각형 안에 포함됐는지를 판별


피킹 작업의 하이라이트 단계이다.


1 번 과정에서 로컬 좌표계로 정의된 3D 객체의 정점들을 화면 좌표계 기준( 2D )으로 바꿨으며

2 번 과정에서 마우스로 선택한 지점을 화면 좌표계 기준으로 얻어왔다.


이제 남은 일은 2 번에서 얻은 점( 마우스 포인트 )이 1 번에서 얻은 영역에 포함돼는

지를 판별하기만 하면 된다.


위 문제를 좀더 일반화 시키면 결국 점이 다각형에 포함됐는지 여부를 검사하는 것이다.

이것에 대한 해결책을 알고 싶다면 아래 링크를 따라가 보도록.


http://blog.naver.com/kzh8055/140053121675

ps) 위의 방법을 써도 되지만 교수님이 가르쳐주신 백터와의 내적을 사용해서도 구할 수 있다.



출 처 : http://blog.naver.com/kzh8055?Redirect=Log&logNo=140053041553

태그 : Picking, 피킹

평면과 3차원에서 직선의 방정식

Posted by 시경이 Neowiz Project : 2008/07/23 01:02
[1] 평면에서의 직선
평면에서의 직선의 표현은 일반적으로
(1) 기울기와 한 점으로 표현할 수가 있다.
기울기가 m이고 한 점 (x1,y1)을 지나는 직선은 y=m(x-x1)+y1 으로 표현한다.

(예) 기울기 2이고 점 (1,3)을 지나는 직선 : y=2(x-1)+3 = 2x+1

(2) 두 점이 주어질 때의 직선
두 점 (x1,y1),(x2,y2)를 지나는 직선 y = (y2-y1)/(x2-x1) *(x-x1)+y1 으로 표현한다.

(예) 두 점(1,2),(3,6)를 지나는 직선 y=(6-2)/(3-1) *(x-1)+2 = 2x


[2]공간에서의 직선
공간에서의 직선은 평면의 직선과 같이 기울기를 정의하지 않는다.
단, 방향을 정하여 준다. 이것이 기울기와 같은 의미를 가지게 된다.

(1) 한 점 (x1,y1,z1)을 지나고 벡터v=(a,b,c)와 평행한 직선
:(x-x1)/a = (y-y1)/b = (z-z1)/c 로 표현한다.
이 때 평행인 벡터를 이 직선의 '방향벡터'라고 한다.. 이것이 기울기의 역할이다.

(예) 점(1,2,3)을 지나고 벡터v=(2,1,3)에 평행한 직선은
(x-1)/2 = y-2 = (z-3)/3 이다.

(2) 두 점 (x1,y1,z1),(x2,y2,z2)를 지나는 직선은
이 때는 (x2-x1,y2-y1,z2-z1)을 방향벡터로 생각하고 구하면 된다.
따라서 직선은 (x-x1)/(x2-x1) = (y-y1)/(y2-y1) = (z-z1)/(z2-z1)

(예) 두 점 (1,2,3),(2,4,6)을 지나는 직선은
x-1 = (y-2)/2 = (z-3)/3 입니다.

======================================================================================
아우 쪼팔린다. 중학교때 배운거였는데~ 새롭네 새로워~ㅡㅡ;

Directx 3D 참고 블로그

Posted by 시경이 Neowiz Project : 2008/07/23 00:08
http://blog.naver.com/kzh8055?Redirect=Log&logNo=140053041553

Memory 에 관해서...

Posted by 시경이 Neowiz Project : 2008/07/21 20:20

논리적 메모리

1.        프로그램이 생성할 때부터 종료할 때까지 메모리에 상주하는 부분
A.       main() 함수와 함수코드(Function)
B.       전역변수(Global Variable)
C.       정적변수(Static Variable)
2.        프로그램 실행 도중에 생성 및 소멸되는 부분
A.       지역변수(Local Variable)
B.       매개변수(Parameter)
C.       Heap 메모리에 생성된 데이터
 
 
함수 또는 클래스
스택 영역(Stack)
지역변수, 매개변수와 같이 쓰고 지우는 일이 빈번한 데이터는 스택영역을 사용한다. 스레드 당 1개씩 생성되며, 기본 크기는 1MB이다. 용량이 작아서 이 용량을 초과할 경우 Stack Overflow라는 에러 메시지가 발생한다. 링킹시 옵션으로 /STACK:reserve [.commit] 형태로 지정을 하면, 그 내용이 실행파일(EXE) 초반부에 기록되고, 프로그램이 실행될 때 운영체제가 이를 참조하여 스택이 사용할 메모리 영역을 할당한다.
데이터영역(Data)
Static 변수
코드 내에서 static 키워드로 생성된 데이터로써, 프로그램 생성시 할당된다.
전역변수(Global)
함수 블록 내에 포함되지 않은 변수로서, 프로그램 생성시 할당된다.
동적할당(Heap)
힙 메모리 함수에 의해 생성되는 데이터로 개발자의 필요에 따라 할당, 해제 할 수 있다. 프로그램이 실행되면 기본적으로 1MB의 크기의 힙 메모리 영역을 할당한다.
코드 영역(Code)
함수 코드가 이 영역에 저장되며, 함수 코드는 프로그램이 실행될 때 변경되면 안 되므로 읽기 전용이다.
 


Windows
메모리 관리

32비트 윈도 운영체제에서 하나의 프로그램은 4GB의 메모리 영역을 가질 수 있다. 그러나 4GB를 모두 어플리케이션 마음대로 사용할 수 있는 것은 아니고, 상위 2GB만 프로그램이 사용할 수 있도록 하였고, 하위 2GB Windows가 실행된 프로그램을 관리하기 위한 코드가 적재된다. 위의 스택, 코드 등의 영역은 하위 2GB에 포함된다.

 

가상 메모리상태 (Virtual Memory Status)



1.
       
램에 맵핑된 상태, 하드 디스크에 맵핑된 상태

2.
       
사용이 예약되어 읽거나 쓸 수 있는 상태

3.
       
초기 상태
 
등급
내용
Committed
물리적 메모리에 맵핑된 상태의 메모리 영역이다. Commit 상태의 메모리 영역은 읽거나 쓸 수 있다. VirtualAlloc()을 통하여 Commit 상태로 변경할 수 있으며, Commit 상태를 해제하여 Reserved 또는 Commit 상태로 바꿀 수 있다. WIN16 API LocalAlloc()을 이용하여 Commit 상태로 변경할 수도 있다.
Reserved
특정 크기가 메모리 영역의 사용을 예약해 놓은 상태이다. , 현재는 사용하지 않지만 앞으로 필요하게 될 부분이므로 다른 함수에 의해서 메모리가 할당될 때, 이 부분은 사용하지 말라는 뜻이다. 이 상태에서는 읽거나 쓸 수 없다. 왜냐하면, 물리적 메모리와 맵핑되지 않은 상태이기 때문이다. 사용하기 위해서는 commit 상태로 되어야 한다. 이때 사용되는 함수가 VirtualAlloc() 이다. Reserved 상태를 해제하려면, VirtualFree() 를 사용한다. Reserved 영역을 해제하면 Free 상태가 되고, 프로그램의 다른 부분에 의해 자유롭게 사용이 가능하게 된다.
Free
최초 가상 메모리가 생성될 때, 모든 가상 메모리 영역은 Free 상태에 놓이게 되는데, 읽거나 쓸 수 없는 빈 영역이라고 생각하면 된다. 이 영역을 Reserved 또는 Commit 상태로 변경할 수 있다. 물론 물리적 메모리와는 맵핑 되지 않은 상태이다.
 


가상
메모리 접근 등급
(Memory Access Protection)



l
       
접근불가 PAGE_NOACCESS

l
       
읽기전용 PAGE_READONLY

l
       
읽기/쓰기 PAGE_READWRITE
 
등급
내용
PAGE_NOACCESS
접근이 금지된 상태
PAGE_READONLY
읽기 전용 상태. 중요한 데이터의 경우 덮어쓰기 등의 경우로 데이터를 잃어버리는 경우를 막기 위하여 사용된다.
PAGE_READWRITE
읽거나 쓸 수 있는 상태. 가장 일반적인 형태로 Commit된 메모리 페이지에 대하여 모든 권한을 부여한다.
 
Windows 운영체제는 메모리에 대하여 접근 제한 속성을 설정할 수 있도록 API를 제공한다. 이러한 내용들은 파일에 대한 접근 등급과도 유사하며, 커널 오브젝트의 특징인 보안 속성과도 유사하다. 속성을 설정할 때는 메모리를 할당할 때 사용하는 VirtualAlloc()을 사용하고 생성된 이후에 VirtualQuery()를 통해 할당된 메모리 영역의 속성을 확인할 수 있다.

 

32비트 윈도우 가상 메모리구조(4GB)



Windows
운영체제가 제공하는 4GB의 메모리 영역을 차지하는 스택, , 코드 영역은 매우 작은 공간에 불과하며, 이외에도 많은 영역들이 존재한다.
 
0x00000000
0x00000FFF
NULL 값 할당 영역(4KB)
Private
2GB
0x00001000
0x003FFFFF
도스 및 16비트 어플리케이션 영역(4KB)
0x00400000
0x7FFFFFFF
프로세스 영역(사용자 영역)
User-Mode(1.99GB)
0x80000000
0xC0000000
공유 메모리 영역(메모리 맵 파일 영역)
Shared Memory-Mapped File(1GB)
Shared
2GB
0xFFFFFFFF
커널 영역
Kernel-Mode(1GB)
Windows 98/Me 가상 메모리 구조
 
 
0x00000000
0x0000FFFF
0x00001000
NULL 값 할당 영역(4KB)
Private
2GB
0xBFFEFFFF
0xBFFF0000
0xBFFFFFFF
0xC0000000
프로세스 영역(사용자 영역)
User-Mode(1.99GB)
 
Off-Limit 영역(64KB)
0xFFFFFFFF
커널 영역
Kernel-Mode(2GB)
Shared
2GB
Windows 2000 가상 메모리 구조
 
Private: 어플리케이션만의 독립적인 사용영역.
Shared: 커널에 의해서 공유되어 사용되는 물리적 메모리 영역을 맵핑해 놓은 부분. , 여러 프로세스가 공유해서 쓰는 커널 부분 또는 메모리 맵 파일 부분의 물리적 메모리 영역을 맵핑시켜 놓은 부분이다.
 
1.        NULL 포인터 영역
이 영역의 값은 모두 0이고 변경 불가능이다. , 운영체제에 의해 NULL 값으로 이미 정해진 절대 접근 금지 영역으로서 이 영역을 읽거나 쓰려고 시도할 경우 Access Violation 에러를 발생시키고, 프로세스는 종료된다. 시스템의 안정성 확보를 위해서 Windows가 할당해 놓은 구간이다.
 
2.        16비트 영역(MS-DOS Windows 3.x 어플리케이션 영역)
MS-DOS
Windows 3.1 운영체제는 16비트 기반이다. Window95 이후의 버전은 32비트로 업그레이드 되었지만, 기존과의 호환성을 위하여 16비트 또는 도스용 어플리케이션 및 디바이스를 사용할 수 있도록 일정 영역을 할당하고 있는데, NT 계열에서는 사용하지 않고 클라이언트 버전인 Windows 95/98/Me에서 사용된다. 32비트 기반의 어플리케이션이 이 영역을 사용하려고 하면 Access Violation 에러를 발생시키고, 프로세스는 운영체제에 의해 종료된다. 32비트, 64비트 Windows 운영체제인 NT4.0 이후의 버전에는 존재하지 않는 영역이다.
 
3.        프로세스 영역(어플리케이션 영역)
응용프로그램이 사용하는 영역. 응용프로그램 코드가 상주한다. 스택, , 코드 영역 등이 여기에 해당한다. 가상메모리, 힙 메모리 할당 등이 모두 프로세스 영역에서 이루어진다.
 
4.        중간 경계 영역
어플리케이션 영역과 공유 메모리 영역 중간의 64KB 크기 메모리 영역을 정하였다. 이 영역은 Private영역과 Shared영역을 구분하기 위한 완충지대를 둔 것이다. NULL 포인터 영역과 같이 접근을 시도할 경우 Access Violation을 일으킨다.
 
5.        공유 메모리 영역(메모리 맵 파일 영역)
Windows98
에서 사용되는 영역으로 운영체제, 즉 시스템이 사용하는 데이터 중 프로세스들과 공유되는 데이터들이 저장되는 영역이다. 또한 메모리 맵 파일(Memory Mapped File)을 사용하여 하드 디스크의 대용량 스트림 데이터를 사용할 때 Windows에 의해 사용되며, 프로세스간의 통신에도 사용된다
.
Windows 2000
이후에는 이 영역이 사라지고, 커널 모드가 최대 2GB를 사용하도록 구분되어 있다. 다시 말하면, Windows2000에서는 더욱 프로세스의 독립성과 안정성을 강조하고 있다고 볼 수 있다.
 
6.        커널 영역
운영체제 시스템 코드가 로드 되는 부분이 커널 모드 영역이다. 예를 들면, 스레드 스케줄링, 메모리 관리, 파일 시스템 코드, 네트워크 관련 코드, 그 밖의 기타 디바이스 드라이버 등이 로드 된다. 이 영역은 시스템 내의 모든 프로세스에게 공유된 메모리이다. 이것은 물리적 메모리에 한 번 로드 되며, 모든 프로세스가 공통으로 사용한다는 뜻인데, 프로세스가 이 영역을 직접 접근하는 것은 막고 있다. API함수를 통하여 얻은 핸들을 통해서만 이 영역을 사용할 수 있다.

 

WIN32 메모리 관련 API 오브젝트

 
WIN32 Application (32비트 윈도우 프로그램)
 
 
 
Local, Global Memory API
CRT Memory Function
 
WIN32 SubSystem
 
Heap Memory API
WIN32 Mapped File API
Virtual Memory API
Windows Virtual Memory Manager
Kernel
 
1.        Local, Global Memory API : LocalAlloc(), GlobalAlloc() 등의win16 API
2.        Heap Memory API : HeapAlloc(), HeapFree() 등의 힙 메모리 관리API
3.        Virtual Memory API : Windows 메모리 관리의 기본인 가상 메모리API
4.        Memory Mapped File API : 메모리 맵 파일을 다루기 위한API
 
메모리 맵 파일 API를 제외하고 모든 API는 가상 메모리 API를 이용하고 있다. 상위의 힙 메모리, 로컬/글로벌 메모리 API는 결국에 가상 메모리 API를 사용하여 구현되었다는 것이다. Windows 운영체제가 가상 메모리 API를 기반으로 만들어졌으니 당연한 일일 것이다.
 
 
구분
내용
Virtual Memory
대용량 객체 또는 구조체 배열 관리에 용이
Heap Memory
작은 용량의 많은 데이터 관리에 용이
Memory Mapped File
파일과 같은 대용량 스트림(stream) 데이터, 시스템 내 프로세스간의 공유 데이터 관리에 용이
 
가상 메모리는 Windows 가 메모리를 관리하는 기본 원칙과 같은 것으로, 메모리 맵 파일을 제외한 모든 메모리 관련 API 또는 오브젝트들은 가상 메모리 관련 함수들을 이용하여 특정 용도에 맞도록 보다 쉽게 구현된 것들이다. 따라서, 결국에는 가상 메모리 API 함수를 통하여 메모리를 사용하거나 관리한다고 이해하면 된다. 각각의 구체적인 특징과 사용방법을 살펴보기로 하자.


 

가상 메모리(Virtual Memory)



가상 메모리 API를 사용하여 메모리를 사용할 때는 시스템에 따른 페이지 크기로만 가능하므로, 불과 몇 바이트의 작은 크기를 사용할 때는 효과적이지 못하다. 따라서, 대용량의 구조체 또는 객체의 배열을 사용하고자 할 때 사용된다. 배열 사용이 가능한 이유는 연속된 메모리 영역을 할당할 수 있기 때문이다.
 

virtualAlloc()



1.
       
페이지 단위로 일정 크기의 메모리 영역을 할당한다.

2.
       
Free 상태의 페이지를 Reserved 상태로 바꾼다.

3.
       
Reserved 상태의 페이지를 Committed 상태로 바꾼다.

4.
       
Free 상태의 페이지를 Reserved 또는 Committed 상태로 바꾼다.

5.
       
PAGE_READWRITE, PAGE_READONLY, PAGE_NOACCESS 등의 접근 속성을 가진다.
 
LPVOID VirtualAlloc(
LPVOID lpAddress,                    // Resion to reserve or commit
SIZE_T dwSize,                          // 메모리 영역의 크기
DWORD flAllocationType,           // 할당 유형 선택
DWORD flProtect                        // 접근 속성 선택
);
 

virtualFree()


1.
       
Commit된 페이지를 Decommit시켜, Reserve된 상태로 바꾼다
.
Decommit
로 인하여 맵핑된 물리적 메모리는 해제되며, 해당 물리적 메모리를 다른 프로세스가 사용 가능하도록 한다. Decommit라는 뜻은 페이지와 물리적 메모리와의 맵핑 상태를 해제하여 다른 프로세스 등이 사용할 수 있도록 한다는 뜻.

 

2.
       
Commit가 아닌 Reserve 또는 Free 상태의 메모리는 참조할 수 없다
.
만일 참조하려 한다면, Access Violation 예외를 일으킨다. 메모리를 직접적으로 참조하거나 사용하려면, Commit 상태를 유지해야 한다.
 
BOOL VirtualFree(
LPVOID lpAddress,                    // committed 페이지 영역의 시작 주소
DWORD dwSize,                         // 메모리 영역의 크기
DWORD dwFreeType                 // Free, Reserved 상태로의 변경 중 선택
}
 

기타 함수


1.
       
VirtualProtect / VirtualProtectEx : 메모리 페이지의 접근 등급을 변경하는API

2.
       
VirtualLock / VirtualUnlock : 특정 영역을 잠근다. 특정영역에 대한 사용을 중지 시키는API

3.
       
VirtualQuery / VirtualQueryEx : 특정 영역의 메모리 정보를 얻는 API. 메모리 영역의 접근 등급, 상태 등을 구조체 MEMORY_BASIC_INFORMATION 를 통하여 알 수 있다.


 

메모리



Windows
는 기본적으로 페이지 단위의 가상 메모리 관리 기법으로 메모리를 관리한다. 이러한 Windows의 가상 메모리 관리 방법과 동일한 수준의 관리를 개발자가 할 수 있도록 가상 메모리 API를 제공한다. 따라서, 페이지 단위의 메모리 할당, commit, 해제 등의 작업을 개발자가 해야 한다.
32바이트를 사용하기 위해 4098바이트 페이지 전체를 물리적 메모리에 맵핑하여 사용해야 하므로, 작은 크기의 데이터에 사용하는 것은 부적절하다.
또한 가상 메모리 API는 각각의 페이지들을 관리하기 위한 페이지 디렉토리(Page Directory) 라는 것이 있고, 이는 64KB 크기의 실제의 물리적 메모리를 소모한다.
작은 크기의 데이터를 사용하는데 있어서 가상 메모리의 이와 같은 단점을 보완한 힙 메모리 API를 제공하고 있다.
 
힙 메모리 할당은 힙 메모리 API를 지칭하기도 하고,
malloc()계열의 C runtime library,
WIN16 GlobalAlloc(), LocalAlloc(),
OLE2 IMalloc 인터페이스,
힙 메모리 API를 총괄해서 부르기도 한다.
다음은 힙 메모리 오브젝트를 이용한 방법.
 
힙 메모리는 커널 오브젝트인 힙 오브젝트를 이용한다. 힙 오브젝트는 크기가 작은 메모리 영역을 효율적으로 사용할 수 있도록 한다. 그리고 가상 메모리 API에서는 페이지 단위로의 메모리 영역을 할당하고 해제하는 등의 작업도 맡아서 한다.
 
1.        장점
A.       페이지 단위의 메모리 할당(Reserve, Commit) 작업이 불필요하다.
B.       힙 메모리 영역의 크기가 자동으로 증가한다.
 
2.        단점
A.       속도가 느리다
힙 메모리 영역에서의 메모리 할당과 해제 작업은 다중 스레드 환경에서 동기화로 이루어진다. 어느 한 시점에서 힙 메모리를 할당하는 작업은 하나의 스레드에게만 허용되는데, 동시에 이 작업을 요청한 다른 스레드는 대기하므로 속도가 느려진다.
B.       메모리 영역을 제어할 수 없다.
C.       다른 메모리 영역으로의 침범이 우려된다.
 
프로세스가 생성될 때, 운영체제는 프로세스에게 기본적으로 1MB의 힙 메모리 영역을 할당한다. new, malloc을 이용한 메모리 사용은 이 영역에서 이루어진다. GetProcessHeap() 으로 이미 생성된 기본 힙 메모리 오브젝트의 핸들을 리턴할 수 있다.
 

새로운 메모리 오브젝트 생성

 
HANDLE HeapCreate(
DWORD flOptions,                      // 힙 할당 옵션
SIZE_T dwInitialSize,                  // 최초 힙 메모리 영역 크기
SIZE_T dwMaximumSize            // 최대 힙 메모리 영역 크기
);
 

프로세스 기본 메모리 오브젝트 참조

 
HANDLE GetProcessHeap(VOID);
// HeapCreate 하지 않고 기본 힙 메모리 오브젝트 사용시
 

현재 생성된 다수의 메모리 오브젝트 참조

 
DWORD GetProcessHeaps(
DWORD NumberOfHeaps,           // ProcessHeaps로 얻을 힙 핸들의 최대개수
PHANDLE ProcessHeaps           // 힙 핸들 데이터 배열 포인터
);
 

메모리 영역 할당 사용

 
LPVOID HeapAlloc(
HANDLE hHeap,            // 힙 메모리 오브젝트 핸들
DWORD dwFlags,          // 메모리 할당에 적용될 옵션
SIZE_T dwBytes           // 할당할 메모리 영역의 크기
);
 

메모리 영역 해제 오브젝트 소멸

 
BOOL HeapFree(
HANDLE hHeap,            // 힙 메모리 오브젝트 핸들
DWORD dwFlags,          // 속성 설정
LPVOID lpMem              // 할당된 메모리 영역의 포인터
);
 
BOOL HeapDestroy(
  HANDLE hHeap             // 반환할 힙 메모리 오브젝트 핸들
);
 
포인터 배열, 즉 할당된 각각의 메모리 영역을 HeapFree() 로 모두 해제하고, 마지막으로 HeapDestroy() 로 해당 힙 메모리 오브젝트를 더 이상 사용하지 않을 것을 운영체제에게 알리고 있다 (CloseHandle() 을 사용하지 않고 HeapDestroy() 를 사용했다)
할당된 메모리 영역의 사용이 끝났으면 HeapFree() 로 메모리 영역을 해제하고, 전체 힙 메모리 영역에 대한 사용을 끝내려면 힙 메모리 오브젝트 핸들을 반환하면 된다
.

HeapReAlloc()    //
메모리 영역 또는 크기를 재할당
.

HeapSize()          //
생성된 힙 메모리 영역의 크기를 확인

 

메모리 파일(Memory Mapped File)




Memory Mapped File
이란 File Mapping Object에 의해 물리적 메모리에 맵핑된 상태의 파일을 말한다.
메모리 맵 파일을 이용하면 일반적인 파일 또는 파일의 일부분을 자신의 프로세스 메모리 영역에 맵핑하여 사용할 수 있으며, 맵핑된 메모리 파일의 일부 또는 전부를 뷰(View)라고 한다. 이 뷰를 참조하면 입출력 방식을 사용하지 않고도 메모리의 포인터 접근방식으로 파일을 접근할 수 있다.
디스크의 파일을 읽는 방법에는 입출력(Input/Output)을 이용하는 방법이 일반적이다. 일반적인 파일 입출력은 시스템이 수행하는 여러 가지 역할 가운데 비용이 큰 편에 속한다. 파일을 참조해야 하는 경우가 자주 발생하거나 파일의 용량 자체가 거대해서 복잡한 버퍼 등을 설계해야 할 필요가 있을 때 파일 입출력을 사용하는 것은 비효율적이다. 이에 반해 메모리 맵 파일을 생성하는 것은 물리적으로 적은 리소스를 소모하기 때문에 매우 거대한 파일을 적은 대가를 지불하고도 사용할 수 있다.
 
l        메모리 맵 파일의 용도 및 특징
A.       EXE 파일과 DLL 파일을 로드하여 프로세스를 실행할 때 시스템에 의해 사용한다.
B.       대용량의 가상 메모리 영역 할당 또는 대용량 파일의 처리에 적합하다.
C.       파일의 입출력 또는 버퍼 관리 등을 하지 않고 데이터 파일의 참조가 가능하다.
D.       동일한 메모리 맵 파일을 이용하여 프로세스간 데이터를 공유가 가능하다.
 

파일 맵핑 오브젝트 생성 – CreateFileMapping()

 
HANDLE CreateFileMapping(
HANDLE hFile,              // 파일 핸들
LPSECURITY_ATTRIBUTES lpAttributes,  // 보안 속성
DWORD flProtect,                       // 접근 속성
DWORD dwMaximumSizeHigh,    // 64비트 상위 DWORD 크기
DWORD dwMaximumSizeLow,    // 32비트 크기, 64비트 하위 DWORD 크기
LPCTSTR lpName                       // 문자열 이름
}
 

메모리 파일 참조(파일 생성) – MapViewOfFile()

 
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,                 // 파일 맵핑 오브젝트 핸들
DWORD dwDesiredAccess,                    // 접근 속성
DWORD dwFileOffsetHigh,
          // 맵핑이 시작되는 포인터의 offset High-Order DWORD
DWORD dwFileOffsetLow,
          // 맵핑이 시작되는 포인터의 offset Low-Order DWORD
SIZE_T dwNumberOfBytesToMap           // 맵핑할 바이트 수
)
 

파일 소멸 맵핑 오브젝트 변환 – UnmapViewOfFile,
 
CloseHandle


사용이 끝난 파일 뷰는 UnmapViewOfFile()을 이용하여 해제하여 메모리 맵 파일과 가상 메모리와의 맵핑 상태를 해제한다. 메모리 맵 파일을 더 이상 사용할 필요가 없을 때, 커널 오브젝트인 파일 맵핑 오브젝트를 CloseHandle()을 사용하여 핸들을 반환한다. 마지막으로 디스크의 파일을 사용한 경우에는 파일을 역시 CloseHandle()을 이용하여 운영체제에게 반환하면 모든 과정이 끝나게 된다.

 

Access Violation 방지 하기 위한 함수



특정 메모리 영역의 포인터를 안전하게 사용하는 몇 가지 WIN32 API 함수
 
 
내용
IsBadCodePtr
현재의 프로세스가 해당 주소가 가리키는 영역을 읽을 수 있는지의 여부를 확인한다.
IsBadReadPtr
현재의 프로세스가 해당 주소가 가리키는 영역을 읽을 수 있는지의 여부를 확인한다.
IsBadStringPtr
현재의 프로세스가 해당 주소가 가리키는 영역을 문자열로 읽을 수 있는지의 여부 확인 하며, 시작 포인터로부터 NULL이 나올 때까지 유효성을 검사한다.
IsBadWritePtr
현재의 프로세스가 해당 주소가 가리키는 영역에 기록할 수 있는지의 여부를 확인한다.
 
  
 
출처 : Visual C++.Net Programming Bible(삼양출판사)

Subversion 사용방법

Posted by 시경이 Neowiz Project : 2008/07/14 13:26
Subversion 사용 HOWTO

이재홍 http://www.pyrasis.com 2003.11.14 ~ 2007.11.28 버전 1.4.3


CVS의 단점들을 개선한 버전 관리 시스템인 Subversion을 이용하여 프로그램의 소스 코드를 관리하는 방법과 유닉스, 리눅스 및 Windows에서 Subversion을 설치해보고 사용하는 방법을 설명합니다.

목차

1 소프트웨어 버전 관리의 이해
1.1 버전 관리 시스템의 필요성
1.2 버전 관리 시스템의 종류
1.3 버전 관리 시스템의 용어들
1.4 저장소의 디렉토리 배치
2 Subversion
2.1 CVS와 비교한 Subversion의 장점들
2.2 설치 준비 작업
2.3 사용 할 각각의 파일들 구하기
3 설치하기
3.1 OpenSSL 컴파일과 설치
3.2 Berkeley DB 컴파일과 설치
3.3 Apache 컴파일과 설치
3.4 Subversion 컴파일과 설치
4 세부 설정
4.1 저장소 만들기
4.1.1 공동 작업을 위한 저장소 그룹 설정
4.2 Apache 설정
4.2.1 Apache에서 ID로 사용자 인증
4.3 svnserve를 사용한 서버
4.3.1 svnserve에서 ID로 사용자 인증
4.4 SSH + svnserve 서버
5 실제로 사용하기
5.1 에디터 설정
5.2 기본 디렉토리 만들기
5.3 Import
5.4 Checkout
5.5 Update
5.6 Commit
5.7 Log
5.8 Diff
5.9 Blame
5.10 lock
5.11 Add
5.12 Rename
5.13 Export
5.14 Branch와 Tag
5.14.1 Branch
5.14.1.1 Merge
5.14.2 Tag
5.15 Revert
5.16 백업 및 복구
5.16.1 Dump
5.16.2 Load
5.17 svnsync
6 Microsoft Windows에서 사용하기
6.1 설치 파일 구하기
6.2 설치
6.3 사용하기
7 운영체제별 전용 패키지
8 GUI 클라이언트 프로그램
8.1 TortoiseSVN
8.2 Ankhsvn
8.3 RapidSVN
9 웹 인터페이스
9.1 ViewVC
9.2 WebSVN
태그 : Subversion, svn

프로그래머가 되는 방법

Posted by 시경이 Neowiz Project : 2008/07/14 06:58
How to be a Programmer: A Short, Comprehensive, and Personal Summary

프로그래머가 되는 방법: 짧고 폭넓고 개인적인 요약


목차
1 도입
2 초보자
2.1 개인적 기능들
2.1.1 디버그 배우기
2.1.2 문제 공간을 나눠서 디버그 하는 방법
2.1.3 오류를 제거하는 방법
2.1.4 로그를 이용해서 디버그 하는 방법
2.1.5 성능 문제를 이해하는 방법
2.1.6 성능 문제를 해결하는 방법
2.1.7 반복문을 최적화하는 방법
2.1.8 I/O 비용을 다루는 방법
2.1.9 메모리를 관리하는 방법
2.1.10 가끔씩 생기는 버그를 다루는 방법
2.1.11 설계 기능을 익히는 방법
2.1.12 실험을 수행하는 방법
2.2 팀의 기능들
2.2.1 시간 추정이 중요한 이유
2.2.2 프로그래밍 시간을 추정하는 방법
2.2.3 정보를 찾는 방법
2.2.4 사람들을 정보의 원천으로 활용하는 방법
2.2.5 현명하게 문서화하는 방법
2.2.6 형편없는 코드를 가지고 작업하기
2.2.7 소스 코드 제어 시스템을 이용하는 방법
2.2.8 단위별 검사를 하는 방법
2.2.9 막힐 때는 잠깐 쉬어라
2.2.10 집에 갈 시간을 인지하는 방법
2.2.11 까다로운 사람들과 상대하는 방법
3 중급자
3.1 개인적 기능들
3.1.1 의욕을 계속 유지하는 방법
3.1.2 널리 신뢰받는 방법
3.1.3 시간과 공간 사이에서 균형을 잡는 방법
3.1.4 압박 검사를 하는 방법
3.1.5 간결성과 추상성의 균형을 잡는 방법
3.1.6 새로운 기능을 배우는 방법
3.1.7 타자 연습
3.1.8 통합 검사를 하는 방법
3.1.9 의사소통을 위한 용어들
3.2 팀의 기능들
3.2.1 개발 시간을 관리하는 방법
3.2.2 타사 소프트웨어의 위험 부담을 관리하는 방법
3.2.3 컨설턴트를 관리하는 방법
3.2.4 딱 적당하게 회의하는 방법
3.2.5 무리 없이 정직하게 반대 의견을 내는 방법
3.3 판단 능력
3.3.1 개발 시간에 맞춰 품질을 조절하는 방법
3.3.2 소프트웨어 시스템의 의존성을 관리하는 방법
3.3.3 소프트웨어의 완성도를 판단하는 방법
3.3.4 구입과 개발 사이에서 결정하는 방법
3.3.5 전문가로 성장하는 방법
3.3.6 면접 대상자를 평가하는 방법
3.3.7 화려한 전산 과학을 적용할 때를 아는 방법
3.3.8 비기술자들과 이야기하는 방법
4 상급자
4.1 기술적 판단 능력
4.1.1 어려운 것과 불가능한 것을 구분하는 방법
4.1.2 내장 언어를 활용하는 방법
4.1.3 언어의 선택
4.2 현명하게 타협하기
4.2.1 작업 일정의 압박과 싸우는 방법
4.2.2 사용자를 이해하는 방법
4.2.3 진급하는 방법
4.3 팀을 위해 일하기
4.3.1 재능을 개발하는 방법
4.3.2 일할 과제를 선택하는 방법
4.3.3 팀 동료들이 최대한 능력을 발휘하게 하는 방법
4.3.4 문제를 나누는 방법
4.3.5 따분한 과제를 다루는 방법
4.3.6 프로젝트를 위한 지원을 얻는 방법
4.3.7 시스템이 자라게 하는 방법
4.3.8 대화를 잘 하는 방법
4.3.9 사람들에게 듣고 싶어 하지 않는 말을 하는 방법
4.3.10 관리상의 신화들을 다루는 방법
4.3.11 조직의 일시적 혼돈 상태를 다루는 방법
5 참고 문헌
5.1
5.2 웹 사이트
6 역사 (2003년 5월 현재) / History (As Of May, 2003)
6.1 피드백 및 확장 요청 / Request for Feedback or Extension
6.2 원본 / Original Version
6.3 원저자의 경력 / Original Author's Bio





Copyright © 2002, 2003 Robert L. Read

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with one Invariant Section being 'History (As of May, 2003)', no Front-Cover Texts, and one Back-Cover Text: 'The original version of this document was written by Robert L. Read without renumeration and dedicated to the programmers of Hire.com.' A copy of the license is included in the section entitled 'GNU Free Documentation License'.

사원수 이해하기

Posted by 시경이 Neowiz Project : 2008/07/14 06:40

처음에

저는 쿼터니언이라는 용어가 마치 비밀스러운 어둠의 힘을 가지고 있는 암흑물체에 관한 양자론 용어처럼 느껴집니다. 여러분도 역시 이 어둠의 힘에 매료되었다면 이 글이 도움이 될 것입니다(그렇게 되기를 바랍니다). 이 글은 쿼터니언을 이용해서 회전을 하는 방법을 보여주어 쿼터니언을 좀더 쉽게 이해하는데 도움을 줄 것입니다. 만약 여러분이 글에서 잘못된 내용을 발견하시면 robin@cyberversion.com로 메일을 보내주세요. 또, 이 기사를 여러분의 사이트에 실으려고 하신다면 저에게 메일을 보내주세요. 저는 저의 글이 어디에 퍼져있는지 알고 싶답니다.

왜 쿼터니언을 사용할까?

이 질문에 대답하기 위해서 우선 방향을 나타내는 일반적인 방법에 대해 논의해 보도록 합시다.

오일러 표현

이것이 현재까지 알려진 방법중 방향을 나타내는 가장 간단한 방법입니다. 오일러 표현은 각각의 축마다 축주위의 회전량을 가지고 있습니다. 따라서, 다음과 같은 0도에서 360도(혹은 0~2π)의 범위에서 변하는 3개의 값을 가집니다.

x, y, z <-- 전역 좌표축 주위의 회전 각도

이 값은 각각 롤, 피치, 요(혹은 피치, 롤, 요, 등 등)를 표현합니다. 방향은 3개의 각도로부터 생성되는 3개의 회전 행렬을 지정한 순서대로 곱해서 구할수 있습니다.

Note: 회전은 전역 좌표계의 좌표축을 기준으로 회전합니다. 즉 최초의 회전이 그 후의 2번째 3번째의 회전축에 영향을 주지 않습니다. 이 때문에 짐벌락(gimbal lock) 문제가 발생하게 됩니다. 짐벌락에 대해서는 조금 후에 자세히 설명하겠습니다.

회전축과 각도(Axis Angle)에 의한 표현

이 방법은 짐벌락을 피할수 있으므로 오일러각 방법보다 좀더 낫습니다. 회전축과 각도에 의한 표현은 임의의 회전축을 나타내는 단위 벡터와 단위 벡터 주위의 회전을 나타내는(0~360) 값으로 구성됩니다.

x, y, z <-- 임의의 축을 나타내는 단위 벡터 angle <-- 바로 윗줄에서 정의한 축 주위로 회전 각도

왜 이 방법이 나쁠까요?

짐벌락

오일러 표현에 있어서 회전은 전역 좌표계에서 일어나기 때문에 한 축의 회전이 다른 축의 회전과 겹치는(override) 문제가 발생합니다. 따라서 회전을 할 수 있는 축 하나를 잃게 됩니다. 이것을 짐벌락이라고 합니다.

예를 들어, X축과 평행한 어떤 벡터를 Y축주위로 회전해서 그 벡터가 Z축과 평행하게 되었다고 하면 이 때, Z축주위로 아무리 회전시켜도, 벡터의 방향은 전혀 변하지 않게 됩니다.

나중에 여러분에게 짐벌락의 예와 쿼터니언을 사용해서 짐벌락을 해결하는 방법을 보여드리겠습니다.

보간 문제

회전축 표현이 짐벌락 문제로 부터 자유롭긴 하지만 두개의 회전을 보간하는 경우, 또 다른 문제가 발생합니다. 보간계산을 마친 회전들이 부드럽지 못하여 회전 애니메이션이 삑사리가 날 수도 있습니다. 오일러 표현도 마찬가지로 이 문제를 가지고 있습니다.

자. 쿼터니언으로 빠져 봅시다 !

시작하기에 앞서, 몇가지 가정을 세우겠습니다. 저는 수학적인 이해가 중요시되는 글에서 수학적인 내용들을 대충 생략해버리는 글들에 진절머리가 납니다. 이런 글들은 독자들을 혼란에 빠뜨리기 쉽상입니다.

좌표계 - 이 글은 OpenGL과 같은 오른손 좌표계를 사용합니다. 만약 여러분이 Direct3D 같은 왼손 좌표계를 사용하고 계시다면 행렬들을 전치(transpose)하셔야 합니다. 이미 Direct3D의 샘플들은 쿼터니언 라이브리러를 가지고 있다는 사실에 유념해주시기 바랍니다. 그럼에도 불구하고 저는 여러분이 그 라이브러리를 사용하시기 전에 그 내부가 어떻게 구성되는지를 한번 짚고 넘어갔으면 하는 바램입니다.

회전순서 - 오일러 표현에서 회전 순서는 X->Y->Z의 순입니다. 행렬 형태로 아래와 같이 표현합니다.

RotX * RotY * RotZ <-- 매우 중요

행렬 - 행렬은 OpenGL 처럼 열우선 방식으로 합니다.

예 [ 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15 ]


벡터와 점 - 변환을 위해 벡터와 점은 4x1의 행렬로 표현합니다. 다음과 같은 모습입니다.

회전 행렬 * [ vx vy vz 1 ] <-- 4x1 벡터

저는 특히 Direct3D보다 OpenGL을 선호하지는 않습니다. 그저 제가 OpenGL을 먼저 배웠고, 쿼터니언도 OpenGL을 통해 익혔을 뿐입니다.

Note: 만약 X->Y->Z 순서가 아닌 다른 회전 순서를 지정하면 몇몇 쿼터니언의 함수들을 다시 구현해야 합니다. 특히 오일러 표현을 다루는 함수들이 그렇습니다.

쿼터니언이란 무엇인가?

복소수는 i라는 기호를 사용하여 정의하는 허수(가상의 수)입니다. i는 i * i = -1 라는 성질을 가지고 있습니다.

쿼터니언은 복소수의 확장입니다. i만 사용하는 것이 아니라 제곱근이 ―1 이 되는 3개의 허수를 가집니다. 이 3개의 수는 보통 i, j, k로 표기합니다. 다시 말해 이것은 다음과 같은 성질을 가집니다.

j * j = -1 k * k = -1

따라서 쿼터니언을 아래와 같이 표현할 수 있습니다.

q = w + xi + yj + zk

여기서 w는 실수, x, y, z는 복소수입니다.

흔히 사용하는 또 다른 표현은 아래처럼 벡터형태로 표현할수 있습니다.

q = [ w, v ]

여기서 v = (x, y, z)는 "벡터"라고 말하며 w는 "스칼라"입니다.

v를 벡터라고 부르지만 이것은 일반적인 3차원 벡터가 아닌 4 차원 공간상에 벡터를 표현한 것으로 직관적으로 시각화할 수 없습니다.

항등 쿼터니언

벡터와 다르게 2개의 항등 쿼터니언이 있습니다.

곱 항등 쿼터니언은 아래 처럼 표현합니다.

q = [1, (0, 0, 0)]

그래서 이 곱 항등 쿼터니언과 곱해진 어떤 쿼터니언도 변하지 않습니다.

가산 항등 쿼터니언은 (저희는 사용하지 않습니다)은 아래처럼 표현합니다.

q = [0, (0, 0, 0)]

방향 표현으로 쿼터니언 사용하기

우선 저는 쿼터니언이 벡터가 아니라는 사실을 말씀드리고 싶습니다. 따라서 여기서 벡터 수학을 사용하지 말아주십시요.

이 부분은 매우 수학적인 내용을 다룰 것입니다. 참을성을 가지고 제 설명을 읽어주세요.

우선 쿼터니언의 크기를 정의합니다.

|| q || = Norm(q) = sqrt(w2 + x2 + y2 + z2)

단위 쿼터니언은 아래와 같은 속성을 가지고 있습니다.

w2 + x2 + y2 + z2 = 1

그 때문에 쿼터니언의 정규화는 아래와 같이 구합니다

q = q / || q || = q / sqrt(w2 + x2 + y2 + z2)

이 단위 쿼터니언은 특별합니다. 왜냐하면 단위 쿼터니언은 3D 공간에서 방향을 표현할 수 있기 때문입니다. 따라서 앞에서 논의된 두가지 방법 대신에 단위 쿼터니언을 통해 방향을 표현할 수 있습니다. 단위 쿼터니언으로 방향을 표현하려면 단위 쿼터니언을 다른 표현(예: 행렬)으로 변환하거나 반대로 변환하는 방법이 필요합니다. 이것에 대해서는 곧 설명드리겠습니다.

단위 쿼터니언 시각화 하기

단위 쿼터니언은 (x, y, z) 요소를 임의의 축, w요소를 회전 각도로 하는 4차원 공간상의 회전으로서 시각화할 수 있습니다. 모든 단위 쿼터니언들은 4D 공간에서 단위 길이를 가지는 구를 형성하게 됩니다. 다시한번 이게 무슨 소리지 하고 여러분은 직관적으로 이해가 안되실것입니다. 하지만 제가 정말 말하고 싶은 것은 쿼터니언의 스칼라 요소(w)의 부호를 반전시키는 것만으로 180도 회전한 쿼터니언을 얻을 수 있다는 것입니다.

Note: 단위 쿼터니언만을 방향표현에 사용할 수 있습니다. 이 뒤에 논할 모든 내용은 모두 단위 쿼터니언을 사용한다고 가정합니다.

쿼터니언으로부터의 변환

효과적으로 쿼터니언을 사용하려면 결국 쿼터니언을 다른 표현으로 변환해야 할 필요가 있을 것입니다. 키 눌림을 쿼터니언으로 해석할 수는 없지 않습니까? 할수 있으신 분 계신가요? 글쎄여. 아직까지는 없는 듯 하죠?

쿼터니언에서 행렬로의 변환

OpenGL와 DirectX는 행렬로 회전을 표현하기 때문에 쿼터니언->행렬 변환이 아마 가장 중요한 변환 함수일 것입니다. 왜냐하면 동차 행렬이 3D의 기본 표현이기 떄문입니다.

쿼터니언 회전과 동등한 회전 행렬은 다음과 같습니다.

 

행렬 = [ w2 + x2 - y2 - z2      2xy - 2wz       2xz + 2wy             0

            2xy + 2wz          w2 - x2 + y2 - z2   2yz - 2wx             0

            2xz - 2wy                2yz + 2wx    w2 - x2 - y2 + z2      0

                  0                            0                   0         w2 - x2 - y2 + z2]

 

 

w2 + x2 + y2 + z2 = 1이 되는 단위 쿼터니언의 속성을 사용하면 위 식을 다음과 같이 간단하게 만들 수 있습니다.

 

행렬 = [ 1 - 2y2 - 2z2          2xy - 2wz         2xz + 2wy            0

            2xy + 2wz            1 - 2x2 - 2z2       2yz - 2wx            0

            2xz - 2wy              2yz + 2wx        1 - 2x2 - 2y2         0

                  0                          0                     0                   1 ]

쿼터니언으로부터 회전축과 각도에 의한 표현으로 변환

한 쿼터니언을 삼차원 공간에서의 임의축 주위의 회전축과 각도에 의한 표현으로 변환하는 방법은 다음과 같습니다.

회전축이 (ax, ay, az)이고 각도가 theta (라디안)이면 각도는 angle= 2 * acos(w)가 됩니다. 그 때 ax= x / scale ay= y / scale az= z / scale 이 됩니다. scale은 scale = sqrt (x2 + y2 + z2)입니다..

제가 알고 있는 다른 방법은 scale = sin(acos(w))을 사용하는 것입니다. 저 스스로 수학적으로 동치관계를 증명하려고 하지는 않았지만 두 방법 다 결과는 같을 것이라 생각합니다.

어쨌든, scale이 0이라면 회전이 없다는 뜻입니다. 그리고 여러분이 특별한 조치를 취하지 않는다면 회전축이 무한대가 됩니다. 따라서 scale이 0인 경우에는 언제나 회전각이 0인 임의의 단위벡터를 그 축에 설정하시면 됩니다.

간단한 예

제가 설명하려고 하는 것들에 대해서 뭐가 뭔지 모르겠다 하시는 독자분들을 위해 이제부터 간단한 예를 보여드리겠습니다.

우선 카메라 방향을 오일러 각으로 표현한다고 해봅시다. 그러면 렌더링 루프에서 다음과 같은 식을 이용하여 카메라를 위치시킵니다.

RotateX * RotateY * RotateZ * Translate

이 때 각각의 요소는 4x4 행렬입니다.

이제 단위 쿼터니언을 사용해서 카메라의 방향을 표현하려면 먼저 쿼터니언을 행렬로 변환해야 합니다. 그러면

(쿼터니언으로부터 변환한 회전 행렬) Rotate * Translate

같은 것이 생기게 됩니다

OpenGL에 특화한 예는 다음과 같게 될것입니다.

오일러 쿼터니언

glRotatef( angleX, 1, 0, 0)
glRotatef( angleY, 0, 1, 0)
glRotatef( angleZ, 0, 0, 1)
// 평행이동

// 오일러를 쿼터니언으로 변환
// 쿼터니언을 회전축과 각도로 변환
glRotate(theta, ax, ay, az)
// 평행이동

위의 표현은 모두 같습니다. 제가 말할려고 하는 것은 방향에 쿼터니언을 사용하는 것은 오일러나 회전축과 각도에 의한 표현과 완전히 같고, 앞서 언급한 변환 함수를 통해 상호교환이 가능하다라는 것입니다.

다만, 위의 쿼터니언에 의한 표현에는 오일러 표현법과 마찬가지로 짐벌락의 위험성이 있습니다.

역자주 – "왜 짐벌락 위험성이 있지"라는 답을 글내용상 알수가 없습니다.


물론, 아직은 회전을 쿼터니언으로 만드는 방법을 모르시겠지만 그것은 아래 부분에서 설명하겠습니다.

Note: Direct3D나 OpenGL를 사용하시는 독자분들은 API가 행렬 연결을 처리해주기 때문에 직접 행렬을 취급하는 일은 없겠지만, 이것에 대해서 알아둘 필요가 있습니다.

쿼터니언 곱

단위쿼터니언이 3D공간에서의 한 방향을 표현하기 때문에 2개의 단위 쿼터니언간의 곱은 2개의 단위 쿼터니언의 회전을 결합한 회전을 나타내는 단위 쿼터니언이 됩니다. 놀라운 일이지만, 사실입니다.

다음과 같은 2개의 쿼터니안이 있다고 가정해봅시다.

Q1=(w1, x1, y1, z1); Q2=(w2, x2, y2, z2);

이 2개의 단위 쿼터니언을 결합한 회전은 아래와 같이 구해집니다

Q1 * Q2 =( w1.w2 - v1.v2, w1.v2 + w2.v1 + v1*v2)

이 때 ,

v1 = (x1, y1, z1) v2 = (x2, y2, z2)

이 됩니다.

여기서 .와 *은 내적과 외적을 나타냅니다.

하지만 위 식을 아래와 같이 최적화할 수 있습니다.

w = w1w2 - x1x2 - y1y2 - z1z2 x = w1x2 + x1w2 + y1z2 - z1y2 y = w1y2 + y1w2 + z1x2 - x1z2 z = w1z2 + z1w2 + x1y2 - y1x2

물론, 여타 쿼터니언들과 마찬가지로 결과 단위 쿼터니언을 다른 회전 표현으로 변환하는 것도 가능합니다. 이런 점이 바로 쿼터니언의 진정한 미학입니다. 2개의 단위 쿼터니언은 구 상에 위치하기 때문에 4차원 공간에서 이들을 곱하는 방법은 짐벌락의 문제를 해결합니다.

여기서 곱하는 순서가 중요합니다. 쿼터니언의 곱은 교환칙이 성립되지 않습니다. 즉 이것은 아래의 식을 의미합니다.

q1 * q2 ≠ q2 * q1

Note: 2개의 쿼터니언은 동일한 좌표계 축을 참조해야 합니다. 저는 서로 다른 좌표계에서 2개의 쿼터니언을 합성하는 실수를 범한 적이 있고, 이 때 그 결과 쿼터니언이 특정 각도에서만 실패하는 이유를 알아내기 위해 많은 시간을 고민했습니다.

쿼티니언으로의 변환

이제 다른 표현법을 쿼터니언으로 변환하는 방법을 배워봅시다. 저는 샘플 프로그램에 있는 모든 변환들을 사용하지는 않지만 역운동학 등의 보다 진보된 용도로 쿼터니언 방향을 사용하려고 할 때 이것들이 필요할 것입니다.

회전축과 각도(Angle Axis)로부터 쿼터니언으로 변환

3D공간에서의 임의의 회전축을 도는 회전은 아래처럼 쿼터니언으로 변환됩니다.

회전축이 (ax, ay, az) 이고 (반드시 단위 벡터여야 함) 회전 각도를 theta (라디안)이면 w = cos(theta/2) x = ax * sin(theta/2) y = ay * sin(theta/2) z = az * sin(theta/2)이 됩니다.

우선 회전축이 정규화되어 있어야 합니다. 만약 정규화된 회전축 벡터의 길이가 0이라면 회전이 없는 것을 의미하므로 쿼터니언은 단위(identity) 쿼터니언으로 설정되어야 합니다.

오일러로부터 쿼터니언으로 변환

오일러로부터 쿼터니언으로의 변환은 회전순서가 올바르지 않으면 안 되기 때문에 조금더 힘듭니다. 임의축으로부터 3개의 좌표축을 분해하면 오일러 각을 3개의 독립적인 쿼터니언으로 변환해서 이 3개의 쿼터니언을 곱하면 최종 쿼터니언을 얻을 수 있습니다.

따라서 오일러 각 (a, b, c)으로 세개의 독립적인 쿼터니언을 만들수 있으며

Qx = [ cos(a/2), (sin(a/2), 0, 0)] Qy = [ cos(b/2), (0, sin(b/2), 0)] Qz = [ cos(c/2), (0, 0, sin(c/2))]

최종 쿼터니언은 Qx * Qy * Qz로 구할수 있습니다.

역자주 –이것은 오일러 회전 순서가 x->y->z의 순서인 경우에만 해당됩니다. 다른 순서로 된다면 Qx * Qy * Qz이 다르게 표현됩니다

데모 - 짐벌락 피하기

드디어 "어떻게 쿼터니언이 짐벌락을 피할수 있지?"에 대해서 모두가 기다렸던 궁금증에 대답할 때가 왔습니다.

기본적인 아이디어는

  1. 회전을 표현하는 데 쿼터니언을 사용한다.
  2. 현재 방향에서 새로운 방향으로 변경하기 위해 하나의 임시 쿼터니언을 생성한다.
  3. 임시 쿼터니언과 원래의 쿼터니언을 곱한다. 이렇게 하면 양쪽 모두의 회전을 합성한 새로운 방향이 얻어진다.
  4. 이 결과 쿼터니언을 행렬로 변환해서 평소처럼 행렬 곱을 사용한다.

우선, 저는 샘플 코드에 대해서는 어떠한 책임도 지지 않겠습니다. 이 코드는 난잡스러고 제대로 구성되어 있지도 않습니다. 이것은 쿼터니언 테스트할 때 제 프로그램에서 사용했던 쿼터니언 코드를 간략히 줄여놓은 버전일 뿐입니다. 따라서 코드에 대해서 크게 신경을 쓰지 않았다는 점만 기억해 주셨으면 좋겠습니다(돈받고 파는 코드가 아니니까요 ^^)

저는 2개의 실행 가능한 샘플을 준비했습니다. 첫번째 프로그램인 CameraEuler.exe (http://www.gamedev.net/reference/programming/features/qpowers/CameraEuler.zip)는 오일러 각을 사용해서 카메라를 구현한 예입니다.

여러분이 주의깊게 봐야 할 부분은 main.cpp의 Main_Lopp 함수입니다.

여러분이 while 문에서 눈여겨 봐야할 곳은 다음과 같습니다.

  1. X축, Y축, Z축 각각의 회전을, 3개의 각도로 보관 유지 부분
  2. 키를 누르면 관련된 회전량을 조정하는 부분
  3. while 문에서, 3 개의 오일러 각도를 회전 행렬로 변환해서 그것을 최종 변환 행렬에 곱하는 부분

위/아래 화살표 키는 X축의 회전, 왼쪽/오른쪽 화살표 키는 Y축회전, Insert/PageUp 키는 Z축회전을 담당합니다.

이 프로그램은, 짐벌락을 일으킵니다. 여러분이 짐벌락 현상을 보고 싶으면, 요를 90도로 취하고 X축이나 Z축회전을 시도해 보십시오. 그리고 무슨일이 일어나는지 살펴보세요.

지금부터는 쿼터니언으로 짐벌락 문제를 해결한 프로그램을 살펴봅시다. 이 프로그램은 CameraQuat.exe (http://www.gamedev.net/reference/programming/features/qpowers/CameraQuat.zip)이며 앞의 프로그램을 조금 변경한 프로그램입니다.

여러분이 while문에서 눈여겨봐야하는 곳은 다음과 같습니다.

  1. 카메라 방향을 쿼터니언으로 표현한 부분
  2. 키 입력으로부터 3개의 각도를 얻는 부분. 이 3개의 각도는 on/off 스윗치이며, 누적되지 않는 다는 것에 주의하십시오. 저는 while문에 이것들을 리셋했습니다. 물론 이것이 최선의 방법은 아닙니다. 그냥 신속히 처리하기 위해서 그랬을 뿐입니다.
  3. 3개의 각도를 임시 쿼터니언으로 변환하는 부분
  4. 임시 쿼터니언과 카메라 쿼터니언을 곱해서 합성한 방향을 얻는 부분. 곱 순서에 주의.
  5. 이렇게 해 얻은 카메라 회전을 최종 변환 행렬을 위해 회전축과 각도에 의한 표현으로 변환하는 부분.

키가 눌러졌을 때, 저는 키 입력에 대응하는 고유의 축에서의 작은 회전을 나타내는 임시 쿼터니언을 생성했습니다. 그리고 임시 쿼터니언과 카메라 쿼터니언을 곱했습니다. 이 4차원 공간에서의 회전을 합성한 것이 짐벌락을 피하게 해줍니다. 여러분이 직접 이것을 해보고 자신의 눈으로 확인해주세요.

카메라 쿼터니언은 최종 변환 행렬로 합성할 수 있도록 행렬이나 그에 상응하는 형태로 변환되야 합니다. 여러분이 쿼터니언을 사용할 때, 3차원 공간과 4차원 공간은 섞일 수가 없기 때문에 항상 이 작업을 해주셔야 합니다. OpenGL의 경우에 저는 그냥 쿼터니언으로부터 회전축을 변경하기만 했고 나머지는 API에게 위임했습니다.

제가 두번째 프로그램에서 오일러 각을 회전에 사용하지 않았지만 저는 여러분이 첫번째 프로그램에서의 오일러 회전에 대해서 볼 수 있게 하기 위해서 오일러 회전 부분을 남겨 두었습니다. 오일러 각은 하나 이상의 축으로 회전시키면 올바르게 되지 않을 것입니다. 왜냐하면 쿼터니언이 카메라 쿼터니언으로부터 오일러 각을 얻는 대신 키 입력을 통해서 계산하기 때문입니다. 두번째 프로그램은 프로그램을 시작했을 때 그냥 여러분이 요 값을 90도로 회전하려는 경우에 짐벌락이 더 이상 일어나지 않는다는 것을 보기 위해서 만든 참고자료일뿐입니다.

Note: 저는 여러분에게 저의 수학 라이브러리를 사용하라고 추천하고 싶지 않습니다. 여러분이 쿼터니언을 이해하고 나서 스스로 짜 보십시오. 참고로 저는 이 코드를 전부 버리고 다시 만들 예정입니다. 이 코드들은 저에게 있어서 너무나 지저분하고 난잡한 코드입니다.

제가 보여드리지 않은 것

여러분이 이미 눈치채셔겠지만 저는 쿼터니언을 오일러 각으로 변환하는 방법에 대해서는 설명하지 않았습니다. 그 이유는 아직까지도 완벽하게 동작하는 변환을 제가 알아내지 못했기 때문입니다. 제가 알고 있는 유일한 방법은 쿼터니언으로부터 행렬을 얻고, 그 행렬로부터 오일러 각을 추출하는 것입니다. 그러나, 오일러에서 행렬 변환은 다대일 관계(sin과 cos으로 인해)이기 때문에 저는 atan2를 사용해서 그 역을 구하는 방법을 알지 못합니다. 만약 정확하게 행렬로부터 오일러 각을 추출하는 방법을 알고 계시다면 저에게 알려주세요.

제가 보여드리지 않은 다른 내용은 행렬을 쿼터니언으로 변환하는 방법입니다. 여러분이 오일러 표현과 회전축과 각도에 의한 표현을 쿼터니언으로 변환할 때 행렬을 거치지 않고도 직접변환이 가능하므로 굳이 행렬을 쿼터니언으로 변환할 필요가 없기 때문입니다.

과제 - SLERP

여러분이 쿼터니언을 완전히 이해했다고 생각하신다면 다시 한번 생각해 보십시오. 아직도 쿼터니언에 대해서 더 배워야 할 게 남아 있습니다. 제가 전에 회전축과 각도(Axis Angle)에 의한 표현이 왜 나쁠까라고 말했던 것을 기억하시나요? '보간'이라는 단어가 갑자기 떠오르지 않나요?

저는 쿼터니언을 사용한 보간에 대해서 설명한 시간을 갖지 못했습니다. 이 글은 제가 예상한 것보다 시간이 오래 걸렸습니다. 여기서 SLERP(구면 선형 보간)에 대한 기본적인 아이디어를 드리겠습니다. 이것은 기본적으로 2개의 쿼터니언 방향 사이에 일련의 쿼터니언들을 생성합니다. 일련의 쿼터니언들은 처음과 마지막 쿼터니언사이에서 부드러운 모션(움직임)의 결과를 제공합니다. 오일러와 회전축 표현으로는 일관성있게 이러한 부드러운 움직임을 만들 수 없습니다.

마지막으로

저는 이 기사가 쿼터니언 이론 뒤에 숨겨진 알수 없는 미스테리들을 속시원하게 없애주었길 바랍니다. 다시한번 마지막으로 여러분에게 당부하고 싶은 말은 서로 다른 좌표계에서 2개의 쿼터니언을 곱하지 말아주십시오. 이렇게 하시면 여러분은 고통의 시간을 맛보시게 될것입니다.

그러면 새로 찾아낸 쿼터니언의 능력에 여러분이 기뻐하시길 바라면서 저는 이제 그만 여러분과 헤어질까 합니다. 몸조심하시구여. 그리구 다시 만나뵙기를 바라면서...




출처 : http://www.galexandria.com/

d3dx10d.dll 파일

Posted by 시경이 Neowiz Project : 2008/07/12 00:03
2005 에 DirectX 쓰다 보면 d3dx10d.dll 파일이 없다고 나온다.

환경설정이 의외로 손이 많이 가는데 처음부터 잘 되게 만들것이지...

은근히 짜증난다...일일히 다 설정해줘야 되고...;;


비밀번호 : www.icrcb.org.cn
태그 : d3dx10d.dll
 «이전 1 2  다음»