그래픽

Direct3D (12) / 큐브(도형) 생성

murlocdev 2026. 8. 11. 20:55

 

큐브 구현

 

이번에는 삼각형 대신 입체적인 큐브를 구현해보려고 한다

삼각형과 달리 큐브는 6개의 면을 가지고 있지만 이를 삼각형으로 구현하면 12개의 삼각형이 필요하고

단순히 계산하면 12*3 = 36개의 버텍스가 필요하게 된다

하지만 큐브는 8개의 버텍스만 가지고 구현하게 되는데

동일한 버텍스를 여러 삼각형에서 재사용 하기 위해 필요한것이 인덱스 버퍼이다

 

먼저 큐브의 버텍스들을 만들어준다

Vertex vertices[8] =
{
	{ -0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f },
	{  0.5f,  0.5f, -0.5f, 0.0f, 1.0f, 0.0f },
	{  0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f },
	{ -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f },

	{ -0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 1.0f },
	{  0.5f,  0.5f,  0.5f, 0.0f, 1.0f, 1.0f },
	{  0.5f, -0.5f,  0.5f, 0.0f, 1.0f, 1.0f },
	{ -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f }
};

 

이제 인덱스 버퍼를 전역변수로 추가해야 한다

ID3D11Buffer* g_IndexBuffer = nullptr;

 

그리고 인덱스 버퍼에 들어갈 인덱스를 만들어야 한다

버텍스를 3가지씩 묶어서 삼각형을 만들어야 한다

unsigned int indices[] =
{
	0, 1, 2,
	0, 2, 3,

	4, 6, 5,
	4, 7, 6,

	4, 0, 3,
	4, 3, 7,

	1, 5, 6,
	1, 6, 2,

	4, 5, 1,
	4, 1, 0,

	3, 2, 6,
	3, 6, 7
};

 

이제 렌더링하기 전에 InitGeometry 함수에서 인덱스 버퍼를 생성해야한다

DXGI_FORMAT_R32_UINT는 Index 하나를 32비트 Unsigned integer로 해석한다는 의미이다

D3D11_BUFFER_DESC ibd = {};						
	ibd.Usage = D3D11_USAGE_DEFAULT;			
	ibd.ByteWidth = sizeof(indices);				
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;		

	D3D11_SUBRESOURCE_DATA indexData = {};			
	indexData.pSysMem = indices;					

	HRESULT ihr = g_Device->CreateBuffer(			
		&ibd,										
		&indexData,									
		&g_IndexBuffer);							

	if (FAILED(ihr))								
	{
		return false;
	}

 

Render함수에서 인덱스 버퍼를 Input Assembler에 바인딩해야한다

	g_Context->IASetIndexBuffer(			
		g_IndexBuffer,							
		DXGI_FORMAT_R32_UINT,					
		0);

 

DrawCube 함수를 생성한다

DrawIndexed 함수의 매개변수는 사용할 인덱스 개수, 시작 인덱스의 위치, 버텍스 버퍼의 시작 위치를 의미한다

void DrawCube(XMMATRIX world, XMMATRIX view, XMMATRIX projection)	
{
	ConstantBuffer cb;									

	XMStoreFloat4x4(									
		&cb.World,										
		XMMatrixTranspose(world));						

	XMStoreFloat4x4(									
		&cb.View,										
		XMMatrixTranspose(view));						

	XMStoreFloat4x4(									
		&cb.Projection,									
		XMMatrixTranspose(projection));					

	g_Context->UpdateSubresource(						
		g_ConstantBuffer,								
		nullptr,										
		&cb,											
		0,												
		0);												

	g_Context->VSSetConstantBuffers(					
		0,												
		&g_ConstantBuffer);								

	g_Context->DrawIndexed(						
		36,										
		0,										
		0);										
}

 

Render에서 삼갹형을 그리던걸 큐브로 고쳐준다

	XMMATRIX world =							
		XMMatrixScaling(						
			1.0f,								
			1.0f,								
			1.0f) *								
		XMMatrixRotationX(g_Angle) *			
		XMMatrixRotationY(g_Angle) *			
		XMMatrixRotationZ(g_Angle) *			
		XMMatrixTranslation(					
			0.0f,								
			0.0f,								
			0.0f);								

	XMMATRIX projection =						
		XMMatrixPerspectiveFovLH(				
			XMConvertToRadians(60.0f),			
			1280.0f / 720.0f,					
			0.1f,								
			20.0f);								

	DrawCube(world, view,projection);

 

마지막으로 프로그램 종료시 인덱스 버퍼의 해제를 위해 Cleanup 함수를 수정한다

if (g_IndexBuffer) g_IndexBuffer->Release();

 

이로써 8개의 버텍스를 여러 삼각형에서 재사용하면서 12개의 삼각형을 구성할 수 잇기 때문에 버텍스 데이터를 36개까지 중복해서 저장할 필요가 없어졌다

 

더보기
#include <Windows.h>									// win32 api 헤더
#include <d3d11.h>										// dx11 헤더
#include <dxgi.h>										// dxgi 헤더
#include <d3dcompiler.h>								// 쉐이더 컴파일러 헤더
#include <DirectXMath.h>								// DirectX 수학 라이브러리 헤더
#include <algorithm>									// std::clamp를 사용하기 위한 헤더

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

using namespace DirectX;								// DirectX 수학 라이브러리 네임스페이스

ID3D11Device* g_Device = nullptr;						// GPU 장치
ID3D11DeviceContext* g_Context = nullptr;				// GPU 컨텍스트
IDXGISwapChain* g_SwapChain = nullptr;					// 화면에 그려질 버퍼를 관리하는 객체 (더블 버퍼링 구현에 사용)
ID3D11RenderTargetView* g_RTV = nullptr;				// 렌더 타겟 뷰 (화면에 그려질 버퍼를 GPU가 인식할 수 있도록 하는 객체)
ID3D11Buffer* g_VertexBuffer = nullptr;					// 정점 버퍼 (3D 모델의 정점 데이터를 GPU에 저장하는 객체)
ID3D11VertexShader* g_VertexShader = nullptr;			// 정점 쉐이더 (3D 모델의 정점 데이터를 처리하는 GPU 프로그램)
ID3D11PixelShader* g_PixelShader = nullptr;				// 픽셀 쉐이더 (화면에 그려질 픽셀의 색상을 계산하는 GPU 프로그램)
ID3D11InputLayout* g_InputLayout = nullptr;				// 입력 레이아웃 (GPU가 정점 데이터를 어떻게 해석할지 정의하는 객체)

ID3D11Buffer* g_ConstantBuffer = nullptr;				// 상수 버퍼 (쉐이더에서 사용할 상수 데이터를 GPU에 저장하는 객체)
ID3D11Texture2D* g_DepthStencilBuffer = nullptr;		// 깊이 스텐실 버퍼 (깊이 테스트와 스텐실 테스트를 위한 버퍼)
ID3D11DepthStencilView* g_DSV = nullptr;				// 깊이 스텐실 뷰 (깊이 스텐실 버퍼를 GPU가 인식할 수 있도록 하는 객체)
ID3D11Buffer* g_IndexBuffer = nullptr;					// 인덱스 버퍼 (3D 모델의 정점 데이터를 인덱스로 참조하여 그리는 객체)

