서의 공간

3. Initializing DirectX 11 본문

Graphics API/DirectX 11 - Rastertek

3. Initializing DirectX 11

홍서의 2021. 1. 7. 06:58

이번 튜토리얼은 Direct3D를 초기화하고 종료하는 방법과 윈도우에 렌더링 하는 방법을 설명한다.

 

프레임워크

모든 Direct3D 시스템 기능을 처리 할 다른 클래스를 프레임워크에 추가한다. 이 클래스는 D3DClass라고 할 것이다. 다음은 업데이트된 프레임워크 다이어그램이다.

다이어그램을 살펴보면 D3DClass는 GraphicsClass 내부에 있다. 이제 GraphicsClass의 변경 사항을 살펴본다.


GraphicsClass.h

첫 번째 변경 사항은 Windows.h 헤더 파일을 인클루드 하는 대신에 D3DClass.h를 인클루드 했다.

두 번째 변경 사항은 D3DClass 객체의 포인터를 GraphicsClass의 멤버 변수로 추가했다.

#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_

// MY CLASS INCLUDES
#include "D3DClass.h"

// GLOBALS
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;

class GraphicsClass
{
public:
	GraphicsClass();
	GraphicsClass(const GraphicsClass&);
	~GraphicsClass();

	bool Initialize(int, int, HWND);
	void ShutDown();
	bool Frame();

private:
	bool Render();

private:
	D3DClass* mDirect3D;
};
#endif

 

GraphicsClass.cpp

이전 튜토리얼에서 이 클래스 소스파일은 비어있었다. 이제 D3DClass 멤버가 있으므로 GraphicsClass 내부에 D3DClass 객체를 초기화하고 종료하는 코드를 작성한다. 또한 Render() 함수에서 BeginScene()및 EndScene() 함수를 호출하면 이제 Direct3D를 사용하여 윈도우에 그릴 것이다.

생성자에서는 mDirect3D을 nullptr로 초기화한다.

Initialize() 함수에서는 D3DClass 객체를 생성한 다음 초기화한다. D3DClass::Initialize() 함수를 호출할 때 화면 너비, 화면 높이, 윈도우 핸들 그리고 GraphicsClass.h에 정의된 4개의 전역 변수를 파라미터로 전달한다. D3DClass는 이 모든 파라미터를 사용하여 Direct3D 시스템을 설정한다.

Shutdown() 함수는 모든 그래픽 객체의 종료가 여기서 발생하므로 D3DClass 객체 역시 이 함수에서 종료한다. 포인터 변수가 유효한 경우만 종료와 해제를 시도한다. 따라서 포인터가 유효하지 않은 경우 종료하지 않을 수 있으며 이것이 바로 클래스 생성자에서 nullptr로 초기화하는 중요한 이유다.

Frame() 함수는 매 프레임마다 Render() 함수를 호출한다.

Render() 함수는 mDirect3D 객체를 통해 화면을 주어진 색상으로 지우고(여기서 지운다는 표현은 주어진 색상으로 화면을 채운다는 의미다), EndScene() 함수를 호출하여 최종적으로 윈도우 화면에 보여준다.

#include "GraphicsClass.h"

GraphicsClass::GraphicsClass()
{
	mDirect3D = nullptr;
}

GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}

GraphicsClass::~GraphicsClass()
{
}

bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
	bool result;

	// Direct3D 객체 생성.
	mDirect3D = new D3DClass;
	if (!mDirect3D) return false;

	// Direct3D 객체 초기화.
	result = mDirect3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
	if (!result)
	{
		MessageBox(hwnd, L"Could nor initialize Direct3D", L"Error", MB_OK);
		return false;
	}

	return true;
}

void GraphicsClass::ShutDown()
{
	// Direct3D 객체 해제.
	if (mDirect3D)
	{
		mDirect3D->Shutdown();
		delete mDirect3D;
		mDirect3D = 0;
	}

	return;
}

