Programming/컴퓨터그래픽스 (DX 11)

[DirectX 11] 2D 변환 행렬

양양줘 2025. 6. 11. 17:57

📌 DirectX 2D 변환 행렬

D2D1::MATRIX_3X2_F : 2D 변환(이동, 회전, 스케일, 기울기)을 표현하는 3*2 행렬

// Direct X에서의 변환행렬 표현법
| M11  M12 |   -> 회전, 확대, 기울이기
| M21  M22 |   -> 회전, 확대, 기울이기
| dx   dy  |   -> 평행이동 (Translate)

// 수학적 변환행렬 표헌법 (참고)
| M11  M12  dx | -> x축 방향벡터, 평행이동
| M21  M22  dy | -> y축 방향벡터, 평행이동
  • 각 원소의 의미
원소 index 역할 의미
M11 [0][0] X축 방향의 X 성분 (ScaleX 등) X축 방향으로 얼마나 늘이거나 줄일지 (스케일 X) + 회전 시 영향을 줌
M12 [1][0] X축 방향의 Y 성분 (ShearY 등) 기울어지거나 회전 시 Y축에 미치는 영향
M21 [0][1] Y축 방향의 X 성분 (ShearX 등) 기울어지거나 회전 시 X축에 미치는 영향
M22 [1][1] Y축 방향의 Y 성분 (ScaleY 등) Y축 방향으로 얼마나 늘이거나 줄일지 (스케일 Y) + 회전 시 영향을 줌
dx [0][2] X축 이동 (Translate X) 도형을 오른쪽으로 얼마나 이동할지
dy [1][2] Y축 이동 (Translate Y) 도형을 아래로 얼마나 이동할지

 

📌 변환(Transform)

좌표 공간에서 점이나 도형의 위치, 방향, 크기 등을 수학적으로 바꾸는 과정으로, 행렬 연산을 통해 작업이 수행된다.

기본 변환으로 이동(Translation), 회전(Rotation), 크기조정(Scale)이 있다.

D2D1::Matrix3x2F::Translation(x, y)        // 좌표를 x,y 만큼 이동 (월드 기준)
D2D1::Matrix3x2F::Rotation(angle, center)  // angle : 회전각도, center : 회전 중심점
D2D1::Matrix3x2F::Scale(sx, sy, center)    // 확대 축소  sx/sy : x/y 방향 스케일 비율, center : 스케일 중심점

변환을 하기 위해서는 행렬연산이 필요한데, 당연히 우리가 직접 연산을 할 필요는 없고 해당 연산을 위한 변환 행렬을 준비하면 된다. 이마저도 직접 3*2 행렬을 만드는 것이 아닌 정적 함수를 통해 직관적인 매개변수 값만 전달하면 알아서 이에 맞는 변환 행렬을 생성해준다. 이 변환행렬을 활용하여 실제 변환 연산을 하는 방법을 아래에 적어보겠다.

 

 

1. 확대축소(Scale) : D2D1::Matrix3x2F::Scale(sx, sy, center)

  • scale x,y값은 1.0f가 기준으로 그 위는 확대, 그 이하는 축소이다.
  • D2D1_POINT_2F center = D2D1::Point2F(100.0f, 100.0f);
  • center는 월드 기준 좌표로 생략 가능하며 기준점은 (0,0) 왼쪽 위이다.
    // 변환 행렬
    [ sx    0  ]
    [ 0     sy ]
    [ cx - sx * cx   cy - sy * cy ]
    
    // 왼쪽 위 (0,0)을 기준으로 가로 2배, 세로 1.5배
    auto scaleMatrix = D2D1::Matrix3x2F::Scale(2.0f, 1.5f);
    renderTarget->SetTransform(scaleMatrix);
    
    // (100,100)을 기준으로 가로 2배, 세로 1.5배 확대
    D2D1_POINT_2F center = D2D1::Point2F(100.0f, 100.0f);
    auto scaleMatrix = D2D1::Matrix3x2F::Scale(2.0f, 1.5f, center);
    renderTarget->SetTransform(scaleMatrix);

 

2. 회전(Rotation) : D2D1::Matrix3x2F::Rotation(angle, center)

  • angle이 양수라면 반시계방향, 음수라면 시계방향으로 회전한다. (Direct는 x+가 오른쪽, y+가 아래방향이기 때문에)
  • center는 월드 기준 좌표로 생략 가능하며 기준점은 (0,0) 왼쪽 위이다.
    // 변환행렬
    [  cosθ   sinθ ]
    [ -sinθ   cosθ ]
    [ cx - cx * cosθ + cy * sinθ   cy - cx * sinθ - cy * cosθ ]
    
    // (0, 0)을 기준으로 45도 회전
    auto rotationMatrix = D2D1::Matrix3x2F::Rotation(45.0f);
    renderTarget->SetTransform(rotationMatrix );
    
    // (200, 150)을 중심으로 45도 회전
    D2D1_POINT_2F center = D2D1::Point2F(200.0f, 150.0f);
    auto rotationMatrix = D2D1::Matrix3x2F::Rotation(45.0f, center);
    renderTarget->SetTransform(rotationMatrix );

 

3. 이동(Transration) : D2D1::Matrix3x2F::Translation(x, y)

// 변환 행렬
[ 1  0 ]
[ 0  1 ]
[ x  y ]

