Direct3D의 초기화 작업에 필요한 기본 형식 및 몇 가지 기본적인 그래픽 개념을 다뤄보겠다.

 

Direct3D의 개요

  • Direct3D는 그래픽 하드웨어를 제어할 수 있는 소프트웨어 인터페이스를 제공한다.
  • Graphic Hardware가 Direct3D capable device이기만 하다면 Hardware의 구체적인 세부사항을 걱정할 필요가 없다.
    • 물론 다중 표본화 횟수 등 Hardware마다 다를 수 있는 항목들도 존재한다.
    • 그래도 Direct3D capable device는 반드시 Direct3D capability set 전체를 지원해야 한다.
  • 이는 Direct3D 9와 비교해서 가장 두드러지는 특징이다

COM(Component Object Model)

  • COM은 DirectX의 프로그래밍 언어 독립성과 하위 호환성을 가능하게 하는 기술이다.
  • C++로 DirectX Application을 개발 할  때에 세부 사항 대부분이 드러나지 않는다.
    • 프로그래머가 알아야 할 것은 COM Interface로의 포인터를 얻는 방법 뿐이다.
  • COM Interface는 C++의 new 키워드로 생성하지 않는다.
    • 또한 삭제 할 때도 delete가 아니라 Interface의 Release 메서드를 호출한다.
    • 이는 COM Object들이 자신만의 고유한 방식으로 메모리를 관리하기 때문이다.
  • COM에 관한 이야기는 이보다 훨씬 많지만, DirectX를 사용하는데 이 이상은 불필요하다.
  • 마지막으로 COM Interface들은 Class 이름이 I로 시작한다.
    • 예를 들어 DirectX11의 2차원 Texture를 나타내는 COM Interface는 ID3D11Texture2D이다.

텍스처 및 자료 자원 형식

  • 2차원 Texture는 2차원 배열로 원소 당 2차원 이미지 한 Pixel의 색상을 담는다.
  • 하지만 이것이 2차원 Texture의 유일한 용도는 아니다.
    • 예를 들어, 법선 매핑에서는 Texture의 각 원소가 색상이 아니라 3차원 Vector를 담는다.
  • Texture라고 하면 이미지 자료 저장을 떠올리지만, 실제로는 그보다 훨씬 범용적이다.
    • Texture에는 밉맵 수준들이 존재할 수도 있다.
    • GPU Filtering, 다중 표본화 등의 특별한 연산도 적용할 수 있따.
    • 더 나아가 아무 자료나 아무 자료나 담을 수 있는 것은 아니라는 점도 중요하다.
  • Texture는 DXGI_FORMAT이라는 Enumeration으로 지정된 format을 따르는 자료만 담을 수 있다.

https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format

 

DXGI_FORMAT (dxgiformat.h) - Win32 apps

Resource data formats, including fully-typed and typeless formats. A list of modifiers at the bottom of the page more fully describes each format type.

docs.microsoft.com

  • 몇가지만 예시를 들어보겠다.
    • DXGI_FORMAT_R32G32B32_FLOAT
      • 각 원소는 32비트 float 3개로 이루어진다.
    • DXGI_FORMAT_R16G16B16_UNORM
      • 각 원소는 [0, 1] 구가능로 사상되는 16비트 성분 4개로 이루어진다.
    • DXGI_FORMAT_R8G8B8A8_SINT
      • 각 원소는 [-128, 127] 구간으로 사상되는 8비트 signed int 성분 4개로 이루어진다.
  • 여기서 R, G, B, A는 빛의 삼원색과 알파(Alpha, 투명도) 성분을 뜻한다.
  • 앞서 얘기 했듯이, RGBA라 하여 반드시 Texture에 색상을 담아야 하는 것은 아니다.
    • 예를 들어 DXGI_FORMAT_R32G32B32_FLOAT에는 float로 이루어진 3차원 Vector를 담을 수 있다.
  • 또한 typeless한 Texture도 존재한다.
    • 이는 메모리만 확보해두고 자료의 구체적인 해석 방식은 나중에 Texture를 Pipeline에 묶을 때 지정한다.
    • C++의 reinterpret_cast와 비슷하다고 생각하면 된다.
    • 예를 들어, 8비트 성분 4개를 할당하되, 구체적인 자료 형식을 지정하지 않으려면 DXGI_FORMAT_R8G8B8A8_TYPELESS를 지정하면 된다.

