DirectX11 - Vertex Buffer와 Shader

Sungsik Bae·2024년 1월 22일

DirectX11

목록 보기
4/6

Vertex Buffer

Vertex Buffer는 단어 그대로 정점을 담고 있는 buffer를 의미한다.
DirectX11에서 Polygon을 그리기 위해서는 primitive data가 필요하다.
Primitive data는 점, 선, 면을 의미한다. (도형의 최소 단위)
[공식문서]

이러한 primitive data를 렌더링 하기 위해서는 vertex, index buffer를 필요로 한다.


Vertex data

struct SimpleVertex
{
	XMFLOAT3 Pos;
};

Vertex data를 작성한다.


Vertex buffer 설정

	// create vertex buffer
	constexpr SimpleVertex vertices[] =
	{
		XMFLOAT3(0.0f, 0.5f, 0.5f),
		XMFLOAT3(0.5f, -0.5f, 0.5f),
		XMFLOAT3(-0.5f, -0.5f, 0.5f),
	};
	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(D3D11_BUFFER_DESC));
	bd.Usage = D3D11_USAGE_DEFAULT;				// default로 사용
	bd.ByteWidth = sizeof(SimpleVertex) * 3;	// vertex 3개
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;	// vertex buffer로 바인드
	bd.CPUAccessFlags = 0;						// cpu access하지 않음.

Vertex buffer를 설정한다.


Subresource 설정

	D3D11_SUBRESOURCE_DATA initData;
	ZeroMemory(&initData, sizeof(D3D11_SUBRESOURCE_DATA));
	initData.pSysMem = vertices;	// 버퍼 데이터 초기화

Subresource를 작성한다.

Vertex buffer를 생성하는 것은 이전에 OpenGL의 경험을 통해서 쉽게 이해할 수 있었는데, Subresource라는 개념은 처음 들어봐서 찾아보았다.
[참고자료] [공식문서]

Resource는 복수의 Subresource 집합이라고 볼 수 있다.
즉, Subresource는 Resource의 전체 혹은 일부분을 참조할 수 있다.

또한 공식문서를 참고해보면, Buffer는 하나의 Subresource를 가질 수 있으나, texture의 경우에는 다른 것 같다.

Texture의 경우에는 Mipmap 혹은 LOD의 경우에 여러개의 Subresource를 가질 수 있는 거 같다.


Vertex buffer 생성 및 설정

	hr = g_pDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer);
	if (FAILED(hr))
	{
		return hr;
	}

	// set vertex buffer
	UINT stride = sizeof(SimpleVertex);
	UINT offset = 0;
	g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);

	// set primitive topology
	g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

Vertex buffer를 생성 및 설정하고, primitive topology를 설정한다.


Shader 작성

float4 VS(float4 Pos : POSITION) : SV_Position
{
    return Pos;
}

float4 PS(float4 Pos : SV_Position) : SV_Target
{
    return float4(1.0f, 0.0f, 0.0f, 1.0f);
}

가장 기본적인 Vertex, Pixel 셰이더 함수이다.
SV의 의미가 뭔지 알아봤는데, System value라는 뜻이라고 한다. 렌더링 파이프라인에서 특별한 의미를 가지는 Semantics라고 한다.

Semantics는 또 무엇인가. [공식문서]

공식 문서를 읽어봤을 때, 파라미터에 특정한 의미를 부여한다고 보면 될 거 같다.
SV_Position은 pixel의 좌표를 의미하고, SV_Target은 Color를 의미하는거 같다.
(왜 SV_Color가 아닌것이지..)


ID3DBlob은 또 무엇인가

	// compile the vertex shader
	ID3DBlob* pVSBlob = nullptr;
	hr = CompileShaderFromFile(L"Tutorial02.fx", "VS", "vs_4_0", &pVSBlob);
	if (FAILED(hr))
	{
		MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.",
			L"Error", MB_OK);
		return hr;
	}

위는 Vertex shader를 컴파일 하는 코드이다.
[공식문서]
ID3DBlob은 임의의 데이터를 반환하는 데 사용되는 Interface이다.