float g_Angle = 0.0f;									// 회전 각도 (삼각형 회전을 구현하기 위해 사용할 변수)

bool g_IsRightMouseDown = false;						// 마우스 오른쪽 버튼이 눌렸는지 여부를 저장하는 변수 (카메라 회전을 구현하기 위해 사용할 변수)
POINT g_LastMousePos = {};								// 마우스의 마지막 위치를 저장하는 변수 (카메라 회전을 구현하기 위해 사용할 변수)
float g_Yaw = 0.0f;										// Yaw (카메라의 좌우 회전을 구현하기 위해 사용할 변수)
float g_Pitch = 0.0f;									// Pitch (카메라의 상하 회전을 구현하기 위해 사용할 변수)

XMVECTOR worldUp = XMVectorSet(0, 1, 0, 0);				// 월드 좌표계에서의 상방 방향 벡터를 저장할 XMVECTOR 타입 변수 (Y축 방향)
														// 언리얼 엔진에서는 카메라가 회전해도 상방 방향이 항상 Y축 방향이므로 up 벡터는 항상 (0, 1, 0)으로 설정

XMFLOAT3 g_CameraLocation =								// 카메라 위치 (카메라의 위치를 저장하는 변수)
{							
	0.0f,												// 카메라의 x 좌표
	0.0f,												// 카메라의 y 좌표
	-3.0f												// 카메라의 z 좌표
};

struct ConstantBuffer									// 상수 버퍼 구조체 (쉐이더에서 사용할 상수 데이터를 저장하는 구조체)
{
	//float angle;										// 회전 각도 (쉐이더에서 사용할 회전 각도를 저장하는 변수)
	//float padding[3];									// 상수 버퍼 구조체의 크기를 16바이트 단위로 맞추기 위한 패딩 (Direct3D 11에서는 상수 버퍼의 크기가 16바이트 단위로 정렬되어야 함)

	XMFLOAT4X4 World;									// 회전 행렬 (쉐이더에서 사용할 회전 행렬을 저장하는 변수)
	XMFLOAT4X4 View;									// 뷰 행렬 (쉐이더에서 사용할 뷰 행렬을 저장하는 변수)
	XMFLOAT4X4 Projection;								// 투영 행렬 (쉐이더에서 사용할 투영 행렬을 저장하는 변수)
};

struct Vertex											// 정점 구조체 (3D 모델의 정점 정보를 저장하는 구조체)
{
	float x;
	float y;
	float z;

	float r;
	float g;
	float b;
};


//Vertex vertices[3] =									// 삼각형을 구성하는 3개의 정점 데이터 (각 정점의 x, y, z 좌표를 저장하는 배열)
//{
//	{ 0.0f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f },			// 빨강
//	{ 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f },			// 초록
//	{-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f }				// 파랑
//};

//정육면체를 구성하는 8개의 정점 데이터 (각 정점의 x, y, z 좌표와 r, g, b 색상값을 저장하는 배열)
Vertex vertices[8] =
{
	// 앞면
	{ -0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f },
	{  0.5f,  0.5f, -0.5f, 0.0f, 1.0f, 0.0f },
	{  0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f },
	{ -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f },

	// 뒷면
	{ -0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 1.0f },
	{  0.5f,  0.5f,  0.5f, 0.0f, 1.0f, 1.0f },
	{  0.5f, -0.5f,  0.5f, 0.0f, 1.0f, 1.0f },
	{ -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f }
};

// 인덱스 버퍼를 구성하는 36개의 인덱스 데이터 (정육면체를 구성하는 삼각형의 정점 인덱스를 저장하는 배열)
unsigned int indices[] =
{
	// 앞면
	0, 1, 2,
	0, 2, 3,

	// 뒷면
	4, 6, 5,
	4, 7, 6,

	// 왼쪽
	4, 0, 3,
	4, 3, 7,

	// 오른쪽
	1, 5, 6,
	1, 6, 2,

	// 위
	4, 5, 1,
	4, 1, 0,

	// 아래
	3, 2, 6,
	3, 6, 7
};

void DrawTriangle(XMMATRIX world, XMMATRIX view, XMMATRIX projection)		// 삼각형을 그리는 함수 (월드 행렬과 뷰 행렬을 인자로 받아 삼각형을 그리는 함수)
{						
	ConstantBuffer cb;									// 상수 버퍼 구조체 생성 (쉐이더에서 사용할 상수 데이터를 저장하는 구조체)

	XMStoreFloat4x4(									// 회전 행렬을 상수 버퍼 구조체에 저장하는 함수
		&cb.World,										// 상수 버퍼 구조체의 World 멤버 변수에 회전 행렬 저장
		XMMatrixTranspose(world));						// 회전 행렬을 전치(transpose)하여 상수 버퍼 구조체에 저장 (Direct3D 11에서는 행렬을 전치하여 저장해야 함)

	XMStoreFloat4x4(									// 뷰 행렬을 상수 버퍼 구조체에 저장하는 함수
		&cb.View,										// 상수 버퍼 구조체의 View 멤버 변수에 뷰 행렬 저장
		XMMatrixTranspose(view));						// 뷰 행렬을 전치하여 상수 버퍼 구조체에 저장 (Direct3D 11에서는 행렬을 전치하여 저장해야 함)

	XMStoreFloat4x4(									// 투영 행렬을 상수 버퍼 구조체에 저장하는 함수
		&cb.Projection,									// 상수 버퍼 구조체의 Projection 멤버 변수에 투영 행렬 저장
		XMMatrixTranspose(projection));					// 투영 행렬을 전치하여 상수 버퍼 구조체에 저장 (Direct3D 11에서는 행렬을 전치하여 저장해야 함)

	g_Context->UpdateSubresource(						// 상수 버퍼의 데이터를 업데이트하는 함수
		g_ConstantBuffer,								// 업데이트할 상수 버퍼 객체
		0,												// 업데이트할 서브리소스 인덱스 (0이면 첫 번째 서브리소스)
		nullptr,										// 업데이트할 영역 (nullptr이면 전체 영역 업데이트)
		&cb,											// 업데이트할 데이터의 포인터 (상수 버퍼 구조체의 주소)
		0,												// 행렬의 행 크기 (0이면 기본값 사용)
		0);												// 행렬의 열 크기 (0이면 기본값 사용)

	g_Context->VSSetConstantBuffers(					// 상수 버퍼를 정점 쉐이더에 바인딩하는 함수
		0,												// 상수 버퍼 슬롯 번호 (0이면 첫 번째 슬롯)
		1,												// 상수 버퍼의 개수 (1이면 하나의 상수 버퍼 사용)
		&g_ConstantBuffer);								// 상수 버퍼 배열 (하나의 상수 버퍼를 사용할 때는 버퍼 포인터의 주소를 전달)

	g_Context->Draw(									// 정점 데이터를 사용하여 도형을 그리는 함수
		3,												// 그릴 정점의 개수 (삼각형을 그리기 위해 3개의 정점 사용)
		0);												// 정점 데이터의 시작 위치 (버퍼의 처음부터 사용)
}