교환 사실과 페이지 전환

  • 페이지 전환은 애니메이션이 깜빡대는 현상을 방지하기 위한 최선의 방법은 버퍼링이다.
    • 애니메이션 한 프레임 전체를 후면 버퍼에 그린 뒤, 하나의 프레임으로 화면에 표시를 한다.
    • 이는 사용자에게 프레임이 그려지는 과정을 나타내지 않는다는 장점이 있다.
  • 버퍼링을 구현 할 때에는 Hardware로 두 개의 Texture Buffer를 관리한다.
    • 화면 모니터에 표시되는 이미지 자료를 담는 것은 전면 버퍼(front buffer)
    • 화면 밖에서 현재 화면에 표시되고 있는 프레임 다음 것을 담는 것은 후면 버퍼(back buffer)
    • Front Buffer가 화면에 표시되면 Back Buffer와 역할을 맞바꾼다.
  • Back Buffer를 Front Buffer와 교환하여 화면에 표시되게 하는 것을 제시(Presenting)이라 부른다.
  • Presenting은 Buffer의 내용이 아니라 각 Buffer의 Pointer를 맞바꾸는 매우 효율적인 연산이다.
  • Front Buffer와 Back Buffer는 하나의 교환 사슬(Swap Chain)을 형성한다.
    • 이는 IDXGISwapChain이라는 Interface로 대표된다.
    • 이 Interface는 Front Buffer와 Back Buffer를 담을 수 있다.
    • 또한 Buffer 크기 변경을 위한 ResizeBuffers와 Presenting을 위한 Present 메소드를 제공한다.
  • 물론 2개 이상의 Buffer를 사용할 수도 있다. 하지만 일반적으로 2개면 충분하다.

깊이 버퍼링(Depth Buffer)

  • Depth Buffer는 이미지 자료를 담지 않는 Texture의 한 예로 각 Pixel의 깊이 정보를 담는다.
    • Pixel의 깊이는 [0.0, 1.0]으로 값이 작을수록 관찰자와 가깝다.
  • Depth Buffer는 Back Buffer의 Pixel들과 일대일 대응 관계이다.
    • 구체적으로 Buffer의 크기와 대응하는 Pixel들의 Index까지 동일하다.
  • Direct3D에서는 한 물체의 Pixel들이 다른 물체 앞에 있는지 판정 하기 위해 Depth Buffering 혹은 Z-Buffering을 사용한다.
  • Depth Buffering에서는 그리는 순서는 중요하지 않다.
    • 실제로 먼 물체부터 그리는 방법으로 깊이 문제를 해결하기도 한다.
    • 하지만 이는 크게 두가지 문제가 발생한다.
      • 하나는 커다란 자료 집합을 정렬 해야 한다는 것.
      • 다른 하나는 맞물린 형태의 물체들은 제대로 처리하지 못한다는 것.
    • 이에 반해 Depth Buffering은 Graphic Hardware 안에서 공짜로 일어나며, 맞물린 물체도 제대로 처리해준다.

Depth Buffer의 작동 방식

  • 먼저 Back Buffer를 흰색이나 검은색과 같은 기본 색으로 지운다.
    • 이 때 Depth Buffer의 기본 값인 1.0으로 초기화 된다.
  • 이후 Rendering 할 물체의 Depth Buffer 값을 비교한다.
    • 물체의 Depth 값이 Depth Buffer 값보다 작을 경우, Depth Buffer와 함께 Back Buffer 값이 갱신된다.
  • Depth 값이 작을수록 관찰자와 가까운 곳에 위치한다는 점을 생각한다면 실로 합당한 알고리즘이다.
  • Depth Buffer도 하나의 Texture이므로 특정한 자료 형식을 지정해서 생성해야 한다.
    • 이 또한 DXGI_FORMAT이란 Enumeration을 따른다.
  • 몇가지 예시를 들어보자.
    • DXGI_FORMAT_D32_FLOAT_S8X24_UINT
      • 각 Texel은 32bit의 float형의 depth 값을 가진다.
      • Stencil은 [0, 255] 구간으로 사상되는 8bit의 unsigned int형의 값을 가진다.
      • 마지막으로 용도 없이 채우는 용으로만 쓰이는 24bit의 padding으로 구성된다.
    • DXGI_FORMAT_D32_FLOAT
      • 각 Texel은 하나의 32bit float형의 depth 값을 가진다.
    • DXGI_FORMAT_D24_UNORM_S8_UINT
      • 각 Texel은 [0, 1] 구간으로 사사오디는 unsigned 24bit의 depth 값을 가진다.
      • Stencil은 [0, 255]구간으로 사상되는 8bit unsigned int형의 값을 가진다.
  • 사실 Depth Buffer는 Depth-Stencil Buffer이라 부르는 것이 더 정확하다.
    • Stencil Buffer를 사용하려면 반드시 Depth Buffer에 부착해야 하기 때문이다.

텍스처 자원 뷰

  • Rendering Pipeline에 Texture를 bind 할 수 있는 단계는 여럿 있따.
  • 흔한 용도는 다음 두가지이다.
    • Texture를 Render 대상으로 묶는 것.
      • Direct3D가 Texture에 Rendering하는 경우.
    • Shader Resource로서 묶는 것
      • Shader 안에서 Texture를 추출하는 경우
  • 이 두가지 용도의 Texture를 생성할 때에는 다음과 같이 Texture를 bind 할 단계를 지정하는 bind flag를 사용한다.