// (50,30)만큼 이동
auto translationMatrix = D2D1::Matrix3x2F::Translation(50.0f, 30.0f);
renderTarget->SetTransform(translationMatrix );

  

 

4. 기울이기 (참고)

/// 기울이기 (참고)
// X축 기준으로 기울이기 (Y값에 따라 X좌표가 변함) — Shear X
float shearX = 1.0f; // 값이 클수록 더 기울어짐
D2D1::Matrix3x2F shearXMatrix = D2D1::Matrix3x2F(
    1.0f,        // M11: X 방향 스케일 (기본값 1)
    0.0f,        // M12: X축 방향의 Y 성분
    shearX,      // M21: Y 방향으로의 X 기울기 → **Shear X**
    1.0f,        // M22: Y 방향 스케일 (기본값 1)
    0.0f,        // dx : X 이동
    0.0f         // dy : Y 이동
);
pRenderTarget->SetTransform(shearXMatrix);

// Y축 기준으로 기울이기 (X값에 따라 Y좌표가 변함) — Shear Y
float shearY = 0.5f;
D2D1::Matrix3x2F shearYMatrix = D2D1::Matrix3x2F(
    1.0f,        // M11: X 방향 스케일
    shearY,      // M12: X 방향으로의 Y 기울기 → **Shear Y**
    0.0f,        // M21: Y 방향으로의 X 성분
    1.0f,        // M22: Y 방향 스케일
    0.0f,        // dx
    0.0f
);
pRenderTarget->SetTransform(shearXMatrix);

 

 

 

📌 복합 변환

DirectX는 변환의 결합을 지원한다. 두개 이상의 행렬을 곱하여 네가지 기본 변환을 결합할 수 있다.

복합 변환시 순서는 ‘Scale → Rotation → Translation’을 따라야 한다.

  • 왜 이 순서를 따르는가 ?
    • 회전을 먼저 시키면 스케일 적용 하려는 축이 달라지기 때문에
    • 이동을 먼저 시키면 회전에 따라 위치가 달라지기 때문에 등..

DirectX 2D는 행벡터 기반으로 행렬곱을 할 때 변환 순서 관점에서 순서는 오른쪽부터 진행된다고 하는데 자료가 많이 없어서 잘 모르겠다.. 아래껀 일단 다시 찾아보기로 한다!

XMMATRIX scale = XMMatrixScaling(sx, sy, 1.0f);
XMMATRIX rotation = XMMatrixRotationZ(angle);
XMMATRIX translation = XMMatrixTranslation(tx, ty, 0.0f);

XMMATRIX world = scale * rotation * translation; // 주의! 이 순서로 하면 변환 순서가 translation → rotation → scale 가 되어 잘못됨
//XMMATRIX world = translation * rotation * scale; // 올바른 코드 작성 순서 (이거 맞는 말임?) -> 일단 무시하기

 

 

 

💡 DirectX 2D 에서 변환행렬을 3*2로 표현한 이유?

이건 정말 내 단순한 궁금증이다. 보통 수학에서는 열벡터를 사용하여 행렬계산을 했었는데 D2D의 3*2 행렬을 보고 원소의 뜻을 읽는 순간 헷갈리기 시작했다. D2D는 행벡터를 사용하기 때문이다. 때문에 행렬 그대로 보고 원래대로 계산하듯 직접 행렬 연산을 하면 안된다.  아래부터 아까 찾아본 내용을 기록해둔다.

 

Direct2D에서 변환행렬을 3x2 행렬로 표현하는 이유는 주로 효율성과 구현 편의성 때문

수학적으로 보면 2D 변환을 동차좌표계(homogeneous coordinates)로 나타낼 때 보통 3x3 행렬을 사용함.

 

수학에서의 변환행렬 (3*3)

이 행렬에서 (M11, M12, M21, M22)는 선형변환(회전, 확대, 축소, 왜곡),

(dx, dy)는 평행이동(translation)을 나타낸다.

 

 DirectX2D에서는 이 3x3 행렬에서

마지막 행 |0 0 1 |이 항상 같기 때문에

저장할 필요가 없다고 판단해서 마지막 행을 없앰

 

D2D의 변환행렬 (Direct2D의 D2D1::Matrix3x2F 구조)

하지만 여기에서 2행 3열의 표현법이 아닌

3행 2열의 구조를 선택하여 표현함

→ 이게 궁금. 수학적 행렬곱할때 원소 옮기기 귀찮음

 

 

 Q。왜 굳이 행과 열의 원소를 바꾸어서 3*2 행렬로 표현했느냐?

수학에서는 보통 열벡터를 기준으로 변환 행렬을 정의하고 연산하지만,

Direct나 대부분의 마이크로 소프트사의 그래픽 API는 행벡터를 사용하기 때문이다.

 

DirectX는 C/C++언어를 기반으로 만든 API이기 때문에

행벡터 방식은 코드 구현이 직관적이다.

또 DirectX는 메모리 정렬이 행우선으로 진행되기 때문에

행벡터 방식으로 곱하면 캐시 접근 효율이 좋고

SIMD 최적화도 쉬워진다.

→ 궁금증 해결!

 

 

 

 

 

이제는 가독성 신경쓸 시간이 없다。。。

끝¡

 

 


 

wooj22 - Overview

🎮 Game Programmer. wooj22 has 22 repositories available. Follow their code on GitHub.

github.com

 

양우정

 

www.youtube.com