void DrawCube(XMMATRIX world, XMMATRIX view, XMMATRIX projection)		// 큐브를 그리는 함수 (월드 행렬과 뷰 행렬을 인자로 받아 큐브를 그리는 함수)
{
	ConstantBuffer cb;									// 상수 버퍼 구조체 생성 (쉐이더에서 사용할 상수 데이터를 저장하는 구조체)

	XMStoreFloat4x4(									// 회전 행렬을 상수 버퍼 구조체에 저장하는 함수
		&cb.World,										// 상수 버퍼 구조체의 World 멤버 변수에 회전 행렬 저장
		XMMatrixTranspose(world));						// 회전 행렬을 전치(transpose)하여 상수 버퍼 구조체에 저장 (Direct3D 11에서는 행렬을 전치하여 저장해야 함)

	XMStoreFloat4x4(									// 뷰 행렬을 상수 버퍼 구조체에 저장하는 함수
		&cb.View,										// 상수 버퍼 구조체의 View 멤버 변수에 뷰 행렬 저장
		XMMatrixTranspose(view));						// 뷰 행렬을 전치하여 상수 버퍼 구조체에 저장 (Direct3D 11에서는 행렬을 전치하여 저장해야 함)

	XMStoreFloat4x4(									// 투영 행렬을 상수 버퍼 구조체에 저장하는 함수
		&cb.Projection,									// 상수 버퍼 구조체의 Projection 멤버 변수에 투영 행렬 저장
		XMMatrixTranspose(projection));					// 투영 행렬을 전치하여 상수 버퍼 구조체에 저장 (Direct3D 11에서는 행렬을 전치하여 저장해야 함)

	g_Context->UpdateSubresource(						// 상수 버퍼의 데이터를 업데이트하는 함수
		g_ConstantBuffer,								// 업데이트할 상수 버퍼 객체
		0,												// 업데이트할 서브리소스 인덱스 (0이면 첫 번째 서브리소스)
		nullptr,										// 업데이트할 영역 (nullptr이면 전체 영역 업데이트)
		&cb,											// 업데이트할 데이터의 포인터 (상수 버퍼 구조체의 주소)
		0,												// 행렬의 행 크기 (0이면 기본값 사용)
		0);												// 행렬의 열 크기 (0이면 기본값 사용)

	g_Context->VSSetConstantBuffers(					// 상수 버퍼를 정점 쉐이더에 바인딩하는 함수
		0,												// 상수 버퍼 슬롯 번호 (0이면 첫 번째 슬롯)
		1,												// 상수 버퍼의 개수 (1이면 하나의 상수 버퍼 사용)
		&g_ConstantBuffer);								// 상수 버퍼 배열 (하나의 상수 버퍼를 사용할 때는 버퍼 포인터의 주소를 전달)

	g_Context->DrawIndexed(								// 인덱스 버퍼를 사용하여 도형을 그리는 함수
		36,												// 그릴 인덱스의 개수 (정육면체를 그리기 위해 36개의 인덱스 사용)
		0,												// 인덱스 버퍼의 시작 위치 (0이면 버퍼의 처음부터 사용)
		0);												// 정점 데이터의 시작 위치 (버퍼의 처음부터 사용)
}

bool InitShader()
{
	ID3DBlob* vsBlob = nullptr;							// 정점 쉐이더 컴파일 결과를 저장할 Blob 객체 포인터

	// =========================
	// Vertex Shader
	// =========================

	HRESULT hr = D3DCompileFromFile(
		L"VertexShader.hlsl",							// 컴파일할 쉐이더 파일 경로
		nullptr,										// 쉐이더 컴파일에 사용할 매크로 정의 (nullptr이면 매크로 정의 없음)
		nullptr,										// 쉐이더 컴파일에 사용할 포함 파일 시스템 (nullptr이면 기본 포함 파일 시스템 사용)
		"VSMain",										// 쉐이더의 진입점 함수 이름 (쉐이더 코드에서 VSMain이라는 함수가 정점 쉐이더의 진입점으로 정의되어 있어야 함)
		"vs_5_0",										// 쉐이더 모델 버전 (vs_5_0은 Direct3D 11에서 지원하는 정점 쉐이더 모델 버전)
		0,												// 컴파일 옵션 (0이면 기본 옵션 사용)
		0,												// 컴파일 옵션 (0이면 기본 옵션 사용)
		&vsBlob,										// 컴파일된 쉐이더 코드를 저장할 Blob 객체 포인터
		nullptr);										// 컴파일 에러 메시지를 저장할 Blob 객체 포인터 (nullptr이면 에러 메시지 저장 안함)

	if (FAILED(hr))										// 쉐이더 컴파일이 실패하면 false 반환
	{
		return false;									// 초기화 실패
	}

	hr = g_Device->CreateVertexShader(					// 컴파일된 쉐이더 코드를 사용하여 정점 쉐이더 객체를 생성하는 함수
		vsBlob->GetBufferPointer(),						// 컴파일된 쉐이더 코드의 포인터
		vsBlob->GetBufferSize(),						// 컴파일된 쉐이더 코드의 크기
		nullptr,										// 클래스 인스턴스화 인터페이스 (nullptr이면 사용 안함)
		&g_VertexShader);								// 생성된 정점 쉐이더 객체를 g_VertexShader 포인터에 저장

	if (FAILED(hr))										// 정점 쉐이더 객체 생성이 실패하면 false 반환
	{
		return false;									// 초기화 실패
	}

	D3D11_INPUT_ELEMENT_DESC layout[] =					// 정점 데이터의 구조를 GPU에 알려주는 입력 레이아웃 설명 배열 (각 요소는 정점 데이터의 한 속성을 설명)
	{
		{
			"POSITION",									// 정점 데이터의 의미 (쉐이더 코드에서 POSITION이라는 의미로 사용됨)
			0,											// 정점 데이터의 인덱스 (같은 의미의 데이터가 여러 개 있을 때 구분하기 위한 인덱스, 0이면 첫 번째 POSITION 데이터)
			DXGI_FORMAT_R32G32B32_FLOAT,				// 정점 데이터의 형식 (3개의 float으로 x, y, z 좌표를 나타냄)
			0,											// 정점 버퍼의 슬롯 번호 (0번 정점 버퍼)
			0,											// 정점 데이터 시작 위치
			D3D11_INPUT_PER_VERTEX_DATA,				// 정점마다 하나씩 데이터를 제공하는 방식
			0											// Instance Data Step Rate (인스턴스 데이터 사용 안함)
		},
		
		{
			"COLOR",									// 정점 데이터의 의미 (쉐이더 코드에서 COLOR이라는 의미로 사용됨)
			0,											// 정점 데이터의 인덱스 (같은 의미의 데이터가 여러 개 있을 때 구분하기 위한 인덱스, 0이면 첫 번째 COLOR 데이터)
			DXGI_FORMAT_R32G32B32_FLOAT,				// 정점 데이터의 형식 (3개의 float으로 r, g, b 색상값을 나타냄)
			0,											// 정점 버퍼의 슬롯 번호 (0번 정점 버퍼)
			12,											// 정점 데이터 시작 위치 (POSITION 데이터가 float x,y,z 12바이트이므로 COLOR 데이터는 12바이트 뒤에 시작)
			D3D11_INPUT_PER_VERTEX_DATA,				// 정점마다 하나씩 데이터를 제공하는 방식
			0											// Instance Data Step Rate (인스턴스 데이터 사용 안함)
		}
	};

	// =========================
	// Input Layout
	// =========================

	hr = g_Device->CreateInputLayout(					// 입력 레이아웃 객체 생성
		layout,											// 입력 레이아웃 설명 배열
		2,												// 입력 레이아웃 설명 배열의 요소 개수 (POSITION, COLOR)
		vsBlob->GetBufferPointer(),						// 컴파일된 쉐이더 코드의 포인터 (입력 레이아웃이 어떤 쉐이더와 호환되는지 확인하기 위해 필요)
		vsBlob->GetBufferSize(),						// 컴파일된 쉐이더 코드의 크기 (입력 레이아웃이 어떤 쉐이더와 호환되는지 확인하기 위해 필요)
		&g_InputLayout);								// 생성된 입력 레이아웃 객체를 g_InputLayout 포인터에 저장

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

	vsBlob->Release();									// 컴파일된 쉐이더 코드를 저장한 Blob 객체 해제 (메모리 누수 방지)

	// =========================
	// Pixel Shader
	// =========================

	ID3DBlob* psBlob = nullptr;							// 픽셀 쉐이더 컴파일 결과를 저장할 Blob 객체 포인터

	hr = D3DCompileFromFile(							// 픽셀 쉐이더 컴파일 함수
		L"PixelShader.hlsl",							// 컴파일할 쉐이더 파일 경로
		nullptr,										// 쉐이더 컴파일에 사용할 매크로 정의 (nullptr이면 매크로 정의 없음)
		nullptr,										// 쉐이더 컴파일에 사용할 포함 파일 시스템 (nullptr이면 기본 포함 파일 시스템 사용)
		"PSMain",										// 쉐이더의 진입점 함수 이름 (쉐이더 코드에서 PSMain이라는 함수가 픽셀 쉐이더의 진입점으로 정의되어 있어야 함)
		"ps_5_0",										// 쉐이더 모델 버전 (ps_5_0은 Direct3D 11에서 지원하는 픽셀 쉐이더 모델 버전)
		0,												// 컴파일 옵션 (0이면 기본 옵션 사용)
		0,												// 컴파일 옵션 (0이면 기본 옵션 사용)
		&psBlob,										// 컴파일된 쉐이더 코드를 저장할 Blob 객체 포인터
		nullptr);										// 컴파일 에러 메시지를 저장할 Blob 객체 포인터 (nullptr이면 에러 메시지 저장 안함)

	if (FAILED(hr))
	{
		psBlob->Release();								// 컴파일된 쉐이더 코드를 저장한 Blob 객체 해제 (메모리 누수 방지)
		return false;
	}

	hr = g_Device->CreatePixelShader(					// 컴파일된 쉐이더 코드를 사용하여 픽셀 쉐이더 객체를 생성하는 함수
		psBlob->GetBufferPointer(),						// 컴파일된 쉐이더 코드의 포인터
		psBlob->GetBufferSize(),						// 컴파일된 쉐이더 코드의 크기
		nullptr,										// 클래스 인스턴스화 인터페이스 (nullptr이면 사용 안함)
		&g_PixelShader);								// 생성된 픽쉘 쉐이더 객체를 g_PixelShader 포인터에 저장

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

	psBlob->Release();									// 컴파일된 쉐이더 코드를 저장한 Blob 객체 해제 (메모리 누수 방지)

	return true;										// 초기화 성공
}

