Search

[StartD2D-6] 이동/회전 변환 이해하기

DirectX 11 2011.09.21 08:00 Posted by 조진현


 

행렬의 등장

기존의 윈도우 애플리케이션 개발자가 Direct2D를 접할 때,
가장 생소한 개념은 행렬( Matrix ) 일 것입니다.

즉, 아래의 API 이겠죠?

<코드>

::g_ipRT->SetTransform( ::D2D1::Matrix3x2F::Identity() );

</코드>

우리는 항상 렌더링 작업을 하기 전에, 이 작업을 해주었습니다.
이것은 과연 무슨 의미를 가지고 있는 것일까요?  

불행하게도(?) Direct2D에서 각종 변환 작업을 위해서는 행렬( Matrix )을 사용합니다.
저는 고등학교 2학년 학기 초에 배웠던 기억이 있습니다.
그 때는 주로 연립 방정식의 해를 구하기 위해서 풀이 방법을 익혔습니다.
아마 대부분 저와 비슷하실 것이라 생각이 듭니다.

왜 뜬금없이 행렬이 등장했는지를 언급하고자 하면, 상당히 고통스럽습니다.^^
저는 이 행렬에 대한 깊은 이해를 원하지 않습니다.
제가 원하는 것은 이 행렬 관련 API를 이용해서 화면 상에 나오는 오브젝트들의
이동이나 회전 등과 같은 변환을 표현 할 수 있다는 것만 아셨으면 좋겠습니다.^^


Direct2D 에서는 3ⅹ2 행렬을 사용합니다.
행렬의 각 성분들은 아래와 같습니다.  

M11

Default : 1.0

M12

Default : 0.0

M21

Default : 0.0

M22

Default : 1.0

M31

OffsetX : 0.0

M32

OffsetY : 0.0

 

이 행렬은 D2D1_MATRIX_3X2 라는 구조체로 표현됩니다.
Direct2D에서는 이 행렬을 손쉽게 다루기 위해서 Matrix3x2F 라는 유틸리티 기능을 지원합니다.
우리는 주로 이 Matrix3x2F 의 기능을 이용해서 변환 정보를 설정할 것입니다.
행렬의 M11, M12, M21, M22 성분은 회전과 확대/축소와 관련된 수치가 입력됩니다.
M31과 M32에는 이동과 관련된 수치가 입력됩니다.
행렬의 수치를 통해서 우리가 원하는 변형을 하고자 할 때,
이들 수치를 직접 입력해서 행렬을 제어하기는 무척 어렵습니다.

그래서 Direct2D 에서 제공하는 개발자 편의를 위한 여러 클래스들을 사용해서 이들 수치를 제어합니다.

 

스크린 좌표계  

변환의 이해를 위해서는 좌표계에 대한 이해가 필수적입니다.
우리가 가장 많이 사용하는 좌표계는 당연히 2차원 좌표계입니다.
바로 모니터가 2차원이기 때문입니다.
하지만 이전에도 언급드린 적이 있지만,
컴퓨터 모니터의 좌표계는 우리가 일반적으로 알고 있는
2차원 좌표계가 아닙니다.

 

좌측이 우리가 흔히 학창시절에 배우는 2차원 데카르트 좌표계( 2D Cartesian coordinate system ) 입니다.
우측이
컴퓨터 모니터가 사용하는 스크린 좌표계입니다.
이점 숙지 하시고, 다음 내용을 보시기 바랍니다.^^

 

이번에 제공되는 샘플의 결과는 다음과 같습니다.

 

이동( Translate )

먼저 살펴 볼 것은 이동( Translate ) 입니다.
가장 위에 있는 이미지가 바로 이동만 적용된 형태입니다.
샘플에서는 단순히 ( X,Y ) 축으로 +20씩만 움직였습니다

<코드>
D2D1::Matrix3x2F matTranslation; 
matTranslation = ::D2D1::Matrix3x2F::Translation( 20.0f, 20.0f );
::g_ipRT->SetTransform( matTranslation );                    
::g_ipRT->DrawBitmap( ::g_ipD2DBitmap, dxArea );    
</코드 