bool GraphicsClass::Frame()
{
	bool result;

	// 그래픽 씬을 렌더링한다.
	result = Render();
	if (!result) return false;

	return true;
}

bool GraphicsClass::Render()
{
	// 처음 씬이 시작할 때 버퍼를 클리어한다.
	mDirect3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

	// 화면에 씬을 표시한다.
	mDirect3D->EndScene();

	return true;
}

D3DClass.h

헤더에서 가장 먼저 할 일은 연결할 라이브러리를 지정하는 것이다. 첫 번째 라이브러리인 d3d11.lib는 DirectX 11에서 3D 그래픽을 설정하고 그리기 위한 모든 Direct3D 기능을 포함한다. 두 번째 라이브러리인 dxgi.lib에는 모니터의 주사율, 사용 중인 비디오 카드(그래픽 카드) 등의 정보를 얻기 위한 하드웨어 인터페이스 도구를 포함한다. 세 번째 라이브러리인 d3dcompiler.lib는 셰이더 컴파일 기능을 포함한다.

다음으로 할 일은 위 라이브러리의 헤더와 DirectX의 수학 관련 헤더를 포함하는 것이다.

D3DClass의 클래스는 가능한 한 단순하게 정의한다. 여러 함수 중 Initialize() 함수와 Shutdown() 함수가 핵심인데, 각각 D3DClass 객체를 초기화하고 종료하는 함수이다. 이 튜토리얼에서는 해당 두 함수의 기능에 집중한다.

이미 Direct3D에 어느 정도 익숙하다면 해당 클래스에 뷰 행렬 변수가 없음을 눈치챘을 것이다. 해당 변수는 향후 카메라 클래스에 넣을 예정이기 때문이다.

#ifndef _D3DCLASS_H_
#define _D3DCLASS_H_

// LINKING
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")

// INCLUDES
#include <d3d11.h>
#include <DirectXMath.h>
using namespace DirectX;

class D3DClass
{
public:
	D3DClass();
	D3DClass(const D3DClass&);
	~D3DClass();

	bool Initialize(int, int, bool, HWND, bool, float, float);
	void Shutdown();

	void BeginScene(float, float, float, float);
	void EndScene();

	ID3D11Device* GetDevice();
	ID3D11DeviceContext* GetDeviceContext();

	void GetProjectionMatrix(XMMATRIX&);
	void GetWorldMatrix(XMMATRIX&);
	void GetOrthoMatrix(XMMATRIX&);

	void GetVideoCardInfo(char*, int&);

private:
	bool bVsyncEnabled;
	int mVideoCardMemory;
	char mVideoCardDescription[128];
	IDXGISwapChain* mSwapChain;
	ID3D11Device* mDevice;
	ID3D11DeviceContext* mDeviceContext;
	ID3D11RenderTargetView* mRenderTargetView;
	ID3D11Texture2D* mDepthStencilBuffer;
	ID3D11DepthStencilState* mDepthStencilState;
	ID3D11DepthStencilView* mDepthStencilView;
	ID3D11RasterizerState* mRasterState;
	XMMATRIX mProjectionMatrix;
	XMMATRIX mWorldMatrix;
	XMMATRIX mOrthoMatrix;
};
#endif

 

D3DClass.cpp

생성자에서는 모든 포인터 변수를 nullptr로 초기화한다. 다음 튜토리얼에서부터는 아주 특별한 변경이 없는 한, 기본적으로 수행해야 할 일들을 수행할 뿐이라면(초기화와 해제), 생성자와 소멸자의 언급은 생략하겠다. 