bool InitGeometry()										// 버퍼를 초기화하는 함수 (삼각형을 그리기 위한 정점 데이터를 GPU에 업로드하는 함수)
{
	// 정점 버퍼
	D3D11_BUFFER_DESC bd = {};							// 버퍼 설명 구조체 (GPU 버퍼를 생성하기 위한 설정을 저장하는 구조체)
	bd.Usage = D3D11_USAGE_DEFAULT;						// 버퍼 기본 사용 방식 (CPU가 자주 접근하지 않고 GPU가 주로 사용하는 버퍼)
	bd.ByteWidth = sizeof(vertices);					// 버퍼의 크기 (정점 데이터의 전체 크기)
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;			// 버퍼의 용도 (정점 버퍼로 사용)

	D3D11_SUBRESOURCE_DATA initData = {};				// 초기 데이터 구조체 (버퍼에 업로드할 초기 데이터를 저장하는 구조체)
	initData.pSysMem = vertices;						// 초기 데이터로 사용할 정점 데이터의 포인터

	HRESULT hr = g_Device->CreateBuffer(				// GPU 버퍼를 생성하는 함수
		&bd,											// 버퍼 설명 구조체
		&initData,										// 초기 데이터 구조체
		&g_VertexBuffer);								// 생성된 버퍼 객체를 g_VertexBuffer 포인터에 저장

	if (FAILED(hr))										// 버퍼 생성이 실패하면 false 반환
	{
		return false;									// 초기화 실패
	}

	// 상수 버퍼
	D3D11_BUFFER_DESC cbd = {};							// 상수 버퍼 설명 구조체 (GPU 상수 버퍼를 생성하기 위한 설정을 저장하는 구조체)

	cbd.Usage = D3D11_USAGE_DEFAULT;					// 상수 버퍼 기본 사용 방식 (CPU가 자주 접근하지 않고 GPU가 주로 사용하는 버퍼)
	cbd.ByteWidth = sizeof(ConstantBuffer);				// 상수 버퍼의 크기 (상수 버퍼 구조체의 크기)
	cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;			// 상수 버퍼의 용도 (상수 버퍼로 사용)
	
	HRESULT chr = g_Device->CreateBuffer(				// GPU 상수 버퍼를 생성하는 함수
		&cbd,											// 상수 버퍼 설명 구조체
		nullptr,										// 초기 데이터 구조체 (nullptr이면 초기 데이터 없음)
		&g_ConstantBuffer);								// 생성된 상수 버퍼 객체를 g_ConstantBuffer 포인터에 저장

	if (FAILED(chr))									// 상수 버퍼 생성이 실패하면 false 반환
	{
		return false;
	}

	// 인덱스 버퍼
	D3D11_BUFFER_DESC ibd = {};							// 인덱스 버퍼 설명 구조체 (GPU 인덱스 버퍼를 생성하기 위한 설정을 저장하는 구조체)
	ibd.Usage = D3D11_USAGE_DEFAULT;					// 인덱스 버퍼 기본 사용 방식 (CPU가 자주 접근하지 않고 GPU가 주로 사용하는 버퍼)
	ibd.ByteWidth = sizeof(indices);					// 인덱스 버퍼의 크기 (인덱스 데이터의 전체 크기)
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;			// 인덱스 버퍼의 용도 (인덱스 버퍼로 사용)

	D3D11_SUBRESOURCE_DATA indexData = {};				// 초기 데이터 구조체 (버퍼에 업로드할 초기 데이터를 저장하는 구조체)
	indexData.pSysMem = indices;						// 초기 데이터로 사용할 인덱스 데이터의 포인터

	HRESULT ihr = g_Device->CreateBuffer(				// GPU 인덱스 버퍼를 생성하는 함수
		&ibd,											// 인덱스 버퍼 설명 구조체
		&indexData,										// 초기 데이터 구조체
		&g_IndexBuffer);								// 생성된 인덱스 버퍼 객체를 g_IndexBuffer 포인터에 저장

	if (FAILED(ihr))									// 인덱스 버퍼 생성이 실패하면 false 반환
	{
		return false;
	}

	D3D11_VIEWPORT vp = {};								// 뷰포트 구조체 (화면에 그려질 영역을 정의하는 구조체)
	vp.Width = 1280.0f;									// 뷰포트의 너비 (화면에 그려질 영역의 너비)
	vp.Height = 720.0f;									// 뷰포트의 높이
	vp.MinDepth = 0.0f;									// 뷰포트의 최소 깊이 (화면에 그려질 영역의 깊이 범위의 최소값)
	vp.MaxDepth = 1.0f;									// 뷰포트의 최대 깊이 (화면에 그려질 영역의 깊이 범위의 최대값)
	vp.TopLeftX = 0.0f;									// 뷰포트의 왼쪽 상단 X 좌표 (화면에 그려질 영역의 왼쪽 상단 모서리의 X 좌표)
	vp.TopLeftY = 0.0f;									// 뷰포트의 왼쪽 상단 Y 좌표 (화면에 그려질 영역의 왼쪽 상단 모서리의 Y 좌표)

	g_Context->RSSetViewports(							// GPU에 뷰포트를 설정하는 함수
		1,												// 설정할 뷰포트의 개수 (1이면 하나의 뷰포트 설정)
		&vp);											// 설정할 뷰포트 배열 (하나의 뷰포트를 사용할 때는 뷰포트 구조체의 주소를 전달)

	return true;										// 초기화 성공
}