D3D11_BIND_RENDER_TARET | D3D11_BIND_SHADER_RESOURCE

 

 

https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_bind_flag

 

D3D11_BIND_FLAG (d3d11.h) - Win32 apps

Identifies how to bind a resource to the pipeline.

docs.microsoft.com

  • 사실 Texture Resource가 Pipeline이 단계에 직접 묶이는 것은 아니다.
    • 실제로는 Texture가 연고나된 Resource View를 Pipeline 단계에 묶는다.
  • 어떤 용도이든 Direct3D에서 Texture를 사용하려면 초기화 시점에서 그 Texture의 Resource View를 생성해야 한다.
  • 이러한 절차는 효율성을 위한 것으로, SDK에는 다음과 같이 나와 있다.
    • 이렇게 하면 실행시점 Runtime과 Driver가 유효성 점검과 매핑을 뷰 생성 시점에서 수행할 수 있기 때문에 결속 시점에서의 형식 점검이 최소화 된다.
  • 따라서 Texture를 Render 대상과 Shader Resource로 사용하기 위해서는 두 개의 View를 생성할 필요가 있다.
    • 하나는 Render 대상 View인 ID3D11RenderTargetView
    • 다른 하나는 Shader Resource View인 ID3D11ShaderResourceView
  • Resource View는 본질적으로 두가지 일을 한다.
    • 하나는 DirectEd에게 Resource의 사용 방식(Pipeline의 어떤 단계에 묶을 것인지)을 알려주는 것.
    • 다른 하나는 생성 시점에서 Typeless Resource의 Type을 지정하는 것.
      • 이 경우, Texture 원소를 한 Pipeline 단계에서는 float로 사용하고, 다른 단계에서는 int로 사용할 수 있다.
  • 어떤 Resource에 대한 View를 생성할 수 있으려면 애초에 해당 Resource을 생성 할 때 Bind Flag를 지정해야 한다.
    • 예를 들어 D3D11_BIND_DEPTH_STENCIL flag를 지정하지 않고 생성한 자원은 ID3D11DepthStencilView를 생성할 수 없다.
    • 무작정 생성하려 하면 다음 오류 메시지가 뜬다
D3D11: ERROR: ID3D11Device::CreateDepthStencilView: A DepthStencilView 
cannot be created of a Resource that did not specify 
D3D11_BIND_DEPTH_STENCIL.

다중표본화의 이론

  • 모니터의 Pixel들은 무한히 작지 않기 위해, 임의의 선을 완벽하게 나타내는 것은 불가능하다.
    • 때문에 Aliasing 효과가 나타나기도 한다.
  • 모니터 해상도를 키워 Pixel 크기를 줄이면 문제를 완화할 수도 있다.
  • 그러나 모니터 해상도를 키우는 것으로 해결 못할 때도 있는데, 이를 위해 Anti Aliasing 기법이 존재한다.
    • 다중표본화(Super Sampling)도 그 중 하나이다.
  • Super Sampling에서는 Back Buffer와 Depth Buffer를 화면 해상도의 4배로 크게 잡는다.
  • 3차원 장면을 4배 크기의 해상도에서 Back Buffer에 Rendering한다.
  • 이를 화면에 출력 할 때는 원래 크기로 Resolving(혹은 down sampling)한다.
  • 이 Resolving 공정은 4개의 Pixel 블록의 각 색상의 평균을 최종 색상으로 사용한다.
    • 간단히 말해서, Super Sampling은 화면 해상도를 Software 상에서 증가하는 것이라 할 수 있다.
  • Super Sampling은 Pixle 처리량과 메모리 소비량이 4배라서 비용이 크다.
    • Direct3D는 이를 대신해 다중표본화(Multi Sampling)라는 절충안을 지원한다.
  • Multi Sampling은 일부 계산 결과를 부분 Pixel들 사이에서 공유하기 때문이 Super Sampling보다 비용이 적다.
    • 4X Multi Sampling의 경우 Super Sampling처럼 해상도 4배의 Depth Buffer와 Back Buffer를 사용한다.
    • 그러나 이미지 색상을 각 부분 Pixel마다 계산하는 것이 아니다.
      • Pixel마다 1번씩만 계산하고, 그 색상과 부분 Pixel들의 가시성과 포괄도를 이용해 최종 색을 결정한다.
        • 가시성: 이를 위해 부분 Pixel당 Depth, Stencil 판정이 일어남
        • 포괄도: 부분 Pixel을 다각형이 어느정도나 덮고 있는지를 뜻하는 값
  • Multi Sampling과 Super Sampling의 차이를 잘 파악하자.
    • Super Sampling은 Pixel별로 계산되므로 한 Pixel당 부분 Pixel의 값이 다를 수 있다.
    • 반면 Multi Sampling은 Pixel당 한번 계산되어 덮인 모든 가시 부분 Pixel에 계산된다.
  • 색을 계산하는 것은 매우 무거운 연산이다.
    • 때문에 Multi Sampling이 Super Sampling에 비해 상당한 비용 절감을 이룬 방식이다.
    • 하지만 더 정확하한 Texture와 Shader Aliasing 처리는 Super Sampling이 월등하다.
  • 하나의 Pixel을 부분 Pixel로 분할하는 패턴은 Hardware 제조사마다 다를 수 있다.