컴퓨터 과학에서는 Blob(Binary large object)으로 줄여 말하는 듯 하다.

즉, 컴파일 된 Binary 코드를 담을 수 있는 클래스이라고 생각된다.


Shader Compile

HRESULT CompileShaderFromFile(LPCTSTR szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
{
	HRESULT hr = S_OK;

	DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
	// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
	// Setting this flag improves the shader debugging experience, but still allows 
	// the shaders to be optimized and to run exactly the way they will run in 
	// the release configuration of this program.
	dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob* pErrorBlob;
	hr = D3DCompileFromFile(
		szFileName,						// 파일명
		nullptr,						// 매크로 정의
		nullptr,						// Include 파일 정의
		szEntryPoint,					// 셰이더 Entry point 이름(메인 함수 이름)
		szShaderModel,					// 셰이더 컴파일 버전(vs_4_0 = Vertex shader version 4.0)
		dwShaderFlags,					// 컴파일 옵션
		0,								// 이펙트 컴파일 옵션
		ppBlobOut,						// 컴파일 된 Byte코드
		&pErrorBlob						// 에러 메세지
	);
	if (FAILED(hr))
	{
		if (pErrorBlob != nullptr)
		{
			OutputDebugStringA(static_cast<LPCSTR>(pErrorBlob->GetBufferPointer()));
		}
		else
		{
			pErrorBlob->Release();
		}
		return hr;
	}

	if (pErrorBlob)
	{
		pErrorBlob->Release();
	}

	return S_OK;
}

작성된 CompileShaderFromFile를 더 파보자.

첫 부분을 보면 셰이더를 컴파일 하기 위해서 옵션을 지정한다. [공식문서]

공식문서에 따르면, 굉장히 많은 옵션들이 존재하는데 D3DCOMPILE_ENABLE_STRICTNESS의 의미는 구식문법을 엄밀히 검사해 금지한다는 뜻이다.
D3DCOMPILE_DEBUG는 역시 작성된 뜻 그대로 DEBUG모드일 때를 의미한다.

D3DCompileFromFile가 실질적으로 Compile를 수행하는 함수이다. [공식문서]에 따르면 D3DCompile2를 사용하는 것을 추천하는데, 그 이유는 Window store에 출시한 앱에서 사용할 수 없다고 하는데, 지금은 공부 중인 단계이므로 D3DCompileFromFile를 사용하겠습니다 :>


Vertex Shader 생성

	// create the vertex shader
	hr = g_pDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader);
	if (FAILED(hr))
	{
		pVSBlob->Release();
		return hr;
	}

위 함수(CompileShaderFromFile)를 통해 컴파일 된 데이터가 ID3DBlob interface에 들어오게 되는데, 들어온 데이터를 통해서 Vertex Shader를 생성한다.

CreateVertexShader 함수를 알아보자. [공식문서]

다른 파라미터들은 알겠는데, pClassLinkage의 역할이 무엇인지 모르겠다.
그래서 공식문서를 뒤져보았는데.. HLSL에 동적 링크를 압축한다고 한다.

HLSL에 동적 링크(DLL)를 압축한다는 뜻이 뭔지 모르겠어서 더 찾아보았다.
그러나 찾을 수 없었다. github open source project들을 찾아봤는데 모든 곳에서 다 nullptr을 파라미터로 보내준다.

혹시 몰라서 ID3D11ClassLinkage interface가 사용되는 곳이 있는지 확인해보았는데, 없었다.

사용되지 않는 파라미터라는 결론이 나왔다. (혹시 아시는 분 있으면 댓글로 알려주세요 :>)

