DirectX와 HLSL간의 행렬순서와 연산

프로그래밍/DirectX

 

'DirectX 11을 이용한 3D 게임 프로그래밍 입문' 책을 참고하며 예제를 따라하고 있었는데 셰이더 부분에서 난관에 봉착했다.

 

예제 프로그램의 셰이더 코드에서는 'World * View * Projection' 순으로 행렬연산을 수행하는게, 내 프로그램에서는 'Project * View * World' 순으로 연산해야 정상적인 결과가 나왔기 때문이다.

 

도대체 무엇이 문제인지 계속 신경이 쓰여서 원인을 찾아보았다.

 

 

 

컴퓨터에서 다차원 배열로 행렬을 구성할 때, 행렬을 어떤 순서로 접근할 것인지에 대한 순서가 있다.

 

DirectX에서는 행렬 순서가 row-major(행우선)이고, HLSL에서는 column-major(열우선)이다.

 

 

위와 같이 행렬을 접근하는 순서가 다르기 때문에, 사용하는 라이브러리에 명시된 순서를 확인하고 그에 맞게 연산을 하지 않으면 제대로 된 결과가 나오지 않는다.

 

서로간의 행렬 순서가 달랐기 때문에, DirectX에서는 'World * View * Projection' 순으로 연산하는것을

셰이더(HLSL)에서는 'Projection * View * World'로 연산해야 제대로 된 결과값이 나왔던 것이다.

 

행렬이 DirectX에서 셰이더 상수로 넘어갈때 자동적으로 row-major에서 column-major로 변환된다.

따라서, 셰이더 코드에서도 DirectX와 똑같은 순서로 연산을 하려면, 상수버퍼의 행렬을 전치행렬로 변환하여 갱신해야 한다.

 

1
2
3
4
m_view = DirectX::XMMatrixTranspose(m_view);
m_projection = DirectX::XMMatrixTranspose(m_projection);
 
d3dDevice->CreateBuffer(&cbd, nullptr, &m_constantBuffer);
cs

 

예제에서는 Effect Interface를 사용하는데, 아마 내부에서 자동적으로 전치행렬로 변환하는 듯 하다.

내가 짠 코드에서는 Effect Interface를 사용하지도 않고, 전치행렬을 사용하지도 않아서 연산 순서가 뒤바뀌었던 것.

 

위와 같이 전치행렬로 변환하면, 셰이더 코드에서도 row-major 기준으로 연산을 할 수 있다.

 

1
2
3
Output.Pos = mul(float4(Input.Pos, 1.0f), matrixWorld);
Output.Pos = mul(Output.Pos, matrixView);
Output.Pos = mul(Output.Pos, matrixProj);
cs

row-major

 

1
2
3
Output.Pos = mul(matrixWorld, float4(Input.Pos, 1.0f));
Output.Pos = mul(matrixView, Output.Pos);
Output.Pos = mul(matrixProj, Output.Pos);
cs

column-major

 

 

참고

 

https://en.wikipedia.org/wiki/Row-_and_column-major_order

 

Row- and column-major order - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Illustration of difference between row- and column-major ordering In computing, row-major order and column-major order are methods for storing multidimensional arrays in linear storage

en.wikipedia.org

http://dolphin.ivyro.net/file/hlsl/tutorial06.html

 

HLSL mul

mul은 벡터와 행렬의 순서에 따라 결과가 다르다. mul(x, y) x값이 vector이면 row-vector로 처리하고 y값이 vector이면 column-vector로 처리한다. mul( vector, matrix ) ===>  row vector  (row-major matrix system) mul(matrix, vector ) ===> column vector  (column-major matrix system) DirectX API에서는 4차

dolphin.ivyro.net

https://www.gpgstudy.com/forum/viewtopic.php?p=108628

 

wvp 와 wv 계산후 proj - GpgStudy 포럼