Initialize() 함수는 Direct3D의 전체 설정을 수행한다. 이 함수에 주어진 screenWidth 및 screenHeight 변수는 SystemClass에서 만든 윈도우의 너비와 높이다. Direct3D는 이 값을 사용하여 동일한 윈도우 크기를 초기화 하고 사용한다. hwnd 변수는 윈도우의 핸들인데, Direct3D가 앞서 만든 윈도우에 액세스하려면 핸들이 필요하기 때문이다. 화면 모드 변수는 창 모드, 전체 화면 모드를 구분한다. screenDepth 및 screenNear 변수는 윈도우에 렌더링 될 3D 환경의 깊이 설정 변수이다. vsync(수직동기화) 변수는 주사율을 어떻게 설정할지 결정하는 변수이다. 사용자의 모니터 주사율을 따를지 아니면 하드웨어가 허락하는 최대의 주사율을 설정할지 vsync 변수로 결정한다.

 

Initialize() 함수에서 본격적으로 Direct3D를 초기화하기 전에 비디오 카드/모니터에서 주사율 값을 얻어와야 한다. 이 값은 컴퓨터마다 다르므로 해당 정보를 질의하여(쿼리) 얻어와야 한다. 분모와 분자값을 얻어와 DirectX로 전달하면 적절한 주사율이 계산된다. 만약 이렇게 하지 않고 주사율 값을 기본값으로 설정한다면 DirectX가 버퍼 플립 대신 블릿(blit)을 실행하는 것으로 대응하여 성능을 저하시키고 디버그 출력에서 성가신 오류를 출력한다.

주사율의 분자와 분모값을 얻어왔다면, 어댑터를 사용하여 비디오 카드의 이름과 비디오 카드의 메모리 크기를 얻어온다.

주사율과 비디오카드의 정보를 모두 얻어왔다면 해당 구조체와 인터페이스들은 더 이상 사용하지 않으므로 해제한다.

그 다음 본격적인 DirectX 초기화를 시작한다. 가장 먼저 할 일은 swap chain의 description을 작성하는 것이다. 이 스왑 체인은 그래픽이 그려질 프론트 및 백 버퍼이다. 일반적으로 백 버퍼는 한 개만 사용하고 모든 그리기를 백 버퍼에 그리고 사용자 모니터 화면에 표시되는 프론트 버퍼와 교체한다. 따라서 백 버퍼와 프론트 버퍼는 계속해서 스왑되므로 스왑체인이라고 하는 것이다.

결국 주사율이라는 것은 바로 1초에 백 버퍼가 프론트 버퍼로 몇 번이나 스왑되는가이다. GraphicsClass.h 헤더에 vsync 변수가 true로 설정되면 주사율은 모니터 설정(예: 60hz)에 고정된다. 즉, 1초에 60번만 화면을 새로 그린다. 그러나 vsync를 false로 설정하면 하드웨어가 허용하는 한 1초에 더 많은 횟수로 화면에 그릴 수 있다. 하지만 이로 인해 여러 visual artifacts(부적절한 영상 샘플링의 결과로 계산기에서 만들어지는 화상의 결함, 특히 상의 경계선이 톱니 모양으로 되는 것. 에일리어스와 동의어이지만 영상처리 분야에서 사용되고 있다.- 출처: 네이버 사전)가 발생한다.

스왑 체인의 description을 설정 한 후 기능 수준(feature level)이라는 변수를 하나 더 설정해야 한다. 이 변수는 DirectX에 우리가 사용할 DirectX의 버전을 알려준다. 이 튜토리얼에서는 11.0 버전으로 설정한다. DirectX 10 또는 DirectX 9 버전을 사용하려면 이 값을 10 또는 9로도 설정하면 된다.

스왑 체인의 description과 기능 수준을 설정했다면 이제 스왑 체인과, Direct3D device및 Direct3D device context를 만들 수 있다. device와 device context는 모든 Direct3D 기능에 대한 인터페이스이므로 매우 중요하다.

사용자의 컴퓨터에 DirectX 11를 지원하지 않는 비디오카드가 없는 경우 device와 device context 인터페이스를 만들지 못한다. 그런 경우 D3D_DRIVER_TYPE_HARDWARE 값을 D3D_DRIVER_TYPE_REFERENCE로 바꾸면 DirectX가 비디오 카드 대신 CPU를 사용하여 그릴 수 있게 된다. 다만 기존 대비 1/1000 정도의 속도로 실행되는 단점이 있다.