다음으로는 IA(Input assembly stage)에서 이 Vertex buffer의 데이터를 어떤 식으로 읽는지를 알려줘야한다.

	// define the input layout (정점 데이터를 GPU에게 알려주는 구조체)
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{"POSITION",					// Element의 목적이 무엇인이 알려주는 문자열 (POSITION, NORMAL, ...)
		0,								// 동일한 Element의 목적을 가진 정점의 Index
		DXGI_FORMAT_R32G32B32_FLOAT,	// 자료형
		0,								// 정점 버퍼 슬롯 index
		0,								// 정점 버퍼 offset
		D3D11_INPUT_PER_VERTEX_DATA,	// D3D11_INPUT_PER_VERTEX_DATA를 사용함.
		0								// Instancing에 사용됨
		},
	};
	constexpr UINT numElements = ARRAYSIZE(layout);

아직까지는 모든 멤버 변수의 역할이 정확히 무엇인지는 모르겠다. (정점 버퍼 슬롯에 관한 내용을 좀 더 공부해봐야겠다.)

	// create the input layout
	hr = g_pDevice->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
	pVSBlob->Release();
	if (FAILED(hr))
	{
		return hr;
	}

	// set the input layout
	g_pImmediateContext->IASetInputLayout(g_pVertexLayout);

설정한 구조체를 생성하고 설정하는 과정이다.


Pixel Shader 생성

	// compile the pixel shader
	ID3DBlob* pPSBlob = nullptr;
	hr = CompileShaderFromFile(L"Tutorial02.fx", "PS", "ps_4_0", &pPSBlob);
	if (FAILED(hr))
	{
		MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.",
			L"Error", MB_OK);
		return hr;
	}

	// create the pixel shader
	hr = g_pDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader);
	pPSBlob->Release();
	if (FAILED(hr))
	{
		return hr;
	}

Vertex shader를 만드는 과정에서 Compile 할 때, 메인함수 이름과 Shader 버전을 제외하고는 동일하다.

최종 결과물

#include <Windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>

//--------------------------------------------------------------------------------------
// Using namespaces
//--------------------------------------------------------------------------------------
using namespace DirectX;

//--------------------------------------------------------------------------------------
// Structures
//--------------------------------------------------------------------------------------
struct SimpleVertex
{
	XMFLOAT3 Pos;
};

//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------
HINSTANCE g_hInstance;
HWND g_hWnd;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_pRenterTargetView = nullptr;
ID3D11VertexShader* g_pVertexShader = nullptr;
ID3D11InputLayout* g_pVertexLayout = nullptr;
ID3D11PixelShader* g_pPixelShader = nullptr;
ID3D11Buffer* g_pVertexBuffer = nullptr;


//--------------------------------------------------------------------------------------
// Forward declarations
//--------------------------------------------------------------------------------------
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
HRESULT InitDevice();
void CleanupDevice();
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Render();

//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing 
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
	if (FAILED(InitWindow(hInstance, nCmdShow)))
	{
		return 0;
	}

	if (FAILED(InitDevice()))
	{
		CleanupDevice();
		return 0;
	}

	MSG msg = { nullptr };
	while (WM_QUIT != msg.message)
	{
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			Render();
		}
	}

	CleanupDevice();

	return static_cast<int>(msg.wParam);
}

HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow)
{
	// Register Window class
	WNDCLASSEX wcex;
	ZeroMemory(&wcex, sizeof(wcex));

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = reinterpret_cast<HBRUSH>((COLOR_WINDOW + 1));
	wcex.lpszMenuName = nullptr;
	wcex.lpszClassName = L"TutorialWindowClass";
	wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

	if (!RegisterClassEx(&wcex))
	{
		return E_FAIL;
	}

	g_hInstance = hInstance;
	RECT rc = { 0, 0, 640, 480 };
	AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
	g_hWnd = CreateWindowEx(NULL,
		L"TutorialWindowClass",
		L"Direct3D 11 Tutorial 1: Direct3D 11 Basics",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rc.right - rc.left,
		rc.bottom - rc.top,
		nullptr,
		nullptr,
		hInstance,
		nullptr);

	if (g_hWnd == nullptr)
	{
		return E_FAIL;
	}

	ShowWindow(g_hWnd, nCmdShow);

	return S_OK;
}