// Direct3D 11 초기화 함수
bool InitD3D(HWND hwnd)
{
	DXGI_SWAP_CHAIN_DESC sd = {};                       // swap chain을 생성하기 위한 구조체
	sd.BufferCount = 1;                                 // 버퍼 개수 (더블 버퍼링이므로 1)
	sd.BufferDesc.Width = 1280;                         // 버퍼의 너비
	sd.BufferDesc.Height = 720;                         // 버퍼의 높이
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;  // 버퍼의 픽셀 형식 (RGBA 각각 8비트)
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;   // 버퍼의 용도 (렌더 타겟으로 사용)
	sd.OutputWindow = hwnd;                             // 버퍼가 그려질 윈도우 핸들
	sd.SampleDesc.Count = 1;                            // 멀티샘플링 카운트 (1이면 멀티샘플링 사용 안함. MSAA는 안티앨리어싱 게임에서 계단현상 제거하는 옵션)
	sd.Windowed = TRUE;                                 // 창 모드로 실행

	HRESULT hr = D3D11CreateDeviceAndSwapChain(         // Direct3D 11 장치와 swap chain을 생성하는 함수
		nullptr,                                        // 기본 어댑터 사용 (첫 번째 GPU)
		D3D_DRIVER_TYPE_HARDWARE,                       // 하드웨어 가속을 사용하는 드라이버 타입 (GPU 사용)
		nullptr,                                        // 소프트웨어 렌더러 사용 안함
		0,                                              // 생성 옵션 (디버그 모드 등)
		nullptr,                                        // 피처 레벨 배열 (Direct3D 11.0, 11.1 등 지원하는 피처 레벨을 지정할 수 있음)
		0,                                              // 피처 레벨 (Direct3D 11.0, 11.1 등)
		D3D11_SDK_VERSION,                              // SDK 버전 (항상 D3D11_SDK_VERSION 사용)
		&sd,                                            // swap chain 설명 구조체
		&g_SwapChain,                                   // 생성된 swap chain 객체를 받을 포인터
		&g_Device,                                      // 생성된 Direct3D 장치 객체를 받을 포인터
		nullptr,                                        // 피처 레벨을 받을 포인터 (필요 없으면 nullptr)
		&g_Context);                                    // 생성된 Direct3D 컨텍스트 객체를 받을 포인터
    
	if (FAILED(hr))                                     // 생성이 실패하면 false 반환
        return false;

	ID3D11Texture2D* backBuffer = nullptr;              // back buffer를 저장할 포인터

	g_SwapChain->GetBuffer(                             // swap chain이 가지고 있는 back buffer를 가져오는 함수
		0,                                              // 버퍼 인덱스 (0이면 첫 번째 버퍼)
		__uuidof(ID3D11Texture2D),                      // 가져올 버퍼의 인터페이스 ID (ID3D11Texture2D)
		(void**)&backBuffer);                           // 가져온 버퍼를 저장할 포인터

	g_Device->CreateRenderTargetView(                   // back buffer를 렌더 타겟 뷰로 만드는 함수
		backBuffer,                                     // 렌더 타겟 뷰로 만들 텍스처 (back buffer)
		nullptr,                                        // 렌더 타겟 뷰 설명 (nullptr이면 기본값 사용)
		&g_RTV);                                        // 생성된 렌더 타겟 뷰 객체를 g_RTV 포인터에 저장

	backBuffer->Release();                              // Back buffer는 더 이상 필요 없으므로 해제

	D3D11_TEXTURE2D_DESC depthDesc = {};                // 깊이 스텐실 버퍼를 생성하기 위한 구조체

	depthDesc.Width = 1280;							    // 깊이 스텐실 버퍼의 너비
	depthDesc.Height = 720;							    // 깊이 스텐실 버퍼의 높이
	depthDesc.MipLevels = 1;						    // 깊이 스텐실 버퍼의 MIP 맵 레벨 수 (1이면 MIP 맵 사용 안함)
	depthDesc.ArraySize = 1;						    // 깊이 스텐실 버퍼의 배열 크기 (1이면 단일 텍스처)
	depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;   // 깊이 스텐실 버퍼의 픽셀 형식 (24비트 깊이 + 8비트 스텐실)
	depthDesc.SampleDesc.Count = 1;					    // 멀티샘플링 카운트 (1이면 멀티샘플링 사용 안함)
	depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;	    // 깊이 스텐실 버퍼의 용도 (깊이 스텐실 버퍼로 사용)

	g_Device->CreateTexture2D(
		&depthDesc,                                     // 깊이 스텐실 버퍼 설명 구조체
		nullptr,                                        // 초기 데이터 (nullptr이면 초기 데이터 없음)
		&g_DepthStencilBuffer);                         // 생성된 깊이 스텐실 버퍼 객체를 g_DepthStencilBuffer 포인터에 저장
										
	g_Device->CreateDepthStencilView(					// 깊이 스텐실 뷰를 생성하는 함수
		g_DepthStencilBuffer,							// 깊이 스텐실 버퍼를 뷰로 만들 텍스처
		nullptr,										// 깊이 스텐실 뷰 설명 (nullptr이면 기본값 사용)
		&g_DSV);										// 생성된 깊이 스텐실 뷰 객체를 g_DSV 포인터에 저장

	return true;                                        // 초기화 성공
}													    
													    
