DirectX 11/DX11 Tutorial

[03] DirectX 11 초기화 (3)

김띠띵 2024. 5. 20. 18:05

 

 

 

Tutorial 3: Initializing DirectX 11

Tutorial 3: Initializing DirectX 11 This tutorial will be the first introduction to working with DirectX 11. We will address how to initialize and shut down Direct3D as well as how to render to a window. Updated Framework We are going to add another class

www.rastertek.com

DirectX11의 공부 목적으로 위 링크의 튜토리얼을 보고 제 생각을 포함하여 정리하였습니다.


들어가며

 

이번엔 정말 초기화 끝내야지


D3DClass.cpp

 

2. Initalize

Rasterizer state라는 단어가 보이는데 Rasterizer는 렌더링 파이프 라인의 단계중 하나인데, 하는 역할은 3D장면을 2D 픽셀 장면으로 변환하는 단계다.

이전에 생성한 Depth, Stencil state가 Depth, Stencil Buffer의 동작 유형을 정의 했으므로, Rasterizer state는 Rasterizer의 동작 유형을 정의한다.

// 폴리곤이 어떻게 그려질지의 속성을 Desc에 채워넣기
rasterDesc.AntialiasedLineEnable = false;
rasterDesc.CullMode = D3D11_CULL_BACK;
rasterDesc.DepthBias = 0;
rasterDesc.DepthBiasClamp = 0.0f;
rasterDesc.DepthClipEnable = true;
rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.FrontCounterClockwise = false;
rasterDesc.MultisampleEnable = false;
rasterDesc.ScissorEnable = false;
rasterDesc.SlopeScaledDepthBias = 0.0f;

// Desc를 이용하여 rasterizer state를 생성
m_device->CreateRasterizerState(&rasterDesc, &m_rasterState);

// 생성한 rasterizer state 파이프 라인에 지정
m_deviceContext->RSSetState(m_rasterState);

이쯤 되면 이런 그래픽 리소스(Rasterizer state, Depth buffer, Swap chain, ...)등을 만들 때 대부분 Desc 구조체에 속성값을 채워 넣고 이를 이용해 생성하는 것을 알 수 있다.

 

그 후 Viewport가 작성된다.

Viewport 의 역할을 설명하면 Render target view에 그려지는 그림이 모니터에 다 출력되는 건 아니다, 그래서 출력되는 영역을 이 Viewport가 알려준다.

이는 렌더링을 좀 더 효율적으로 만들어 주는데 그 예시로 만약 사진처럼 프로그램의 반이 화면에서 벗어나면 벗어난 화면에 대해서는 굳이 렌더링하며 시간과 노력을 할 필요 없다.

또한 뷰포트를 여러개 배치할 수도 있는데 유명한 "It takes two"처럼 왼쪽, 오른쪽 뷰포트를 설정해 같은 공간에서 다른 시점으로 제공할 수도 있다.

https://www.ea.com/ko-kr/games/it-takes-two

// rasterizer state 파이프 라인 RS단계에 지정
m_deviceContext->RSSetState(m_rasterState);

// Viewport는 다른 그래픽 리소스와 달리 Desc없이 직접 설정한다.
// 뷰포트 속성 설정
m_viewport.Width = (float)screenWidth;
m_viewport.Height = (float)screenHeight;
m_viewport.MinDepth = 0.0f;
m_viewport.MaxDepth = 1.0f;
m_viewport.TopLeftX = 0.0f;
m_viewport.TopLeftY = 0.0f;

// viewport를 생성하고 파이프 라인 RS단계에 지정.
m_deviceContext->RSSetViewports(1, &m_viewport);

 

다음은 3D 공간변환을 위한 행렬 설정이 작성된다.  사용 되는 공간은 네 가지로 볼 수 있다.

1. 물체 공간

2. 월드 공간

3. 뷰 공간

4. 투영 공간

 

3D게임이라고 해도 결국 결국 최종으로 보는 것은 2D Textrue다. 한 프레임 한 프레임마다 그냥 2D Texture가 바뀌는 것 뿐이다.

우리가 만드는 3D공간을 2D Texture위로 투영 시키게 되면 그 Texture가 가진 공간이 투영공간이다.

투영 : 도형이나 입체를 다른 평면에 옮기는 일.

 

우리가 여태 설정한 코드에 따르면 결국 뷰포트 영역만이 렌더링 되어 모니터로 나타난다.

즉, 게임 월드에서 원하는 시점, 원하는 각도로 렌더링할 공간을 일단 잘라내서 우리의 모니터(투영공간)으로 그리는데, 이 일단 잘라낸 공간을 뷰 공간이라 한다. 카메라와 같다고 생각하면 된다.

뷰 공간 이전은 "뷰포트의 영역 + 뷰포트에 포함되지 않은 영역" 인데 이 공간을 월드 공간이라 한다.

위의 사진처럼 게임 월드에 있는 모두 그려지는것들은 월드 공간을 사용한다.

 

갑자기 Player4에게 검이 주어져 검을 손에 장착 시키고 싶어졌다.

우리가 월드 공간만 조작할 수 있다고 하면 계속 월드 공간을 기준으로 힘들게 계산을 할 수 밖에 없다.

근데 Player4가 기준이 되는 공간이 있다면 Player4가 어느곳을 이동하더라도 Player4의 손 좌표는 같기 때문에 캐릭터 고유 공간을 기준으로 계산하면 쉽게 검을 쥐어줄 수 있다.

이런 Player4처럼 게임 월드 속에서 각각 그려지는 것 마다의 고유 공간을 로컬 공간이라 한다.

