DirectX Practice

1. DirectX 기초

Srff5123 2023. 10. 5. 02:26
728x90

Direct3D는 응용 프로그램에서 GPU(그래픽 처리 장치)를 제어 하고 프로그래밍하는 데 쓰이는 저수준 그래픽 API(응용 프로그래밍 인터페이스)이다. 이를 이용하여 3차원 그래픽 가속 기능을 이용하여 3차원 세계를 렌더링할 수 있게 된다.

 

COM은 DirectX의 프로그래밍 언어 독립성과 하위 호환성을 가능하게 하는 기술

 

COM 객체의 수명 관리를 돕기 위해 Windows 런타린 라이브러리는 Microsofr::WRL::Comptr이라는 클래스를 제공한다. (#include wrl.h 사용) 이 클래스는 COM 객체를 위한 똑똑한 포인터인데. 범위를 벗어난 Comptr 인스턴스는 바탕 COM 객체에 대해 자동으로 Release를 호출해준다. 따라서 프로그래머가 직접 Release를 호출할 필요는 없다.

 

Comptr의 여러 메소드 중 세가지 정도를 알아보자

 

1. Get : 바탕 COM 인터페이스를 가리키는 포인터를 돌려준다. 해당 COM 인터페이스 포인터 형식의 인수를 받는 함수를 호출할 때 흔히 쓰인다.

 

2. GetAddressOf : 바탕 COM 인터페이스를 가리키는 포인터의 주소를 돌려준다. 함수 매개변수를 통해서 COM 인터페이스 포인터*를 돌려받을 때 흔히 쓰인다.

 

3. Reset : Comptr 인스턴스를 nullptr로 설정하고 바탕 COM 인터페이스의 참조 횟수를 1 감소한다. 이메서드를 사용하는 대신 Comptr 인스턴스에 직접 nullptr을 배정한다.

 

COM 인터페이스들은 이름이 대문자 I로 시작을 한다. 예(ID3D12GraphicsCommandList)

 

 

텍스처 형식

 

2차원 텍스처는 자료 원소들의 행렬 2차원 배열로 나타낸다. 2차원 텍스처의 용도 하나는 2차원 이미지 자료를 저장하는데, 이때 텍스처의 각 원소는 픽셀 하나의 색상을 담는다. 그러나 이것 뿐 아니라 법선 매핑이라는 고급 기법에서는 텍스처를 각 원소의 색상이 아닌 3차원 벡터를 담아. 사용을 하는데 이렇듯 텍스처는 단순한 자료 배열이 아닌 밉맵 수준들이 존재할수있고, GPU 필터링, 다중표본화 등의 특별한 연산도 텍스처에 적용을 할수있다. 단 텍스터는 특정 형식(format)의 자료 원소만 담을 수 있는데  DXGI_FORMAT이라는 열거형으로 지정한다.

 

담을 수 있는 자료의 원소형식의 예시이다.

RGBA는 적 녹 청 알파로 , 앞의 세개는 3원색을 의미하고 A 알파는 일반적으로는 투명도를 제어한다. 하지만 텍스처에 반드시 색상을 담아야 하는 것은 아니기에 임이의 3차원 벡터도 담을 수 있다.

무형식 텍스처

무형식의 텍스처는 메모리만 확보를 해두고 자료의 구체적인 해석방식은 나중에 텍스처 파이프라인에 묶을떄 지정하는 용도로 쓰이는데 이것은 reinterpret_cast와 비슷한 용도로 쓰인다. 

 

교환 사슬과 페이지 전환

 

게임을 하거나 애니메이션이 껌벅이는 현상이 있다 그것을 피하기 위한 방법

 

이중버퍼링 기법을 사용하는 것인데 우선 하드웨어로 관리되는 두개의 텍스처 버퍼를 만들어주고

하나는 전면 버퍼(먼저 출력) 또 하나는 후면버퍼(나중 출력)로 전면 버퍼가 먼저 화면에 표시가 될때 후면 버퍼가 다음 화면을 미리준비하여 이 과정을 서로 바꾸어가며 하는 방법으로 그래서 교환사슬이라고 한다. Direct3D에서는 제시(presenting)이라고 한다.  이것을 대표하는 인터페이스는 IDXGISwapchain으로 버퍼 크기 변경을 위한 메서드

(IDXGISwapChain::ResizeBuffers)와 버퍼의 제시를 위한 메서드(IDXGISwapChain::Present)도 제공을 해준다.

 

렌더링 파이프라인

 

3차원 장면의 기하학적 서술과 가상 카메라의 위치 및 방향이 주어졌을 때, 현재 가상 카메라에 비친 3차원 장명의 모습에 근거해서 2차원 이미지를 생성하는 데 필요한 일련의 단계들 전체를 렌더링 파이프라인 이라고 부른다.

 

이것을 구성하기 위해서는 두가지를 알아야하는데

1.  3차원적 환상(평평한 모니터 화면을 통해서 3차원 세상을 보고 있다는 환상)

2.  색상을 수학과 Direct3D 코드로 표현하고 다루는 방법이다.

 

컴퓨터 화면은 2차원 평면 이기 때문에 사람이 화면의 3차원 세계를 인식하려면 2차원 이미지에서 부피와 공간적 깊이감을 실제처럼 느껴지게 해야한다. 착시효과를 주는 방법이다.

 

가까이에 있는 물체가 멀리 있는 것보다 더 크게보이는 현상을 주는 것으로 그림을 그릴때 멀리있는 것을 앞에 있는 것보다 작게 그리는 것이다. 

 

다음으로는 조명과 음영이다. 이 두가지는 3차원 물체의 입체적 형태와 부피를 묘사하는데 아주 중요한 역할을 한다. 그림자를 이용하여 어떠한 물체가 공중에서 얼마나 떠있는 지를 대략이나마 알수있게 된다.

모형의 표현

 

3차원 물체는 보통 삼각형의 메시로 근사해서 표현을 한다. 게임해서 어떤 캐릭터나 집 이런모든 것들은 삼각형 메시를 여러개 이어붙여 모델링을 표현하는데 많은 삼각형을 사용할수록 물체를 세밀하게 만들 수 있다. 예를 들면 그림판에서 원을 하나 그리고 그것을 확대하면 네모난 점으로 나타나는것과 비슷하다.

 

그리기 연산

 

정점과 입력 배치

Direct3D의 정점에 공간적 위치 이외에 추가적인 자료를 부여할수있다. 그러기 위해선 자료를 담을 구조체를 정의한다.

 

하나는 위치와 색상으로 구성되고 하나는 위치, 법선 그리고 두개의 2차원 텍스처 좌표로 구성된다.

 

구조체를 정의한 다음 각 필드, 정점의 각 성분으로 무엇을 할 것인지 Direct3D에  전달을 해주어야 한다.

그것을 위한 수간이 입력 배치 서술(input layout description)이다. 이 서술은 D3D12_IPUT_LAYOUT_DESC구조체 이다.

 

입력 배치 서술은 그냥 D3D12_INPUT_ELEMENT_DESC 형식의 원소들을 담은 배열과 그 원소들의 개수이다.

D3D12_INPUT_ELEMENT_DESC 배열은 각 원소는 정점 구조체의 각 성분을 서술하고,

이 배열의 원소들과 정점 구조체의 성분들은 일대일로 대응하여야 한다.

따라서 정점 구조체가 두개면 그것에 해당하는 D3D12_INPUT_ELEMENT_DESC배열에도 원소 두개가 있어야한다.

 

1.SematicName : 성분에 부여된 문자열의 이름, 정점 셰이더에서 (semantic*)이름으로 쓰이고 반드시 유효한 변수이름이어야 한다. 정점 구조체의 성분을 정점 셰이더 입력과 대응시키는 역할을 한다.

 

2. SemanticIndex : semantic 에 부여된 색인, 하나의 점점 구조체에 텍스처 좌표가 여러개가 있을 수가 있는데, 각 텍스처 좌표에 개별적인 이름을 부여하는 대신, 색인을 통하여 구별한다.

 

3. Format : DXGI_FORMAT 열거형의 한 멤버로, 이 정점 성분의 자료 형식을 Direct3D에게 알려주는 역할을 한다.

 

4. InputSlot : 이 성분의 자료를 가져올 정점 버퍼 슬롯의 색인이다. 총 16개의 정점 버퍼 슬롯을 통하여 정점 자료를 공급 한다. 

 

5. AlignedByteOffset : 지정된 입력 슬롯에서 c++ 정점 구조체의 시작 위치와 이 정점 성분의 시작 위치 사이의 거리를 나타내는 오프셋(Byte 단위)이다. 예를 들어 정점 구조체에서 Pos 성분의 오프셋은 0Byte이다. 구조체의 시작 위치와 성분의 시작 위치가 일치하기 때문이다. 반면 Normal 성분은 Pos 성분의 바이트들을 지나친 위치에서 시작하기에 오프셋이 12Byte이다.  Tex0 성분의 오프셋은 Pos와 Normal을 지나친 위치에 해당하는 24Byte이고, Tex1은 32가 된다.

 

6. InputSlotClass : 이 필드에 항상 D3D12_INPUY_PER_VERTEX_DATA를 지정한다. 다른 값은 고급 기법인 인스턴싱에 쓰인다.

 

7. InstanceDataStepRate : 일단 지금은 0을 지정하고, 이 것 또한 고급 기법인 인스턴싱에 쓰인다.

 

두 정점 구조체 Vertex1과 Vertex2에 대해서는 각각 다음과 같은 입력 배치 서술 배열들을 사용하면 된다.

 

정점 버퍼

 

GPU가 정점들의 배열에 접근하려면, 그 정점들은 버퍼 라고 부르는 GPU 자원(ID3D12Resource)에 넣어 두어야 한다.

그 정점들을 저장하는 버처가 정점 버퍼이다. 버퍼는 텍스처 보다 단순한 자원으로 다차원이 아니며, 필터나 다중표본화 기능이 없다. 응용 프로그램에서 정점같은 자료 원소들의 배열을 GPU에 제공할 때는 항상 버퍼를 사용한다.

 

정점 버퍼를 생성하려면  버퍼 자원을 서술하는 D3D12_RESOURCE_DESC를 채우고 ID312Device::CreateCommittedResource 메서드를 호출해서 ID3D12Resource 객체를 생성한다.

Direct3D 12는 D3D12_RESOURCE_DESC를 상속해서 편의용 생성자들과 메서드들을 추가한 C++ 래퍼 클래스 CD3DX12_RESOURCE_DESC를 제공한다. 이 클래스의 다음 메서드를 이용하면 버퍼를 서술하는 D3D12_RESOURCE_DESC 구체 인스턴스를 간단히 생성할 수 있다.

 

범용 GPU 자원으로 버퍼에서 너비는(Width)는 가로가 아닌 버퍼의 Byte 개수를 뜩한다.

즉 버퍼의 너비는가 float 64개면  예) 64*sizeof(float)로 표현한다.

728x90