2D, 3D, 다각형, 픽셀 등 게임의 그래픽 프로그래밍에 관한 포럼입니다. 운영자: 류광 심 형근 전체글: 526 가입일: 2002-08-19 23:30 전체글 글쓴이: 심 형근 » 2008-09-23 10:45 고정폭을 경계선을 가지는 카툰렌더링을 구현중 약간 이상한 증상을 발견하였는데, 왜 그런지를 모르겠습니다. 보통 입력된 버텍스 버퍼를 WorldViewProj 전환을 위해서 wvp = World * View * Proj 를 할당하며 쉐이더에서는

www.gpgstudy.com

SwapChain의 DXGI_FORMAT 종류

프로그래밍/DirectX

DirectX에서 SwapChain을 생성할때 후면버퍼의 Format을 설정하는 부분이 있다.

 

1
2
3
4
5
6
7
8
9
// Swap chain
DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = m_width;
sd.BufferDesc.Height = m_height;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
cs

 

책에서나 다른 예제에서는 대충 "DXGI_FORMAT_R8G8B8A8_UNORM 을 자주 쓴다." 라는 말만 적혀있지, 왜 그걸 자주 쓰며 다른 Format에는 어떤 것이 있는지는 잘 나와있지 않다.

 

호기심에 DXGI_FORMAT의 다른 값을 넣어봤다.

DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R8G8B8A8_UINT 등등...

 

결과는 SwapChain 생성에 실패했다.

 

구글링을 해보았지만, 대부분 "DXGI_FORMAT_R8G8B8A8_UNORM을 기본으로 사용한다" 라는 튜토리얼 형식의 똑같은 설명 뿐이고 다른 Format을 사용하는 예제는 보이지 않았다.

 

내가 중간에 잘못 쓴 코드가 있는 것인지, 컴퓨터가 문제인지 알 수 없어서 한참을 찾아본 끝에서야 MSDN에서 관련 내용을 찾을 수 있었다.

 

This structure is used by the GetDisplayModeList and FindClosestMatchingMode methods.

The following format values are valid for display modes and when you create a bit-block transfer (bitblt) model swap chain. The valid values depend on the feature level that you are working with.

 

즉, SwapChain의 후면버퍼와 같이 디스플레이에 관련된 DXGI_FORMAT은 위에 나열된 형식밖에 되지 않는다는 것.

DirectX의 버전이 올라가면서 표현 가능한 색상의 비트수만 커질뿐, 본질적인 형태는 크게 변하지 않은 듯 하다.

그래서 보통 "대부분의 경우에는 DXGI_FORMAT_R8G8B8A8_UNORM을 사용한다" 라고 설명하고 사용한 것 같다.

 

DXGI_FORMAT_R8G8B8A8_SNORM나 DXGI_FORMAT_R8G8B8A8_UINT와 같은 Format은 아예 지원하지 않는 형식이기 때문에 SwapChain을 만들지 못하고 오류가 발생한 것으로 보인다.

 

https://docs.microsoft.com/ko-kr/previous-versions/windows/desktop/legacy/bb173064(v=vs.85)

 

DXGI_MODE_DESC structure (Windows)