HRESULT CompileShaderFromFile(LPCTSTR szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
{
	HRESULT hr = S_OK;

	DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
	// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
	// Setting this flag improves the shader debugging experience, but still allows 
	// the shaders to be optimized and to run exactly the way they will run in 
	// the release configuration of this program.
	dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob* pErrorBlob;
	hr = D3DCompileFromFile(
		szFileName,						// 파일명
		nullptr,						// 매크로 정의
		nullptr,						// Include 파일 정의
		szEntryPoint,					// 셰이더 Entry point 이름(메인 함수 이름)
		szShaderModel,					// 셰이더 컴파일 버전(vs_4_0 = Vertex shader version 4.0)
		dwShaderFlags,					// 컴파일 옵션
		0,								// 이펙트 컴파일 옵션
		ppBlobOut,						// 컴파일 된 Byte코드
		&pErrorBlob						// 에러 메세지
	);
	if (FAILED(hr))
	{
		if (pErrorBlob != nullptr)
		{
			OutputDebugStringA(static_cast<LPCSTR>(pErrorBlob->GetBufferPointer()));
		}
		else
		{
			pErrorBlob->Release();
		}
		return hr;
	}

	if (pErrorBlob)
	{
		pErrorBlob->Release();
	}

	return S_OK;
}

HRESULT InitDevice()
{
	HRESULT hr = S_OK;

	RECT rc;
	GetClientRect(g_hWnd, &rc);
	const UINT width = rc.right - rc.left;
	const UINT height = rc.bottom - rc.top;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	constexpr UINT numDriverTypes = ARRAYSIZE(driverTypes);

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	constexpr UINT numFeatureLevels = ARRAYSIZE(featureLevels);

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = g_hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
	{
		g_driverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDeviceAndSwapChain(
			nullptr,						// device를 생성하기 위한 video adapter(nullptr = default adapter)
			g_driverType,					// driver type
			nullptr,						// software handle
			createDeviceFlags,				// device flag
			featureLevels,					// feature level array
			numFeatureLevels,				// feature level array length
			D3D11_SDK_VERSION,				// sdk version
			&sd,							// swap chain structure
			&g_pSwapChain,					// 생성된 swap chain 
			&g_pDevice,						// 생성된 device
			&g_featureLevel,				// 생성된 feature level
			&g_pImmediateContext);			// 생성된 device context

		if (SUCCEEDED(hr))
		{
			break;
		}
	}

	if (FAILED(hr))
	{
		return hr;
	}

	ID3D11Texture2D* pBackBuffer = nullptr;
	hr = g_pSwapChain->GetBuffer(
		0,													// 백버퍼 index
		__uuidof(ID3D11Texture2D),							// back buffer를 다루기 위한 interface
		reinterpret_cast<LPVOID*>(&pBackBuffer)				// back buffer interface output
	);
	if (FAILED(hr))
	{
		return hr;
	}

	hr = g_pDevice->CreateRenderTargetView(
		pBackBuffer,			// View에서 접근할 리소스
		nullptr,				// RTV 정의
		&g_pRenterTargetView);	// RTV를 받아올 변수
	// 사용한 back buffer를 Release 
	pBackBuffer->Release();
	if (FAILED(hr))
	{
		return hr;
	}

	g_pImmediateContext->OMSetRenderTargets(
		1,						// Render target의 개수(최대 8개)
		&g_pRenterTargetView,	// Render target view의 배열
		nullptr					// Depth stencil view 포인터
	);

	D3D11_VIEWPORT vp;
	vp.Width = static_cast<FLOAT>(width);
	vp.Height = static_cast<FLOAT>(height);
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	g_pImmediateContext->RSSetViewports(1, &vp);

	// compile the vertex shader
	ID3DBlob* pVSBlob = nullptr;
	hr = CompileShaderFromFile(L"Tutorial02.fx", "VS", "vs_4_0", &pVSBlob);
	if (FAILED(hr))
	{
		MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.",
			L"Error", MB_OK);
		return hr;
	}

	// create the vertex shader
	hr = g_pDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader);
	if (FAILED(hr))
	{
		pVSBlob->Release();
		return hr;
	}

	// define the input layout (정점 데이터를 GPU에게 알려주는 구조체)
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{"POSITION",					// Element의 목적이 무엇인이 알려주는 문자열 (POSITION, NORMAL, ...)
		0,								// 동일한 Element의 목적을 가진 정점의 Index
		DXGI_FORMAT_R32G32B32_FLOAT,	// 자료형
		0,								// 정점 버퍼 슬롯 index
		0,								// 정점 버퍼 offset
		D3D11_INPUT_PER_VERTEX_DATA,	// D3D11_INPUT_PER_VERTEX_DATA를 사용함.
		0								// Instancing에 사용됨
		},
	};
	constexpr UINT numElements = ARRAYSIZE(layout);

	// create the input layout
	hr = g_pDevice->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
	pVSBlob->Release();
	if (FAILED(hr))
	{
		return hr;
	}

	// set the input layout
	g_pImmediateContext->IASetInputLayout(g_pVertexLayout);

	// compile the pixel shader
	ID3DBlob* pPSBlob = nullptr;
	hr = CompileShaderFromFile(L"Tutorial02.fx", "PS", "ps_4_0", &pPSBlob);
	if (FAILED(hr))
	{
		MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.",
			L"Error", MB_OK);
		return hr;
	}

	// create the pixel shader
	hr = g_pDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader);
	pPSBlob->Release();
	if (FAILED(hr))
	{
		return hr;
	}

	// create vertex buffer
	constexpr SimpleVertex vertices[] =
	{
		XMFLOAT3(0.0f, 0.5f, 0.5f),
		XMFLOAT3(0.5f, -0.5f, 0.5f),
		XMFLOAT3(-0.5f, -0.5f, 0.5f),
	};
	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(D3D11_BUFFER_DESC));
	bd.Usage = D3D11_USAGE_DEFAULT;				// default로 사용
	bd.ByteWidth = sizeof(SimpleVertex) * 3;	// vertex 3개
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;	// vertex buffer로 바인드
	bd.CPUAccessFlags = 0;						// cpu access하지 않음.

	D3D11_SUBRESOURCE_DATA initData;
	ZeroMemory(&initData, sizeof(D3D11_SUBRESOURCE_DATA));
	initData.pSysMem = vertices;	// 버퍼 데티
	hr = g_pDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer);
	if (FAILED(hr))
	{
		return hr;
	}

	// set vertex buffer
	UINT stride = sizeof(SimpleVertex);
	UINT offset = 0;
	g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);

	// set primitive topology
	g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	return S_OK;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (uMsg)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}

	return 0;
}