이렇게 각 공간이 있는데 최종적으로 우리가 보기까지

"로컬 공간 → 월드 공간 → 뷰 공간 → 투영 공간" 으로 공간 변환이 이루어 진다. (계속 좌표의 기준이 바뀜)

각 공간마다 행렬을 만들어 행렬을 곱해주면 공간 변환이 이루어진다.

// 월드 행렬을 단위 행렬로 초기화 합니다. (월드 변환시 사용)
m_worldMatrix = XMMatrixIdentity();

// 2D 렌더링을 위해 직교 투영 행렬을 생성한다.
m_orthoMatrix = XMMatrixOrthographicLH((float)screenWidth, (float)screenHeight, screenNear, screenDepth);

// 투영 행렬 설정
fieldOfView = 3.141592654f / 4.0f;
screenAspect = (float)screenWidth / (float)screenHeight;

// 3D렌더링을 위한 투영 행령 생성 (투영 변환시 사용)
m_projectionMatrix = XMMatrixPerspectiveFovLH(fieldOfView, screenAspect, screenNear, screenDepth);

튜토리얼에서는 뷰 변환을 위한 행렬 생성이 빠졌는데 나중에 카메라 클래스를 만들 때 생성한다.

직교 투영은 우리가 눈으로 보는 것처럼 깊이가 있지 않고 깊이가 없다고 생각하면 된다.

3. Shutdown

모든 포인터 함수의 초기화를 시도 한다 (코드 생략)

4. BeginScene & EndScene

D3DClass에는 몇 가지 도움 함수가 있다. 처음 설명할 건 BeginScene과 EndScene이다.

BeginScene은 버퍼를 초기화하여 그릴 준비가 되게끔 하고, EndScene은 모든 그림이 그려지면 스왑체인에 버퍼를 표시하도록 한다.

void D3DClass::BeginScene(float red, float green, float blue, float alpha)
{
	float color[4];

	// 매개변수로 들어온 색상으로 버퍼를 초기화 한다.
	color[0] = red;
	color[1] = green;
	color[2] = blue;
	color[3] = alpha;

	// Clear the back buffer.
	m_deviceContext->ClearRenderTargetView(m_renderTargetView, color);

	// Clear the depth buffer.
	m_deviceContext->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	return;
}


void D3DClass::EndScene()
{
	// Present the back buffer to the screen since rendering is complete.
	if (m_vsync_enabled)
	{
		// Lock to screen refresh rate.
		m_swapChain->Present(1, 0);
	}
	else
	{
		// Present as fast as possible.
		m_swapChain->Present(0, 0);
	}

	return;
}

5. GetDevice & GetDeviceContext

단순히 Direct device와 Direct device context에 대한 포인터를 가져오는 함수다. (코드 생략)

 

6. GetProjectionMatrix & GetWorldMatrix & GetOrthoMatrix

Get함수지만 매개변수가 들어간 레퍼런스로 각 공간의 행렬의 복사본을 제공해준다.

나중에 셰이더란걸 작업할때 이 행렬들이 필요하므로 미리 작성해준다. (코드 생략)

 

7. GetVideoCardInfo

그래픽 카드 이름과 VRAM양을 반환한다, 나중에 디버깅시 도움이 될 수도 있다. (코드 생략)

 

8. SetBackBufferRenderTarget & ResetViewport

우리가 했던 설정들을 함수화 했는데 나중의 튜토리얼에 다시 사용하기에 미리 함수화 했다.

void D3DClass::SetBackBufferRenderTarget()
{
	// Bind the render target view and depth stencil buffer to the output render pipeline.
	m_deviceContext->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);

	return;
}


void D3DClass::ResetViewport()
{
	// Set the viewport.
	m_deviceContext->RSSetViewports(1, &m_viewport);

	return;
}

요약

 

마침내 DirectX를 화면의 색을 지정하여 초기화하여 생성하고 종료할 수 있게 되었다.


연습

 

1. 전체화면 만들어보기

ApplicationClass.h의 전역 변수인 FULL_SCREEN을 true로 바꾸어준다.

2. 노란 화면으로 창 초기화하기

 

3. 그래픽 카드의 이름을 텍스트 파일에 저장

나는 Application.cpp의 Initilalize함수에서 D3DClass가 초기화 된 후에 저장했다.

// 1. 쓰기 전용으로 파일 열기 (파일이 없으면 생성해 연다.)
std::ofstream myTXTfile;
myTXTfile.open("Graphic_card_name.txt");

// 파일이 정상적으로 열렸는지 확인
if (myTXTfile.is_open() == true)
{
	// 2. 그래픽 카드 정보 가져와 파일에 쓰기
	char GCname[128];
	int VRAMsize = 0;
	m_Direct3D->GetVideoCardInfo(GCname, VRAMsize);
	myTXTfile << GCname;

	// 3. ofstream이 자동으로 소멸돼 알아서 닫아주지만 굳이 파일 닫기를 해줬다.
	myTXTfile.close();
}

 

저장된 파일의 위치는 해당 작업 폴더에 저장된다.


개념 정리

 

렌더 타겟 뷰 : DX가 그림을 그리는 버퍼 장착 칸, 보통 스왑체인의 백 버퍼 포인터와 연결된다.

뷰포트 : 렌더 타겟 뷰에서 그려질 영역을 정의하는 구조체

투영 공간 : 모니터 공간

뷰 공간 : 카메라 공간

월드 공간 : 게임속 세상 공간

로컬 공간 : 월드의 그려지는 각 오브젝트들의 고유 공간