D2D1::Matrix3x2F::Translation() 를 통해서 우리가 원하는 이동을 수행하고 있습니다.
쉽죠? 이런 식으로 하면 행렬에 대한 깊은 이해도 필요하지 않을 것이라 생각합니다.^^  


회전( Rotate ) 

두 번째는 회전입니다.
샘플에서는 두 번째 그림과 세 번째 그림이 바로 회전 변환에 대한 결과물입니다.
사실 회전은 행렬의 성분들을 굉장히 복잡한 수치로 변경시킵니다.
왜냐하면 이들 회전 수치들은 삼각함수 값들이기 때문입니다.

하지만, 두려워하지 마시기 바랍니다.
Direct2D 에서는 이를 편리하게 수행하기 위해
::D2D1::Matrix3x2F::Rotation() 를 제공하고 있습니다.
회전 시킬 때, API 에 입력되는 값이 회전량과 회전 중점입니다.
회전량의 경우에는 라디안 값이 아닌, 각도 값이 입력됨을 주의하시기 바랍니다. 
즉, Degree 값입니다.
그리고 이 회전량의 경우 음의 값과 양의 값을 모두 입력이 가능합니다.
음의 값이 입력이 되면 시계 반대 방향으로 회전을 수행하게 됩니다.
( 양수면 시계방향으로 회전 합니다. )

 

또한 회전 중점의 경우도 유의해야 하는데, 아래의 그림을 보시기 바랍니다.

 

좌측 그림의 경우에는 물체의 중점 위치를 기준으로 회전을 수행한 것입니다.
반면에, 우측 그림의 경우에는 물체의 좌측 상단을 기준으로 회전을 수행한 것입니다.
결과물이 다르죠?
이처럼 회전 변환을 할 때는 이 두 가지를 잘 고려해서 수행해야 합니다.  

<코드>
matRot = ::D2D1::Matrix3x2F::Rotation( -30.0f,
                                       D2D1::Point2F( 120.0f, 320.0f ) );
 

::g_ipRT->SetTransform( matRot );                           
::g_ipRT->DrawBitmap( ::g_ipD2DBitmap, dxArea );    
</코드>


이동과 회전의 결합 

지금까지 이동과 회전을 각각 수행해 보았습니다.
만약에 이동 작업과 회전 작업이 모두 한 오브젝트에 필요하다면 어떻게 해야 할까요?
그런 경우가 필요하다면 바로 행렬의 곱셈 작업을 적용합니다.
그러면 이 두 가지 작업을 모두 표현 할 수 있습니다.
그런데 이 때 주의 할 부분이 있습니다.
행렬은 교환 법칙이 성립하지 않습니다.
즉 행렬 A, B 가 있을 때, A * B != B * A 라는 것입니다.

<코드>
matTranslation = ::D2D1::Matrix3x2F::Translation( 20.0f, 220.0f );         matRot = ::D2D1::Matrix3x2F::Rotation( -30.0f,
                                       D2D1::Point2F( 120.0f, 320.0f ) );
matTM = matTranslation * matRot;            
::g_ipRT->SetTransform( matTM );
                          
::g_ipRT->DrawBitmap( ::g_ipD2DBitmap, dxArea );     

 
matTranslation = ::D2D1::Matrix3x2F::Translation( 300.0f, 220.0f );         matRot = ::D2D1::Matrix3x2F::Rotation( -30.0f, 
                                       D2D1::Point2F( 400.0f, 320.0f ) );

matTM = matRot * matTranslation;                        
::g_ipRT->SetTransform( matTM );
::g_ipRT->DrawBitmap( ::g_ipD2DBitmap, dxArea ); 
</코드>

 

이 코드가 우리의 샘플에서 두 번째와 세 번째 그림의 차이를 보여주는 것입니다.
두 번째 그림은 이동을 한 후에 회전 작업을 수행한 것이고,
세 번째 그림은 회전 작업을 수행한 후에 이동 작업을 수행한 것입니다.  

실제로 우리가 수행하는 모든 변환은 좌표계 변환입니다.
위에서 우리가 했던 변환은 모두 실제로 오브젝트들의 좌표계를 변환시켜서 얻은 결과물들입니다.
이 개념들은 분명히 이해하기 어려운 부분들입니다.
굳이 이렇게 어려운 부분까지 이해하실 필요는 없습니다.
샘플을 올려드리니, 수치를 바꿔보면서 여러 결과들을 눈으로 확인해 보시기 바랍니다.^^


 

 

 