비디오 카드가 있음에도 DirectX 11의 기능을 지원하지 않는 비디오 카드라면 device 생성이 실패할 수 있다(역주: 2021년 현재 시점에서 DirectX 11을 지원하지 않는 비디오 카드는 거의 없지만, 원글 작성시점이 상당히 오래 전이다). 또는 일부 컴퓨터에는 기본 비디오 카드가 DirectX 10 비디오 카드로, 보조 비디오 카드가 DirectX 11 비디오 카드로 되어 있을 수 있다. 또한 일부 하이브리드 그래픽 카드는 기본이 저전력 Intel 카드이고 보조가 고전력 NVidia 카드인 방식으로 동작한다. 이 문제를 해결하려면 기본값으로 설정된 비디오 카드를 사용하지 않는 대신 현재 컴퓨터의 모든 비디오 카드를 나열한 후 사용자가 직접 사용할 카드를 지정하여 device를 만들도록 해야한다.

스왑 체인을 만들었다면 백 버퍼의 포인터를 가져와 스왑 체인에 연결한다. CreateRenderTargetView 함수를 이용하여 백 버퍼를 스왑 체인에 연결한다.

다음은 깊이 버퍼를 만들기 위해 description을 설정한다. 폴리곤이 3D공간에서 제대로 렌더링 되려면 깊이 버퍼가 필요하다. 깊이 버퍼를 만듦과 동시에 스텐실 버퍼도 연결한다. 스텐실 버퍼는 모션 블러, 볼륨 섀도우 등의 효과를 얻는 데 사용한다.

깊이 버퍼의 description을 작성한 후 깊이/스텐실 버퍼를 만든다. CreateTexture2D() 함수를 사용하여 버퍼를 만드는데, 버퍼는 2D 텍스처일 뿐이라는 것을 의미한다. 이러한 이유는 폴리곤이 정렬된 다음 래스터화된 면이 2D 버퍼에서 색상이 지정된 픽셀이 되기 때문이다. 그런 다음 주어진 2D 버퍼가 화면에 그려진다.

다음은 depth/stencil state를 만들기 위해 description를 작성한다. 이 인터페이스를 통해 Direct3D가 각 픽셀에 대해 깊이 값 테스트를 제어할 수 있다. 생성된 depth/stencil state 사용하여 효과가 나타나도록 파이프라인의 Input Assembler 단계에 설정한다.

그 다음 depth/stencil buffer view를 만들기 위해 description을 설정한다. Direct3D가 깊이 버퍼를 깊이 스텐실 텍스처로 사용하는 것을 알기 위해 이렇게 한다. description을 작성 후 CreateDepthStencilView () 함수를 호출하여 생성한다. 그리고 이것을 파이프라인의 Output merge단계에 바인딩한다. 이렇게 하면 파이프라인이 렌더링하는 결과물이 백 버퍼에 그려진다.

이제 render target이 설정되었으므로 향후 튜토리얼에서 장면을 더 잘 제어 할수 있는 몇 가지 추가 기능을 계속 진행 할 수 있다. 첫 번째로 생성할 것은 rasterizer state이다. 이것은 폴리곤이 렌더링되는 방식을 제어 할 수 있다. 씬을 와이어 프레임 모드로 렌더링 하거나 DirectX가 폴리곤의 앞면과 뒷면을 모두 그리도록(culling)하는 작업을 수행한다. 기본적으로 DirectX에는 이미 rasterize state가 설정되어있고 그 기본값은 주어진 코드와 동일하다.

Direct3D가 클립 공간 좌표를 렌더링 대상 공간에 매핑 할 수 있도록 뷰포트도 설정해야 한다. 뷰포트는 앞서 만든 윈도의 전체 크기(당연하게도 모니터 화면이 아닌)로 설정한다.