void Render()
{
	constexpr float clearColor[4] = { 0.0f, 0.125f,0.3f,1.0f };
	g_pImmediateContext->ClearRenderTargetView(g_pRenterTargetView, clearColor);

	g_pImmediateContext->VSSetShader(g_pVertexShader, nullptr, 0);
	g_pImmediateContext->PSSetShader(g_pPixelShader, nullptr, 0);
	g_pImmediateContext->Draw(3, 0);

	g_pSwapChain->Present(0, 0);
}

void CleanupDevice()
{
	if (g_pImmediateContext)
	{
		g_pImmediateContext->ClearState();
	}

	if (g_pVertexBuffer)
	{
		g_pVertexBuffer->Release();
	}
	if (g_pVertexLayout)
	{
		g_pVertexLayout->Release();
	}
	if (g_pVertexShader)
	{
		g_pVertexShader->Release();
	}
	if (g_pPixelShader)
	{
		g_pPixelShader->Release();
	}
	if (g_pRenterTargetView)
	{
		g_pRenterTargetView->Release();
	}
	if (g_pSwapChain)
	{
		g_pSwapChain->Release();
	}
	if (g_pImmediateContext)
	{
		g_pImmediateContext->Release();
	}
	if (g_pDevice)
	{
		g_pDevice->Release();
	}
}
profile
Game Developer

0개의 댓글