Direct3D의 Multi Sampling

  • Multi Sampling을 위해서는 DXGI_SAMPLE_DESC라는 구조체를 채워야 한다.
typedef struct DXGI_SAMPLE_DESC
{
    UINT Count;
    UINT Quality;
} DXGI_SAMPLE_DESC;
  • Count
    • Pixel당 추출할 표본의 개수
  • Quality
    • 원하는 품질 수준을 지정한다.
      • "품질 수준"에 대한 구체적인 의미는 Hardware 제조사마다 다를 수 있다.
  • 표본 개수가 많을수록, 품질 수준이 높을수록 렌더링 비용이 증가한다.
    • 때문에 비용과 속도 사이에 절충선을 잘 잡아야 한다.
  • 품질 수준들의 범위는 Texture type과 Pixel당 표본 개수에 의존한다.
  • 주어진 Texture type과 표본 개수의 조합에 대한 품질 수준들의 개수를 알고 싶을때는 다음 메소드를 사용한다.
    • 주어진 Texture type과 표본 개수의 조합을 장치가 지원하지 않으면 0을 반환한다.
    • 그 외에는 주어진 조합에 대한 품질 수준 개수가 pNumQualityLevels에 저장된다.
      • 하나의 Texture type과 표본 개수 조합에 대한 유효한 품질 수준은 [0, *pNumQualityLevels - 1]이다.
HRESULT ID3D11Device::CheckMultisampleQualityLevels(
  DXGI_FORMAT Format,
  UINT        SampleCount,
  UINT        *pNumQualityLevels
);
  • D3D12에서는 해당 객체에 이 메서드를 찾을 수 없었다. 대신 다음 구조체를 발견할 수 있었다.
typedef struct D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS {
  DXGI_FORMAT                           Format;
  UINT                                  SampleCount;
  D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS Flags;
  UINT                                  NumQualityLevels;
} D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS;
  • 한 Pixel당 추출할 수 있는 최대 표본 개수는 다음과 같이 정의된다.
#define	D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT	( 32 )
  • 그러나 실제로는 Multi Sampling의 성능과 메모리 비용을 합리적인 수준으로 유지하기 위해 4개나 8개만 추출한다.
  • Multi Sampling을 하고 싶지 않으면 표본 개수를 1로, 품질 수준을 0으로 설정하면 된다.

기능 수준(Feature Level)

  • Direct3D 11에서 도입된 Feature Level은 엄밀한 기능성 집합을 정의한다.
typedef 
enum D3D_FEATURE_LEVEL
    {
        D3D_FEATURE_LEVEL_1_0_CORE	= 0x1000,
        D3D_FEATURE_LEVEL_9_1	= 0x9100,
        D3D_FEATURE_LEVEL_9_2	= 0x9200,
        D3D_FEATURE_LEVEL_9_3	= 0x9300,
        D3D_FEATURE_LEVEL_10_0	= 0xa000,
        D3D_FEATURE_LEVEL_10_1	= 0xa100,
        D3D_FEATURE_LEVEL_11_0	= 0xb000,
        D3D_FEATURE_LEVEL_11_1	= 0xb100,
        D3D_FEATURE_LEVEL_12_0	= 0xc000,
        D3D_FEATURE_LEVEL_12_1	= 0xc100
    } 	D3D_FEATURE_LEVEL;
  • 이 기능의 핵심은, 사용자의 Hardware가 특정 Feature Level을 지원하지 않는다면 그보다 더 낮은(오래된) Feature Level로 내려가서 Application을 실행한다는 것이다.
    • 이를 위해 Application은 Feature Level 지원 여부를 최신서부터 오래된 순서로 하나씩 점검해나간다.

 

------------------------------------------------

책에서 4-1챕터에 해당하는 내용들입니다.

이론적인 부분과 코드 부분이 많아서 분리해서 올리고자 합니다.

다음에는 5챕터에 해당하는 내용을 정리하고, 그 다음에는 4-2부터 남은 4챕터의 내용을 코드와 함께 정리하겠습니다.

+ Recent posts