그다음 프로젝션(또는 투영) 행렬을 만든다. 프로젝션 행렬은 3D 장면을 이전에 만든 2D 뷰포트 공간으로 변환한다. 씬을 렌더링하는데 사용할 셰이더에 전달할 수 있도록 이 행렬의 복사본을 저장한다.

또한 월드 행렬도 만드는데, 이 행렬은 3D 장면에서 오브젝트의 정점을 로컬 좌표를 월드 공간의 좌표로 변환하는데 사용한다. 이 행렬은 오브젝트의 회전, 이동 및 크기를 조정할 때도 사용한다. 월드 행렬은 처음에 단위 행렬도 초기화하고 복사본을 저장한다. 이 복사본은 렌더링을 위해 셰이더로 전달될 것이다.

뷰 행렬도 만들어야 하는데, 뷰 행렬은 우리가 실제 화면에 보여질 위치를 계산하는데 사용한다. 카메라라고 생각할 수 있으며 이 카메라를 통해서만 씬을 볼 수 있다. 나중에 카메라 클래스를 따로 만들 것이고 현재는 일단 건너 뛸 것이다.

마지막으로 설정할 것은 직교투영행렬이다. 이 행렬은 화면에서 사용자 인터페이스(UI)와 같은 2D 요소를 렌더링 하는데 사용되므로 3D 렌더링을 하지 않아도 된다. 나중에 2D 그래픽과 글자들을 화면에 렌더링하는 튜토리얼에서 사용할 예정이다.

 

Shutdown() 함수는 Initialize 함수에 사용한 모든 객체의 포인터를 해제하고 정리한다. 포인터를 해제하기 전에 스왑 체인을 창 모드로 먼저 전환하도록 하는 것을 확인하자. 창 모드로 전환하지 않고 곧바로 전체 화면에서 스왑 체인을 해제하려고하면 몇 가지 예외가 발생한다. 따라서 Direct3D를 종료하기전에는 항상 창 모드로 강제 전환한 후 종료한다.

 

D3DClass에는 몇 가지 도우미 함수가 있다. 처음 두 개는 BeginScene()과 EndScene() 함수이다. BeginScene()은 각 프레임의 시작 부분에 새로운 3D 씬을 그릴 때마다 호출된다. 이 함수가 하는 일은 버퍼를 주어진 색상으로 지워 그려질 준비를 한다. EndScene() 함수는 각 프레임의 끝에서 완료된 모든 드로잉을 스왑 체인에 표시한다.

 

세 개의 도우미 함수 GetWorldMatrix(), GetOrthMatrix, GetProjectionMatrix() 함수는 모두 출력 파라미터를 받는다. 파라미터를 통해 복사된 행렬 값을들 반환한다.

 

마지막 도우미 함수 GetVideoCardInfo() 함수는 비디오 카드의 이름과 비디오 카드의 메모리 크기를 반환하는 함수이다. 

#include "D3DClass.h"
#include <iostream>
#include <fstream>

D3DClass::D3DClass()
{
	mSwapChain = nullptr;
	mDevice = nullptr;
	mDeviceContext = nullptr;
	mRenderTargetView = nullptr;
	mDepthStencilBuffer = nullptr;
	mDepthStencilState = nullptr;
	mDepthStencilView = nullptr;
	mRasterState = nullptr;
}

D3DClass::D3DClass(const D3DClass &)
{
}

D3DClass::~D3DClass()
{
}

