DirectX 11

220922

슬뷔 2022. 9. 26. 05:34

그래픽스 파이프라인 -> 물체 하나가 그려지는 과정 (GPU로 연산)

 

우선은 아래와 같이 간소하게 사용함

 

Input-Assenbler (IA) -> 화면에 그리기 위해서 필요한 데이터를 입력받는 단계

 

Vertex Shader -> 버텍스버퍼에 만들어진 정점 정보를 IA 단계가 받고, 버텍스 쉐이더에서 Rasterizer로 정점정보를 넘긴다. 정점 하나당 버텍스스테이지를 병렬로 수행한다.

 

Rasterizer -> 받은 정점 세개를 정규화된 좌표(NDC, NormarizedDeviceCordinate)로 변환된 걸 받는다.

*NDC 좌표계 -> 중심이 0,0 이고 화면의 전체 영역을 -1 ~ 1 범위로 잡은 좌표계이다.

정규화 한다는 것은 비율을 적용해서 값을 -1 ~ 1 사이의 값으로 변환하는 것이다.

Z축으로도 깊이가 1이다.

 

Pixel Shader

 

Output-Merger(OM) -> 최종적으로 랜더타겟에 그림을 출력하는 과정

 

화면에 그림을 그리기 위해서 '정점' 이 필요하다.

 

ID3D11Buffer는 ID3D11Resource 상속을 받음.(Texture 2D도 얘를 상속받는다)

 

ID3D11Resource(리소스) -> 버퍼

                                          -> 텍스쳐 

gpu 메모리상에서 무언가를 만들어낸다. 이미지형태인지(텍스쳐) 아님 단순 메모리 버퍼인지(버퍼)

 

버퍼의 대표적인 용도가 버텍스버퍼, 인덱스버퍼, 콘스탄트버퍼(상수), 스트럭트버퍼 가 있다.

 

버텍스버퍼에다 정점 정보를 저장시킬 것이다.

정점(버텍스)은 삼차원 공간 상에서의 점의 정보이다. 

정점 구성은 프로그래머가 구성할 수 있다.

정점은 삼차원 상에서의 위치정보가 필요하다. 

Vector3로 Pos를 표현한다.

 

0,1,1

1,0,1

-1,0,1

 

이렇게 만든 좌표는 지역변수이기 때문에 init이 끝나면 메모리가 사라진다.

그렇기 때문에 gpu메모리로 할당해서 옮겨야한다.

버퍼에서 gpu로 보내기 위해서는 D3D11_BUFFER_DESC bufferdesc = {}; 을 채워야한다.

버퍼를 만드는 시점에는 디바이스 클래스가 초기회가 되어있어야 한다.

 

bufferdesc.bindflags -> 버퍼가 gpu 장치에 전달될 때 (바인딩) 어떤식으로 연결될 것인가의 용도를 설정

정점정보를 수정하고 덮어쓰고 해야되기 때문에

usage = dynamic 으로 하고

CPUAccessFlags = Write로 해야한다.

 

서브리소스데이터 -> gpu에 넣어주기 전에 초기값 한 번 전달할 수 있다.

전역변수로 버퍼를 생성해서 릴리즈를 해주어야한다.

 

GPU에 버텍스버퍼 36byte 정점3개 들어가있다.

파이프라인 시작을 할 때, IA 단계에서 버텍스버퍼를 IA 단계에서 사용하라고 전달을 시킨다.

그럼 IA 단계에 입력될 버텍스가 얘구나 하고 인지를 하게 된다.

 

stride -> 간격, 장치입장에서는 버텍스버퍼라고 주면 36byte가 정점 하나인지 아니면 몇 바이트당 정점 하나인지

모르기 때문에 간격을 정해주어야한다. -> 버택스구조체사이즈를 주면 된다. -> sizeof()

 

offset -> 시작점으로부터 얼마나 떨어져있는지

버텍스 버퍼 안에 정점이 수천개~수만개가 들어있을 경우, 하나의 버텍스 버퍼안에 시작주소를 옮겨서

0이 아닌 다른 곳으로 시작주소를 줄 수가 있다.

 

버텍스 쉐이더는 HLSL이라는 쉐이더 언어로 코딩을 해야한다.

이걸 컴파일 한 뒤에 바이너리 코드로 바꾸고 gpu가 이해할 수 있는 기계어로 바꾼 다음에 ID3D11VertexShader 객체를 만들어낸다.

 