DXGI_MODE_DESC structure 05/18/2018 2 minutes to read In this article --> Describes a display mode. Syntax typedef struct DXGI_MODE_DESC { UINT                     Width; UINT                     Height; DXGI_RATIONAL            RefreshRate; DXGI_FORMAT   

docs.microsoft.com

DXGI_MODE_SCANLINE_ORDER, DXGI_MODE_SCALING의 Flags 종류

프로그래밍/DirectX

DXGI_MODE_SCANLINE_ORDER

 

1
2
3
4
5
6
typedef enum DXGI_MODE_SCANLINE_ORDER { 
  DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED        = 0,
  DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE        = 1,
  DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST  = 2,
  DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST  = 3
} DXGI_MODE_SCANLINE_ORDER;
cs
  • DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED
    Scanline order is unspecified.

  • DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
    The image is created from the first scanline to the last without skipping any.

  • DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST
    The image is created beginning with the upper field.

  • DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST
    The image is created beginning with the lower field.

 

디스플레이에 화면을 그릴 때, 스캔라인 순서를 지정한다.

스캔라인 순서로 화면을 그릴 때 프로그레시브(Progressive)로 그리는가, 인터레이스(Interlace)로 그리는가를 설정할 수 있다.

 

DXGI_MODE_SCANLINE_ORDER_PROGRESSSIVE를 선택하면 프로그레시브(Progressive)로 그리게 된다.

DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST를 선택하면 인터레이스(Interlace)로 그리면서 홀수번째 줄에서,

DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST를 선택하면 짝수번째 줄에서부터 시작한다.

DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED를 선택하면 아무런 스캔라인 순서를 설정하지 않게 되는데, 이게 결과적으로 디스플레이에 맞춰서 자동적으로 선택을 하는건지 어떻게 되는건지 전혀 모르겠다. 정보가 안보인다.

 

프로그레시브(Progressive)와 인터레이스(Interlace)에 관한 내용은 아래의 링크를 참조.

 

https://m.cafe.daum.net/korea-man/7v20/84?

 

프로그레시브(Progresive)와 인터레이스(Interlace)는 어떻게 틀립니까?

TV나 모니터와 같은 디스플레이 기기를 스캔해 주는 방식을 뜻합니다. 인터레이스는 주사선을 한 칸씩 건너 뛰어서 2회 스캐닝한다고 하여 우리말로는 비월주사 방식이라고 하며, 프로그레시브는 건너뜀 없이 모든 주사선을 스캐닝해 줍니다. 일반적으로 TV는 주로 동영상을 보여주므로 비월주사를 하더라도 인간의 눈으로는 구분이 되지 않기 때문에

m.cafe.daum.net

https://hruj.tistory.com/entry/m3

 

3. 인터레이스란 무엇일까 (인터레이스 프로그레시브 풀다운 IVTC)

인터레이스와 프로그래시브가 뭘까요... 요즘 hd 티비 구매를 고려 하시거나 dvd 몇번 다뤄보신분은 익숙하실거 같은데요 음... 인터레이스에 대한 이야기는 또 티비라는 매체가 대중화 될수 있을까를 논하던 시대..

hruj.tistory.com

 

 

 

DXGI_MODE_SCALING

 

1
2
3
4
5
typedef enum DXGI_MODE_SCALING { 
  DXGI_MODE_SCALING_UNSPECIFIED  = 0,
  DXGI_MODE_SCALING_CENTERED     = 1,
  DXGI_MODE_SCALING_STRETCHED    = 2
} DXGI_MODE_SCALING;
cs
  • DXGI_MODE_SCALING_UNSPECIFIED
    Unspecified scaling.

  • DXGI_MODE_SCALING_CENTERED
    Specifies no scaling. The image is centered on the display. This flag is typically used for a fixed-dot-pitch display (such as an LED display).

  • DXGI_MODE_SCALING_STRETCHED
    Specifies stretched scaling.

 

디스플레이의 비례에 따른 처리를 지정한다.

화면을 늘리거나 줄이거나, 전체화면을 하는 등 후면버퍼와의 비례차가 생길 때 어떻게 하는가에 대한 옵션이다.

 

DXGI_MODE_SCALING_UNSPECIFIED를 선택하면 아무런 설정도 하지 않는다.

DXGI_MODE_SCALING_CENTERED를 선택하면 스케일링을 하지 않고, 이미지가 디스플레이의 정 중앙에 위치하게 된다. 그리고 비례에 맞춰서 화면의 좌우/상단을 잘라내서 검은색 패딩으로 채운다.

DXGI_MODE_SCALING_STRETCHED를 선택하면 확장 스케일링을 지정한다.

 

DXGI_MODE_SCALING_CENTERED를 선택한 경우는 다른 두개의 옵션과는 명확하게 다른 결과가 나왔다. 하지만, UNSPECIFIED와 STRETCHED의 차이는 아직 잘 모르겠다. 추후 이미지를 띄워보고 살펴봐야 할 것 같다.

 

추가로, Alt+Enter 를 눌러서 전체화면을 할 경우 UNSPECIFIED는 바로 화면이 뜨는 반면 CENTERED/STRETCHED는 화면이 뜨기까지의 잠시 텀이 있는 등 스케일링 옵션을 처리하기 위해서인지 반응이 조금 느리다.