신고

[StartD2D-5] Direct2D의 리소스 기본 개념.

DirectX 11 2011.07.11 08:30 Posted by 조진현

 

 

Direct2D 리소스( Resource ) 기본 개념

 

Direct2D 에서 리소스라는 것은 단지 메모리를 의미합니다.

그 메모리는 지오메트리(Geometry)나 비트맵 이미지가 될 수도 있습니다.

즉, 이들 리소스는 메모리들이 추상화 된 일종의 개념적인 분류들입니다.

 

이들 리소스를 처리해서 모니터에 최종적으로 보여주는 것이 Direct2D의 역할입니다.

이전 까지 사용되던 GDI 도 바로 이런 역할을 하는 것입니다.

 

하지만, GDI와 Direct2D 는 리소스 처리 방식이 완전히 다릅니다.

앞선 시간을 통해서, GDI는 CPU만을 활용한다고 꾸준히 언급했습니다.

 

반면에, Direct2D는 CPU와 GPU를 동시에 활용합니다.

CPU와 GPU를 동시에 활용하기 때문에,

리소스들도 이들에 맞게 추상적으로 재분류 되어야 합니다.

 

Direct2D는 이 리소스들을 크게 두 가지 형태로 분류합니다.

바로 디바이스 독립적인 리소스( device-independent resource )와

디바이스 의존적인 리소스( device-dependent resource )로 분류하고 있습니다.

이들 두 리소스들은 모두 ID2D1Resource 인터페이스를 상속받습니다.

 

 

디바이스 독립적인 리소스

 

디바이스 독립적인 리소스라고 하는 것은 CPU에 의해서 처리되는 리소스를 의미합니다.

단어 자체에서 볼 수 있듯이,

이들 리소스는 사용자의 그래픽 하드웨어와 상관없는 처리 결과를 보장합니다.

 

뒤에서 살펴보겠지만, 지오메트리를 표현하는 인터페이스들이 여기에 속합니다.

( 조금 더 정확히 나열하면, ID2D1DrawingStateBlock, ID2D1Factory, ID2D1Geometry,

ID2D1GeometrySink, ID2D1SimplifiedGeometrySink, ID2D1StrokeStyle 등이 여기에 속합니다. )

아래는 이런 디바이스 독립적인 리소스들의 계층구조를 보여주는 그림입니다.

 

 

 

 

 

디바이스 의존적인 리소스

 

반면에 디바이스 의존적인 리소스는 GPU에 의해서 처리되는 리소스를 의미합니다.

사용자들이 가지고 있는 그래픽 하드웨어들은 매우 다양하기 때문에, 그 기능들을 모두 동일한 방식으로 처리하는 것이 쉽지 않습니다.

( 각각의 하드웨어마다 성능이나 명령어들이 모두 차이가 나기 때문이겠죠..^^ )

즉, 이들은 사용자의 하드웨어들에 따라서 결과가 차이가 날 수 있습니다.

이 차이라는 것은 눈으로 확인 가능한 차이를 얘기하는 것이 아니라,

그래픽 하드웨어들이 생성하는 명령어들과 성능 등과 같은 차이를 얘기하는 것입니다.

 

우리가 보는 모니터는 그래픽 하드웨어들이 생성한 명령어들의 최종적인 결과입니다.

리소스들도 바로 이 그래픽 하드웨어의 영향을 받아서 결과가 생성되는 것입니다.

 

앞서 우리가 화면에 렌더링 하기 위해서 만들었던 렌더타겟이나 브러시, 이미지 같은 리소스들은 모두 여기에 속합니다.

아래의 그림은 디바이스에 의존적인 리소스들의 계층구조를 표현하는 그림입니다.

 

 

 

 

Direct2D는 리소스 처리 방식은 되도록이면 GPU를 활용합니다.

GPU 처리가 불가능하다면, 자동적으로 CPU로 처리하게 됩니다.

GPU를 활용하는 리소스라면, 더욱 높은 품질로 빠르게 처리할 수 있습니다.

 

이들 리소스에 대한 설명은 다음 시간부터 차근히 살펴보겠습니다.^^

 

신고