void Render()                                           // RGB 색상값과 알파값을 포함하는 4개의 요소로 구성된 배열 (RGBA)
{			
	XMVECTOR camPos =									// 카메라 위치를 저장할 XMVECTOR 타입 변수
		XMLoadFloat3(&g_CameraLocation);				// XMFLOAT3 타입의 g_CameraLocation 변수를 XMVECTOR 타입으로 변환하여 camPos에 저장

	XMVECTOR forward =									// 카메라의 전방 방향을 저장할 XMVECTOR 타입 변수
		XMVectorSet(									// 카메라의 전방 방향 벡터를 계산하는 함수
			cosf(g_Pitch) * sinf(g_Yaw),				// X 좌표 (카메라의 피치와 요를 이용하여 계산)
			sinf(g_Pitch),								// Y 좌표 (카메라의 피치를 이용하여 계산)
			cosf(g_Pitch) * cosf(g_Yaw),				// Z 좌표 (카메라의 피치와 요를 이용하여 계산)
			0.0f);										// W 좌표 (카메라의 전방 방향 벡터는 방향 벡터이므로 W 좌표는 0으로 설정)

	forward = XMVector3Normalize(forward);				// 카메라의 전방 방향 벡터를 정규화하여 forward에 저장 (정규화는 벡터의 길이를 1로 만드는 과정)

	XMVECTOR right =									// 카메라의 우방 방향을 저장할 XMVECTOR 타입 변수
		XMVector3Normalize(								// 카메라의 우방 방향 벡터를 정규화하여 right에 저장
			XMVector3Cross(worldUp, forward));			// 카메라의 우방 방향 벡터를 계산하는 함수 (월드 좌표계에서의 상방 방향 벡터와 전방 방향 벡터의 외적을 계산하여 우방 방향 벡터를 구함)

	XMVECTOR up = XMVector3Cross(forward, right);		// 카메라의 상방 방향을 저장할 XMVECTOR 타입 변수 (우방 방향 벡터와 전방 방향 벡터의 외적을 계산하여 상방 방향 벡터를 구함)

	XMVECTOR lookAt = camPos + forward;					// 카메라가 바라보는 지점을 저장할 XMVECTOR 타입 변수 (카메라 위치 + 전방 방향 벡터)

	float speed = 0.01f;								// 카메라 이동 속도 (키보드 입력에 따라 카메라가 이동하는 속도를 조절하는 변수)

	if (GetAsyncKeyState('W') & 0x8000)					// W 키가 눌렸는지 확인하는 함수 (0x8000은 키가 눌렸음을 나타내는 비트 마스크)
		camPos += forward * speed;						// 카메라 위치를 전방 방향으로 이동 (W 키를 누르면 카메라가 전방으로 이동)

	if (GetAsyncKeyState('S') & 0x8000)
		camPos -= forward * speed;

	if (GetAsyncKeyState('A') & 0x8000)
		camPos -= right * speed;

	if (GetAsyncKeyState('D') & 0x8000)
		camPos += right * speed;

	if (GetAsyncKeyState('Q') & 0x8000)
		camPos -= worldUp * speed;

	if (GetAsyncKeyState('E') & 0x8000)
		camPos += worldUp * speed;

	if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)			// 마우스 오른쪽 버튼이 눌렸는지 확인
	{
		if (!g_IsRightMouseDown)						// 마우스 오른쪽 버튼이 처음 눌렸을 때만 처리
		{
			g_IsRightMouseDown = true;					// 마우스 오른쪽 버튼이 눌렸음을 표시

			GetCursorPos(&g_LastMousePos);				// 마우스 커서의 현재 위치를 g_LastMousePos에 저장
		}

		POINT curMousePos;								// 마우스 커서의 현재 위치를 저장할 POINT 구조체
		GetCursorPos(&curMousePos);						// 마우스 커서의 현재 위치를 currentPos에 저장

		int deltaX = curMousePos.x - g_LastMousePos.x;	// 마우스 커서의 X축 이동 거리 계rm
		int deltaY = curMousePos.y - g_LastMousePos.y;	// 마우스 커서의 Y축 이동 거리 계산

		float sensitivity = 0.003f;						// 마우스 이동 감도 설정

		g_Yaw += deltaX * sensitivity;					// 마우스 이동 거리에 따라 Yaw 값 변경
		g_Pitch -= deltaY * sensitivity;				// 마우스 이동 거리에 따라 Pitch 값 변경

		g_Pitch = std::clamp(							// Pitch 값을 -90도에서 90도로 제한
			g_Pitch,									// 현재 Pitch 값
			-XM_PIDIV2 + 0.01f,							// 최소값 (-90도 + 0.01)
			XM_PIDIV2 - 0.01f);							// 최대값 (90도 - 0.01)

		g_LastMousePos = curMousePos;					// g_LastMousePos를 현재 마우스 위치로 업데이트
	}
	else
	{
		g_IsRightMouseDown = false;						// 마우스 오른쪽 버튼이 눌리지 않았음을 표시
	}

	XMMATRIX view =										// 뷰 공간의 좌표를 정의하는 행렬
		XMMatrixLookAtLH(								// 왼손 좌표계 기준의 뷰 행렬 생성 함수
			camPos,										// 카메라의 위치 벡터
			lookAt,										// 카메라가 바라보는 지점 벡터 (카메라 위치 + 전방 방향 벡터)
			up);										// 카메라의 상방 방향 벡터 (월드 좌표계에서의 상방 방향 벡터)

    float color[4] =
    {
        0.0f,
        0.3f,
        0.8f,
        1.0f
    };

	g_Context->OMSetRenderTargets(                      // 렌더 타겟을 설정하는 함수
		1,                                              // 렌더 타겟의 개수 (1이면 하나의 렌더 타겟 사용)
		&g_RTV,                                         // 출력 대상
		g_DSV);											// DepthStencilBuffer (깊이 버퍼 사용 안함)

	g_Context->ClearRenderTargetView(                   // 렌더 타겟 뷰를 지정된 색상으로 지우는 함수
		g_RTV,                                          // 지울 렌더 타겟 뷰
		color);                                         // 지울 색상 (RGBA)

	g_Context->ClearDepthStencilView(					// 깊이 스텐실 뷰를 지우는 함수
		g_DSV,											// 지울 깊이 스텐실 뷰
		D3D11_CLEAR_DEPTH,								// 지울 깊이 스텐실 뷰의 종류 (깊이 버퍼만 지움)
		1.0f,											// 깊이 버퍼를 지울 때 사용할 값 (1.0f이면 가장 먼 거리)
		0);												// 스텐실 버퍼를 지울 때 사용할 값 (0이면 스텐실 버퍼를 지우지 않음)

	g_Context->IASetInputLayout(g_InputLayout);         // 입력 레이아웃 설정 (GPU가 정점 데이터를 어떻게 해석할지 설정)

	UINT stride = sizeof(Vertex);                       // 정점 데이터의 크기 (한 정점이 차지하는 바이트 수)	
	UINT offset = 0;                                    // 정점 데이터의 시작 위치 (버퍼의 처음부터 사용)

	g_Context->IASetVertexBuffers(						// 정점 버퍼 설정 (GPU에 정점 데이터를 제공하는 함수)
		0,												// 정점 버퍼 슬롯 번호 (0이면 첫 번째 슬롯)
		1,												// 정점 버퍼의 개수 (1이면 하나의 버퍼 사용)
		&g_VertexBuffer,								// 정점 버퍼 배열 (하나의 버퍼를 사용할 때는 버퍼 포인터의 주소를 전달)
		&stride,										// 정점 데이터의 크기 (한 정점이 차지하는 바이트 수)
		&offset);										// 정점 데이터의 시작 위치 (버퍼의 처음부터 사용)

	g_Context->IASetIndexBuffer(						// 인덱스 버퍼 설정 (GPU에 인덱스 데이터를 제공하는 함수)
		g_IndexBuffer,									// 사용할 인덱스 버퍼 객체
		DXGI_FORMAT_R32_UINT,							// 인덱스 데이터의 형식 (32비트 부호 없는 정수)
		0);												// 인덱스 데이터의 시작 위치 (버퍼의 처음부터 사용)

	g_Context->IASetPrimitiveTopology(					// 정점 데이터를 어떻게 연결하여 도형을 만들지 설정하는 함수
		D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);			// 정점 데이터를 3개씩 묶어서 삼각형을 만드는 방식 (각 3개의 정점이 하나의 삼각형을 형성)

	g_Context->VSSetShader(								// 정점 쉐이더 설정 (GPU에 정점 쉐이더를 사용하도록 하는 함수)
		g_VertexShader,									// 사용할 정점 쉐이더 객체
		nullptr,										// 클래스 인스턴스화 인터페이스 (nullptr이면 사용 안함)
		0);												// 추가 옵션 (0이면 기본값 사용)

	g_Context->PSSetShader(								// 픽셀 쉐이더 설정 (GPU에 픽셀 쉐이더를 사용하도록 하는 함수)
		g_PixelShader,									// 사용할 픽셀 쉐이더 객체
		nullptr,										// 클래스 인스턴스화 인터페이스 (nullptr이면 사용 안함)
		0);												// 추가 옵션 (0이면 기본값 사용)
	
	g_Context->IASetIndexBuffer(						// 인덱스 버퍼 설정 (GPU에 인덱스 데이터를 제공하는 함수)
		g_IndexBuffer,									// 사용할 인덱스 버퍼 객체
		DXGI_FORMAT_R32_UINT,							// 인덱스 데이터의 형식 (32비트 부호 없는 정수)
		0);												// 인덱스 데이터의 시작 위치 (버퍼의 처음부터 사용)

	g_Angle = XMConvertToRadians(45.0f);				// 회전 각도를 라디안 단위로 변환하여 g_Angle 변수에 저장 (45도)

	XMMATRIX world =									// 월드공간의 좌표를 정의하는 행렬
		XMMatrixScaling(								// 크기 조절
			1.0f,										// x축 크기
			1.0f,										// y축 크기
			1.0f) *										// z축 크기
		XMMatrixRotationX(g_Angle) *					// 회전
		XMMatrixRotationY(g_Angle) *					// 회전
		XMMatrixRotationZ(g_Angle) *					// 회전	
		XMMatrixTranslation(							// 이동
			0.0f,										// 이동할 x 좌표
			0.0f,										// 이동할 y 좌표
			0.0f);										// 이동할 z 좌표

	XMMATRIX projection =								// 투영 공간의 좌표를 정의하는 행렬
		XMMatrixPerspectiveFovLH(						// 원근 투영 행렬 생성 함수 (왼손 좌표계 기준)
			XMConvertToRadians(60.0f),					// 수직 시야각 (60도)
			1280.0f / 720.0f,							// 화면의 가로 세로 비율 (1280 / 720)
			0.1f,										// 근평면 (카메라에서 가장 가까운 거리), 이상의 값만 렌더링
			20.0f);										// 원평면 (카메라에서 가장 먼 거리), 미만의 값만 렌더링

	DrawCube(world, view,projection);				

	//g_Angle = XMConvertToRadians(90.0f);

	//world =												// 월드공간의 좌표를 정의하는 행렬
	//	XMMatrixScaling(								// 크기 조절
	//		1.0f,										// x축 크기
	//		1.0f,										// y축 크기
	//		1.0f) *										// z축 크기
	//	XMMatrixRotationZ(g_Angle) *					// 회전
	//	XMMatrixTranslation(							// 이동
	//		5.0f,										// 이동할 x 좌표
	//		0.0f,										// 이동할 y 좌표
	//		0.0f);										// 이동할 z 좌표

	//DrawCube(world, view, projection);				

	//world =												// 월드공간의 좌표를 정의하는 행렬
	//	XMMatrixScaling(								// 크기 조절
	//		1.0f,										// x축 크기
	//		1.0f,										// y축 크기
	//		1.0f) *										// z축 크기
	//	XMMatrixRotationZ(g_Angle) *					// 회전
	//	XMMatrixTranslation(							// 이동
	//		-5.0f,										// 이동할 x 좌표
	//		0.0f,										// 이동할 y 좌표
	//		0.0f);										// 이동할 z 좌표

	//DrawCube(world, view, projection);				

	//world =												// 월드공간의 좌표를 정의하는 행렬
	//	XMMatrixScaling(								// 크기 조절
	//		1.0f,										// x축 크기
	//		1.0f,										// y축 크기
	//		1.0f) *										// z축 크기
	//	XMMatrixRotationZ(g_Angle) *					// 회전
	//	XMMatrixTranslation(							// 이동
	//		0.0f,										// 이동할 x 좌표
	//		0.0f,										// 이동할 y 좌표
	//		5.0f);										// 이동할 z 좌표

	//DrawCube(world, view, projection);					

	g_SwapChain->Present(                               // 화면에 그려진 버퍼를 표시하는 함수
		1,                                              // VSync 사용 (1이면 모니터의 수직 동기화에 맞춰 버퍼를 교체, 0이면 즉시 교체)
		0);                                             // 추가 옵션 (0이면 기본값 사용)

	XMStoreFloat3(&g_CameraLocation, camPos);			// XMVECTOR 타입의 camPos를 XMFLOAT3 타입으로 변환하여 g_CameraLocation에 저장
}