bool D3DClass::Initialize(int screenWidth, int screenHeight, bool vsync, HWND hwnd, bool fullScreen, float screenDepth, float screenNear)
{
	HRESULT result;
	IDXGIFactory* factory;
	IDXGIAdapter* adapter;
	IDXGIOutput* adapterOutput;
	unsigned int numModes, i, numerator, denominator;
	unsigned long long stringLength;
	DXGI_MODE_DESC* displayModeList;
	DXGI_ADAPTER_DESC adapterDesc;
	int error;
	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	D3D_FEATURE_LEVEL featureLevel;
	ID3D11Texture2D* backBufferPtr;
	D3D11_TEXTURE2D_DESC depthBufferDesc;
	D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
	D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
	D3D11_RASTERIZER_DESC rasterDesc;
	D3D11_VIEWPORT viewport;
	float fieldOfView, screenAspect;

	// vsync(수직동기화) 설정 저장.
	bVsyncEnabled = vsync;

	// DirectX 그래픽스 인터페이스인 IDXGIFactory 생성.
	result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
	if (FAILED(result)) return false;

	// Factory 객체를 사용하여 기본으로 설정된 그래픽스 인터페이스를 위한 어댑터 생성(비디오 카드를 말함).
	result = factory->EnumAdapters(0, &adapter);
	if (FAILED(result)) return false;

	// 기본으로 설정된 어댑터 출력(모니터)를 열거한다.
	result = adapter->EnumOutputs(0, &adapterOutput);
	if (FAILED(result)) return false;

	// 어댑터 출력(모니터)의 DXGI_FORMAT_R8G8B8A8_UNORM 화면 포맷에 맞는 모드의 개수를 구한다. 
	result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 
		DXGI_ENUM_MODES_INTERLACED, &numModes, nullptr);
	if (FAILED(result)) return false;

	// 해당 모니터/비디오 카드 조합으로 가능한 모든 디스플레이 모드의 리스트 생성.
	displayModeList = new DXGI_MODE_DESC[numModes];
	if (!displayModeList) return false;

	// 화면 모드 리스트를 채운다.
	result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, 
		&numModes, displayModeList);
	if (FAILED(result)) return false;

	// 이제 모든 디스플레이 모드를 살펴보고 화면 너비 및 높이와 일치하는 모드를 찾는다.
	// 일치하는 항목을 찾으면 해당 모니터 주사율의 분모값과 분자값을 저장한다.
	for (i = 0; i < numModes; i++)
	{
		if (displayModeList[i].Width == (unsigned int)screenWidth)
		{
			if (displayModeList[i].Height == (unsigned int)screenHeight)
			{
				numerator = displayModeList[i].RefreshRate.Numerator;
				denominator = displayModeList[i].RefreshRate.Denominator;
			}
		}
	}
	// 어댑터의 description을 얻어온다.
	result = adapter->GetDesc(&adapterDesc);
	if (FAILED(result)) return false;
	
	// 비디오 카드 메모리를 MB단위로 저장.
	// 정보가 제대로 저장되지 않는다면 주석한 다음 줄의 함수를 사용해 볼 것.
	mVideoCardMemory = (unsigned int)adapterDesc.DedicatedVideoMemory / 1024 / 1024;
	//mVideoCardMemory = (unsigned int)adapterDesc.DedicatedSystemMemory / 1024 / 1024;

	// 비디오 카드의 이름을 배열에 저장.
	error = wcstombs_s(&stringLength, mVideoCardDescription, 128, adapterDesc.Description, 128);
	if (error != 0) return false;

	// 디스플레이 모드 리스트를 해제.
	delete[] displayModeList;
	displayModeList = 0;
	// adapterOutput 객체 해제.
	adapterOutput->Release();
	adapterOutput = 0;
	// adapter 객체 해제.
	adapter->Release();
	adapter = 0;
	// Factory 객체 해제.
	factory->Release();
	factory = 0;

	// Initialize the swap chain description.
	ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
	// Set to a single back buffer.
	swapChainDesc.BufferCount = 1;
	// Set the width and height of the back buffer.
	swapChainDesc.BufferDesc.Width = screenWidth;
	swapChainDesc.BufferDesc.Height = screenHeight;
	// Set regular 32-bit surface for the back buffer.
	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	// Set the refresh rate of the back buffer.
	if (bVsyncEnabled)
	{
		swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
		swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
	}
	else
	{
		swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
		swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	}
	// Set the usage of the back buffer.
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	// Set the handle of the window to render to.
	swapChainDesc.OutputWindow = hwnd;
	// Turn multisampling off.
	swapChainDesc.SampleDesc.Count = 1;
	swapChainDesc.SampleDesc.Quality = 0;
	// Set to full screen or windowed mode.
	if (fullScreen)
	{
		swapChainDesc.Windowed = false;
	}
	else
	{
		swapChainDesc.Windowed = true;
	}
	// Set the scan line ordering and scaling to unspecified.
	swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
	// Discard the back buffer contents after presenting.
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
	// Don't set the advanced flags.
	swapChainDesc.Flags = 0;

	// Set the feature level to DirectX 11.
	featureLevel = D3D_FEATURE_LEVEL_11_0;
	// Create the swap chain, Direct3D device, and Direct3D device context.
	result = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, &featureLevel, 1,
		D3D11_SDK_VERSION, &swapChainDesc, &mSwapChain, &mDevice, nullptr, &mDeviceContext);
	if (FAILED(result)) return false;

	// Get the pointer to the back buffer.
	result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
	if (FAILED(result)) return false;
	
	// Create the render target view with the back buffer pointer.
	result = mDevice->CreateRenderTargetView(backBufferPtr, nullptr, &mRenderTargetView);
	if (FAILED(result)) return false;
	
	// Release pointer to the back buffer as we no longer need it.
	backBufferPtr->Release();
	backBufferPtr = 0;
		
	// Initialize the description of the depth buffer.
	ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));
	// Set up the description of the depth buffer.
	depthBufferDesc.Width = screenWidth;
	depthBufferDesc.Height = screenHeight;
	depthBufferDesc.MipLevels = 1;
	depthBufferDesc.ArraySize = 1;
	depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthBufferDesc.SampleDesc.Count = 1;
	depthBufferDesc.SampleDesc.Quality = 0;
	depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthBufferDesc.CPUAccessFlags = 0;
	depthBufferDesc.MiscFlags = 0;
	// Create the texture for the depth buffer using the filled out description.
	result = mDevice->CreateTexture2D(&depthBufferDesc, nullptr, &mDepthStencilBuffer);
	if (FAILED(result)) return false;
	
	// Initialize the description of the stencil state.
	ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
	// Set up the description of the stencil state.
	depthStencilDesc.DepthEnable = true;
	depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
	depthStencilDesc.StencilEnable = true;
	depthStencilDesc.StencilReadMask = 0xFF;
	depthStencilDesc.StencilWriteMask = 0xFF;
	// Stencil opertations if pixel is front-facing.
	depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	// Stencil operations if pixel is back-facing.
	depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
	depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	// Create the depth stencil state.
	result = mDevice->CreateDepthStencilState(&depthStencilDesc, &mDepthStencilState);
	if (FAILED(result)) return false;
	
	// Set the depth stencil state.
	mDeviceContext->OMSetDepthStencilState(mDepthStencilState, 1);

	// Initialize the depth stencil view.
	ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
	// Set up the depth stencil view description.
	depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	depthStencilViewDesc.Texture2D.MipSlice = 0;
	// Create the depth stencil view.
	result = mDevice->CreateDepthStencilView(mDepthStencilBuffer, &depthStencilViewDesc, &mDepthStencilView);
	if (FAILED(result)) return false;

	// Bind the render target view and depth stencil buffer to the output render pipeline.
	mDeviceContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);

	// Setup the raster description which will determine how and what polygons will be drawn.
	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;
	// Create the rasterizer state from the description we just filled out.
	result = mDevice->CreateRasterizerState(&rasterDesc, &mRasterState);
	if (FAILED(result)) return false;
	
	// Now set the rasterizer state.
	mDeviceContext->RSSetState(mRasterState);

	// Set up the viewport for rendering.
	viewport.Width = (float)screenWidth;
	viewport.Height = (float)screenHeight;
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;
	viewport.TopLeftX = 0.0f;
	viewport.TopLeftY = 0.0f;
	// Create the viewport.
	mDeviceContext->RSSetViewports(1, &viewport);
	
	// Set up the projection matrix.
	fieldOfView = 3.141592654f / 4.0f;
	screenAspect = (float)screenWidth / (float)screenHeight;
	// Create the projection matrix for 3D rendering.
	mProjectionMatrix = XMMatrixPerspectiveFovLH(fieldOfView, screenAspect, screenNear, screenDepth);

	// Initialize the world matrix to the identity matrix.
	mWorldMatrix = XMMatrixIdentity();
	
	// Create an orthographic projection matrix for 2D rendering.
	mOrthoMatrix = XMMatrixOrthographicLH((float)screenWidth, (float)screenHeight, screenNear, screenDepth);

	{	// 애플리케이션을 실행할 때 그래픽카드의 정보를 txt파일로 저장한다.
    	// 이 튜토리얼 이후로 사용하지 않으므로 정보를 확인하고 해당 부분의 코드는 삭제한다.
		char strOut[128];
		unsigned int memorySize = 0;
		GetVideoCardInfo(strOut, memorySize);
		std::ofstream out("Video Card Information.txt");
		out << "모델명: " << strOut << std::endl;
		out << "메모리 크기: " << memorySize << "MB" << std::endl;
	}

	return true;
}