HLSL은 C++ 코드가 아니기 때문에 #pragma once 를 사용하지 못한다.

그래서 C 스타일로 중복참조를 방지해주어야한다.

#ifndef 파일명

#define 파일명

 

#endif

 

HLSL은 .fx 파일이고, 엔진이 컴파일될때 포함되는 파일이 아니고 별도의 파일이다.

같이 빌드되는건 아니다.

HLSL컴파일러에서 셰이더 형식을 fx로 바꾸고, 모델은 5.0 하고 저장.

 

버텍스 쉐이더 만들기 전에 Input layout 을 만들어야한다.

Input layout 도 IA 단계에 전달이 된다.

Input layout -> 정점 구조 정보

 

IA에 버텍스버퍼의 정보가 넘어오면(간격 28byte만 넘어옴) 앞쪽의 12byte가 좌표고 나머지 16byte가 컬러인걸 모른다.

그래서 하나의 정점 안을 어떻게 구성되어 있는지 정보를 알려주는 것이 Input layout 이다.

 

D3D11_INPUT_ELEMENT_DESC arrLayout[2] -> 정점에 들어있는 데이터 개수만큼 있어야한다.

지금은 포지션, 컬러로만 구성되어 있기 때문에 [2]가 필요하다.

구조체 하나마다 어떤 녀석인지 설명을 할 것이다.

AlignedByteOffset -> 메모리 시작위치가 어디인지?

포지션의 시작위치는 -> 0

사이즈는 12byte 기 때문에 -> DXGI_FORMAT_R32G32B32_FLOAT; ( 플롯은 4바이트씩)

SemanticName = "POSITION";

SemanticIndex = 0;

 

컬러의 시작위치는 -> 12 -> 포지션이 12바이트이기때문~

사이즈는 16byte기 때문에 -> DXGI_FORMAT_R32G32B32A32_FLOAT;

SemanticName = "COLOR";

SemanticIndex = 0;

 

만약, 시멘틱 네임이 컬러 정보지만, POSITION 이라면.. 인덱스는 1로 변경해주어야 겹치지 않는다.

(같은 이름은 존재 할 수가 없다.)

최종 이름은 시멘틱네임+시멘틱인덱스 해서 작성해야한다. EX) POSITION0, COLOR0

시멘틱 인덱스가 0일 경우, 0을 작성하지 않아도 자동으로 0으로 생각한다.

 

Input layout  이 꼭 필요한 이유.

구조체가 정점의 형태랑 맞춰진다는 보장이 없다. 

포지션, 컬러 중에 원하는 것만 받아올 수도 있다.

 

받아온 데이터를 그대로 OUT 구조체를 만들어서 반환한다.

반환할 때, 입력된 pos 는 float3 지만 float4로 반환한다.

시멘틱은 이 녀석이 어떤 녀석인지 설명하는 역할을 한다.

입력 받을 때는 layout이랑 똑같이 매칭시켜서 가져온다.

아웃시킬 때는 SV_Position을 사용한다. 컬러는 그대로 동일..

레스터라이저 단계에 넘어가면 전달한 데이터 중에 sv_position을 NDC좌표계로 인식을 한다.

애초에 입력한 좌표가 NDC를 생각해서 정점단위를 생각했기 때문이다.

x, y, z, 그리고 w는 동차좌표로 1로 넣어줘야한다.

타입이 안맞을땐 float3 를 float4로 확장해서 float4(_in.vPos, 1.f); w를 동차좌표 1로 넣어준다.

(_in.vPos 까지가 float3 이기 때문에 뒤에 w값만 넣어준다)

 

*HLSL 구조체 초기화 문법

VTX_OUT output = (VTX_OUT) 0.f;

output.vPos = _in.vPos;

output.vColor = _in.vColor;

return output;

 

*  SV가 붙은 시멘틱 

시스템 벨류라고해서 특수한 시멘틱이다.

 

버텍스 쉐이더는 하는 일 없이 입력 받은 값을 그대로 레스터라이저에게 리턴시키는 역할만 한다.

 

 

'DirectX 11' 카테고리의 다른 글

220923  (0) 2022.10.01
220929  (0) 2022.09.29
220921  (0) 2022.09.26
220920  (0) 2022.09.22
220919  (1) 2022.09.19