void Cleanup()                                          // 객체 해제 함수
{
	if (g_VertexBuffer) g_VertexBuffer->Release();      // 정점 버퍼 해제

	if (g_InputLayout) g_InputLayout->Release();        // 입력 레이아웃 해제
	if (g_VertexShader) g_VertexShader->Release();      // 정점 쉐이더 해제
	if (g_PixelShader) g_PixelShader->Release();        // 픽셀 쉐이더 해제

	if (g_RTV) g_RTV->Release();                        // 렌더 타겟 뷰 해제
	if (g_SwapChain) g_SwapChain->Release();            // swap chain 해제
	if (g_Context) g_Context->Release();                // GPU 컨텍스트 해제
	if (g_Device) g_Device->Release();                  // GPU 장치 해제

	if (g_ConstantBuffer) g_ConstantBuffer->Release();  // 상수 버퍼 해제

	if (g_DSV) g_DSV->Release();								// 깊이 스텐실 뷰 해제
	if (g_DepthStencilBuffer) g_DepthStencilBuffer->Release();  // 깊이 스텐실 버퍼 해제	

	if (g_IndexBuffer) g_IndexBuffer->Release();		// 인덱스 버퍼 해제
}

LRESULT CALLBACK WndProc(                               // 윈도우 메시지를 처리하는 함수
	HWND hwnd,                                          // 윈도우 핸들
	UINT msg,                                           // 메시지 코드
	WPARAM wParam,                                      // 메시지에 대한 추가 정보 (예: 키보드 입력, 마우스 클릭 등)
	LPARAM lParam)                                      // 메시지에 대한 추가 정보 (예: 키보드 입력, 마우스 클릭 등)
{
	switch (msg)                                        // 메시지 코드에 따라 처리할 작업을 결정
    {
	case WM_DESTROY:                                    // 윈도우가 파괴될 때 발생하는 메시지
		PostQuitMessage(0);                             // 메시지 루프에 WM_QUIT 메시지를 보내서 프로그램 종료를 알리는 함수
		return 0;                                       // 메시지 처리 완료를 알리는 반환값
    }

	return DefWindowProc(                               // 기본 윈도우 프로시저를 호출하여 처리되지 않은 메시지를 처리 (개발자가 직접 처리하지 않은 메시지는 운영체제가 기본적으로 처리하도록 하는 함수)
		hwnd,                                           // 윈도우 핸들
		msg,											// 메시지 코드
		wParam,											// 메시지에 대한 추가 정보
		lParam);										// 메시지에 대한 추가 정보
}