void D3DClass::Shutdown()
{
	// Before shutting down set to windowed mode or when you release the swap chain it will throw an exception.
	if (mSwapChain)
	{
		mSwapChain->SetFullscreenState(false, nullptr);
	}
	if (mRasterState)
	{
		mRasterState->Release();
		mRasterState = 0;
	}
	if (mDepthStencilView)
	{
		mDepthStencilView->Release();
		mDepthStencilView = 0;
	}
	if (mDepthStencilState)
	{
		mDepthStencilState->Release();
		mDepthStencilState = 0;
	}
	if (mDepthStencilBuffer)
	{
		mDepthStencilBuffer->Release();
		mDepthStencilBuffer = 0;
	}
	if (mRenderTargetView)
	{
		mRenderTargetView->Release();
		mRenderTargetView = 0;
	}
	if (mDeviceContext)
	{
		mDeviceContext->Release();
		mDeviceContext = 0;
	}
	if (mDevice)
	{
		mDevice->Release();
		mDevice = 0;
	}
	if (mSwapChain)
	{
		mSwapChain->Release();
		mSwapChain = 0;
	}

	return;
}

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

	// Setup the color to clear the buffer to.
	color[0] = red;
	color[1] = green;
	color[2] = blue;
	color[3] = alpha;
	// Clear the back buffer.
	mDeviceContext->ClearRenderTargetView(mRenderTargetView, color);
	// Clear the depth buffer.
	mDeviceContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	return;
}

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

	return;
}

ID3D11Device * D3DClass::GetDevice()
{
	return mDevice;
}

ID3D11DeviceContext * D3DClass::GetDeviceContext()
{
	return mDeviceContext;
}

void D3DClass::GetProjectionMatrix(XMMATRIX & projectionMatrix)
{
	projectionMatrix = mProjectionMatrix;
	return;
}

void D3DClass::GetWorldMatrix(XMMATRIX & worldMatrix)
{
	worldMatrix = mWorldMatrix;
	return;
}

void D3DClass::GetOrthoMatrix(XMMATRIX & orthoMatrix)
{
	orthoMatrix = mOrthoMatrix;
	return;
}

void D3DClass::GetVideoCardInfo(char * cardName, unsigned int & memory)
{
	strcpy_s(cardName, 128, mVideoCardDescription);
	memory = mVideoCardMemory;
	return;
}

실행화면

'Graphics API > DirectX 11 - Rastertek' 카테고리의 다른 글

5. Texturing  (0) 2021.01.07
4. Buffers, Shaders, and HLSL  (0) 2021.01.07
2. Creating a Framework and Window  (0) 2021.01.07
1. Setting up DirectX 11 with Visual Studio  (0) 2021.01.07
DirectX 11 Framework - Rastertek  (0) 2021.01.02
Comments