int WINAPI wWinMain(									// 윈도우 애플리케이션 진입점 함수
	HINSTANCE hInstance,								// 프로그램 인스턴스 핸들
	HINSTANCE,											// 이전 인스턴스 핸들 (윈도우 95/98에서는 사용되었지만 현대 윈도우에서는 항상 nullptr)
	PWSTR,												// 명령줄 인수 (유니코드 문자열로 전달, 일반적으로 사용되지 않음)
	int nCmdShow)										// 창의 초기 표시 방법을 나타내는 정수 (예: SW_SHOW, SW_HIDE 등)
{
	WNDCLASS wc = {};									// 윈도우 클래스 구조체 (윈도우의 속성과 동작을 정의하는 구조체)

	wc.lpfnWndProc = WndProc;							// 윈도우 프로시저 함수 포인터 (윈도우 메시지를 처리하는 함수)
	wc.hInstance = hInstance;							// 프로그램 인스턴스 핸들
	wc.lpszClassName = L"MyWindow";						// 윈도우 클래스 이름 (윈도우를 생성할 때 이 이름을 사용하여 어떤 클래스의 윈도우를 생성할지 지정)

	RegisterClass(&wc);									// 윈도우 클래스를 등록하는 함수 (윈도우를 생성하기 전에 클래스 정보를 운영체제에 등록하여 나중에 이 클래스를 기반으로 윈도우를 생성할 수 있도록 하는 함수)

	HWND hwnd = CreateWindow(							// 윈도우를 생성하는 함수
		L"MyWindow",									// 생성할 윈도우의 클래스 이름 (RegisterClass에서 등록한 클래스 이름)
		L"DX11 Study",									// 생성할 윈도우의 제목 (윈도우 상단에 표시되는 텍스트)
		WS_OVERLAPPEDWINDOW,							// 윈도우 스타일 (WS_OVERLAPPEDWINDOW는 일반적인 창 모양을 나타내는 스타일로, 제목 표시줄, 테두리, 최소화/최대화 버튼 등이 포함됨)
		100,											// 윈도우의 초기 X 위치
		100,											// 윈도우의 초기 Y 위치
		1280,											// 윈도우의 너비
		720,											// 윈도우의 높이
		nullptr,										// 부모 윈도우 핸들 (nullptr이면 부모 윈도우가 없음)
		nullptr,										// 메뉴 핸들 (nullptr이면 메뉴가 없음)
		hInstance,										// 프로그램 인스턴스 핸들
		nullptr);										// 추가 매개변수 (일반적으로 사용되지 않음)

    ShowWindow(hwnd, nCmdShow);							// 윈도우를 화면에 표시하는 함수 (생성된 윈도우를 실제로 화면에 보이도록 하는 함수)
														// nCmdShow 매개변수는 창의 초기 표시 방법을 나타내며, 일반적으로 wWinMain 함수의 매개변수로 전달된 값을 사용

	if (!InitD3D(hwnd))									// Direct3D 11 초기화에 실패하면 프로그램을 종료하는 조건문
		return -1;										// 초기화 실패 시 -1을 반환하여 프로그램 종료

	if(!InitShader())									// 쉐이더 초기화에 실패하면 프로그램을 종료하는 조건문
		return -1;										// 초기화 실패 시 -1을 반환하여 프로그램 종료

	if(!InitGeometry())									// 정점 버퍼 초기화에 실패하면 프로그램을 종료하는 조건문
		return -1;										// 초기화 실패 시 -1을 반환하여 프로그램 종료

	MSG msg = {};										// 윈도우 메시지를 저장하는 구조체 (윈도우 메시지 루프에서 메시지를 받아 처리하는 데 사용)

	while (msg.message != WM_QUIT)						// 프로그램이 종료될 때까지 메시지 루프를 계속 실행하는 조건문 (WM_QUIT 메시지가 발생하면 루프 종료)
    {
		if (PeekMessage(								// 메시지 큐에서 메시지를 확인하는 함수
			&msg,										// 메시지를 저장할 구조체 포인터
			nullptr,									// 윈도우 핸들 (nullptr이면 모든 윈도우의 메시지를 확인)
			0,											// 메시지 범위의 최소값 (0이면 모든 메시지를 확인)
			0,											// 메시지 범위의 최대값 (0이면 모든 메시지를 확인)
			PM_REMOVE))									// 메시지를 큐에서 제거하는 옵션 (PM_REMOVE는 메시지를 확인한 후 큐에서 제거, PM_NOREMOVE는 메시지를 확인하지만 큐에서 제거하지 않음)
        {
			TranslateMessage(&msg);						// 키보드 입력 메시지를 문자 메시지로 변환하는 함수 (예: WM_KEYDOWN을 WM_CHAR로 변환)
            DispatchMessage(&msg);						// 메시지를 해당 윈도우 프로시저로 전달하는 함수 (예: WndProc 함수로 메시지를 전달하여 처리)
        }
        else
        {
			Render();									// 화면에 그리는 함수 (Direct3D 11을 사용하여 화면에 그래픽을 렌더링하는 함수)
														// 메시지 큐에 처리할 메시지가 없을 때 Render 함수를 호출하여 화면을 계속 업데이트 (게임 루프의 핵심 부분)
        }
    }

	Cleanup();											// Direct3D 11에서 사용한 리소스를 해제하는 함수 (프로그램 종료 전에 GPU 리소스를 정리하여 메모리 누수 방지)

	return 0;											// 프로그램이 정상적으로 종료되었음을 나타내는 반환값 (0은 성공적으로 종료되었음을 의미)
}