(DirectX11) 초기화 단계
DirectX 11로 그래픽 애플리케이션을 개발하려면 몇 가지 초기화 단계가 필요합니다.
DirectX 11 장치 만들기(Direct3D 장치)
● D3D11CreateDevice 함수를 호출하여 DirectX 11 장치를 만듭니다. 이 기능은 지원되는 하드웨어 장치를 나열하고 그 중 하나를 선택하여 DirectX 11 장치를 만듭니다.
스왑 체인 생성
● DXGI(DirectX Graphics Infrastructure) 라이브러리를 사용하여 스왑 체인을 만듭니다. 스왑 체인은 백 버퍼와 프런트 버퍼를 관리하여 프레임을 출력합니다.
백 버퍼에 렌더링 대상 보기 만들기
● D3D11RenderTargetView 개체를 생성하여 렌더링 대상을 백 버퍼로 설정합니다. 이 개체는 렌더링을 위해 백 버퍼를 사용하여 렌더링 작업을 수행하는 데 사용됩니다.
깊이 템플릿 보기 생성
● 깊이 버퍼는 렌더링 프로세스에 깊이 정보를 추가하는 데 사용할 수 있습니다. D3D11DepthStencilView 개체를 만들어 깊이 스텐실 보기를 만들고 깊이 스텐실 보기를 백 버퍼로 설정합니다.
뷰포트 설정
● 뷰포트는 렌더링할 화면의 영역을 지정하는 역할을 합니다. D3D11_VIEWPORT 구조를 사용하여 뷰포트의 위치, 크기 및 최소/최대 깊이 값을 설정합니다.
셰이더 컴파일
● 셰이더는 DirectX 11에서 렌더링 작업을 처리하는 데 필요한 핵심 요소 중 하나입니다. D3DCompile 함수를 사용하여 HLSL(High-Level Shading Language)로 작성된 셰이더 코드를 컴파일하여 생성됩니다.
입력 레이아웃 만들기
● 입력 레이아웃은 정점 데이터의 레이아웃을 정의하는 데 사용됩니다. CreateInputLayout 함수를 사용하여 입력 레이아웃을 만듭니다.
버텍스 버퍼 생성
● 정점 버퍼는 그래픽 객체의 정점 데이터를 저장하는 메모리 버퍼입니다. D3D11_BUFFER_DESC 구조는 정점 버퍼의 속성을 정의하는 데 사용되고 D3D11_SUBRESOURCE_DATA 구조는 정점 데이터를 초기화하는 데 사용됩니다. ID3D11Device::CreateBuffer 메서드를 호출하여 정점 버퍼를 만듭니다.
상수 버퍼 생성
● 상수 버퍼는 셰이더에 전달되는 상수 값을 저장하는 메모리 버퍼입니다. 상수 버퍼를 만들려면 D3D11_BUFFER_DESC 구조를 사용하여 버퍼의 속성을 정의하고 ID3D11Device::CreateBuffer 메서드를 호출합니다.
렌더링 파이프라인 설정
● 렌더링 파이프라인은 그래픽 개체를 렌더링하는 데 사용되는 일련의 처리 단계를 말합니다. 이러한 계층에는 셰이더, 입력 레이아웃, 버텍스 버퍼, 래스터라이저, 블렌더 등이 포함됩니다.
렌더 루프 시작
● 렌더 루프는 그래픽 개체를 실제로 렌더링하는 데 사용됩니다. 렌더링 루프에서는 프레임마다 렌더링 파이프라인이 설정되고 그래픽 개체가 렌더링되어 화면에 표시됩니다.
멈추다
● 응용 프로그램이 닫히면 DirectX 11 장치와 스왑 체인을 해제하십시오.
다음은 몇 단계로 연결된 두 삼각형에서 사각형을 만드는 코드입니다.
HRESULT result;
UINT width = WIN_WIDTH;
UINT height = WIN_HEIGHT;
// DXGI 디바이스 및 스왑체인 생성
// Swap Chain 설정
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
// 화면 크기
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
// 색상 포맷
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
// 주사율
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
// 멀티 샘플링(안티앨리어싱 설정)
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
// 버퍼 용도 지정
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hWnd;
// 전체화면 X
swapChainDesc.Windowed = TRUE;
// 후면 버퍼를 어떻게 처리할지를 결정
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
UINT flags = D3D11_CREATE_DEVICE_DEBUG;
result = D3D11CreateDeviceAndSwapChain(
nullptr, // 이 매개변수를 nullptr로 지정하면 기본 그래픽 카드를 사용합니다.
D3D_DRIVER_TYPE_HARDWARE, // 하드웨어 드라이버를 사용하도록 지정
nullptr, // 소프트웨어 모듈의 핸들을 지정합니다. 이 매개변수를 nullptr로 지정하면 소프트웨어 렌더러를 사용하지 않습니다.
flags, // D3D11_CREATE_DEVICE_DEBUG 사용
nullptr, // nullptr로 지정하면 최대 레벨이 선택됩니다.
0, // 기능 레벨의 수를 지정합니다.
D3D11_SDK_VERSION, // DirectX 11을 사용하도록 지정합니다.
&swapChainDesc, // 스왑 체인의 속성을 지정하는 구조체를 지정합니다.
&swapChain, // 생성된 스왑 체인의 포인터를 반환합니다.
&device, // 생성된 디바이스의 포인터를 반환합니다.
nullptr, // 생성된 디바이스의 기능 레벨을 반환합니다.
&context // 생성된 디바이스의 컨텍스트를 반환합니다.
);
if (FAILED(result)) { return; }
// 백버퍼 및 뎁스 스텐실 버퍼 생성
ID3D11Texture2D* backBuffer;
//ID3D11Texture2D* depthBuffer;
// 스왑 체인의 후면 버퍼를 가져옵니다.
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
// 후면 버퍼에 대한 렌더 타겟 뷰(ID3D11RenderTargetView)를 생성합니다.
device->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView);
backBuffer->Release();
// 렌더 타겟 뷰를 출력 병합(OM) 단계의 렌더 타겟으로 설정합니다.
context->OMSetRenderTargets(1, &renderTargetView, nullptr);
// 뷰포트 생성 및 설정
D3D11_VIEWPORT viewport;
// 뷰포트 영역의 너비와 높이입니다.
viewport.Width = width;
viewport.Height = height;
// 뷰포트 영역의 깊이 값 범위를 지정합니다.
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
// 뷰포트 영역의 왼쪽 위 모서리의 좌표입니다.
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
// 래스터라이저의 뷰 포트 설정
context->RSSetViewports(1, &viewport);
// 셰이더 및 입력 레이아웃 생성
// 정점 셰이더 컴파일
ID3DBlob* vertexShaderBlob;
result = D3DCompileFromFile(
L"Shader/VertexShader.hlsl", // 대상 파일 문자열 포인터
nullptr, // 전처리기 매크로 정의
nullptr, // 참조하는 다른 파일의 경로
"main", // 진입점 함수 이름
"vs_5_0", // 셰이더 코드의 대상 버전
D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_DEBUG, // 컴파일 옵션
0, // 예약되어 있음 항상 0?
&vertexShaderBlob, // 컴파일된 쉐이더 코드가 저장될 위치
nullptr // 컴파일 에러 메시지 저장 위치
);
if (FAILED(result)) { return; }
// 정점 셰이더 생성
result = device->CreateVertexShader(
vertexShaderBlob->GetBufferPointer(), // HLSL 코드에 대한 포인터입니다.
vertexShaderBlob->GetBufferSize(), // HLSL 코드의 길이입니다.
nullptr, // ID3D11ClassLinkage 인터페이스에 대한 포인터
&vertexShader // ID3D11VertexShader 인터페이스 개체에 대한 포인터입니다.
);
if (FAILED(result)) { return; }
// 픽셀 셰이더 컴파일
ID3DBlob* pixelShaderBlob;
result = D3DCompileFromFile(
L"Shader/PixelShader.hlsl",
nullptr,
nullptr,
"main",
"ps_5_0",
D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_DEBUG,
0,
&pixelShaderBlob,
nullptr
);
if (FAILED(result)) { return; }
// 픽셀 셰이더 생성
result = device->CreatePixelShader(
pixelShaderBlob->GetBufferPointer(),
pixelShaderBlob->GetBufferSize(),
nullptr,
&pixelShader
);
if (FAILED(result)) { return; }
// 인풋 레이아웃 설정
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc(2);
inputLayoutDesc(0).SemanticName = "POSITION";
inputLayoutDesc(0).SemanticIndex = 0;
inputLayoutDesc(0).Format = DXGI_FORMAT_R32G32B32_FLOAT;
inputLayoutDesc(0).InputSlot = 0;
inputLayoutDesc(0).AlignedByteOffset = 0;
inputLayoutDesc(0).InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
inputLayoutDesc(0).InstanceDataStepRate = 0;
inputLayoutDesc(1).SemanticName = "COLOR";
inputLayoutDesc(1).SemanticIndex = 0;
inputLayoutDesc(1).Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
inputLayoutDesc(1).InputSlot = 0;
inputLayoutDesc(1).AlignedByteOffset = 12;
inputLayoutDesc(1).InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
inputLayoutDesc(1).InstanceDataStepRate = 0;
UINT layoutSize = ARRAYSIZE(inputLayoutDesc);
// 인풋 레이아웃 생성
result = device->CreateInputLayout(
inputLayoutDesc, // D3D11_INPUT_ELEMENT_DESC 구조체 배열의 포인터
layoutSize, // 배열의 길이
vertexShaderBlob->GetBufferPointer(), // 쉐이더 코드의 바이트코드입니다.
vertexShaderBlob->GetBufferSize(), // 바이트 수
&inputLayout // 입력 레이아웃의 포인터
);
if (FAILED(result)) { return; }
// 버텍스 정보
vertices.emplace_back(-0.1f, 0.1f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
vertices.emplace_back(0.1f, 0.1f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
vertices.emplace_back(-0.1f, -0.1f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f);
vertices.emplace_back(0.1f, -0.1f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
// 인덱스 정보
indices = {
0, 1, 2, 2, 1, 3
};
stride = sizeof(Vertex); // 구조체 크기
offset = 0;
//
// 버텍스 버퍼 생성
D3D11_BUFFER_DESC vertexBufferDesc = {};
vertexBufferDesc.ByteWidth = stride * vertices.size(); // 버텍스 3개
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; // 동작 방식을 지정
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; // 어떤 버퍼로 사용할지
vertexBufferDesc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE
vertexBufferDesc.MiscFlags = 0; // 리소스의 다른 속성을 지정하는 데 사용됩니다.
// 버텍스 데이터 저장
D3D11_SUBRESOURCE_DATA vertexData = {};
vertexData.pSysMem = vertices.data(); // 초기 데이터가 있는 메모리의 포인터
vertexData.SysMemPitch = 0; // 행 간격을 바이트 단위로 지정
vertexData.SysMemSlicePitch = 0; // 서브리소스의 깊이 슬라이스 간격을 바이트 단위로 지정
// 버텍스 버퍼 생성
device->CreateBuffer(&vertexBufferDesc, &vertexData, &vertexBuffer);
// 인덱스 버퍼 생성
D3D11_BUFFER_DESC indexBufferDesc = {};
indexBufferDesc.ByteWidth = sizeof(UINT) * indices.size(); // 인덱스 3개
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
// 인덱스 데이터 저장
D3D11_SUBRESOURCE_DATA indexData = {};
indexData.pSysMem = indices.data();
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// 인덱스 버퍼 생성
device->CreateBuffer(&indexBufferDesc, &indexData, &indexBuffer);
// 화면 클리어
float clearColor(4) = { 0.0f, 0.125f, 0.3f, 1.0f };
context->ClearRenderTargetView(renderTargetView, clearColor);
// 버텍스, 인덱스 정보 입력 조립
context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0);
context->IASetInputLayout(inputLayout); // VertexShader 정보 적용
context->VSSetShader(vertexShader, nullptr, 0); // VertexShader 적용
context->PSSetShader(pixelShader, nullptr, 0); // PixelShader 적용
context->DrawIndexed(indices.size(), 0, 0); // 설정된 VS, PS를 사용하여 그리기
swapChain->Present(0, 0); // 백 버퍼(Back Buffer)에 그려진 화면을 프론트 버퍼(Front Buffer)로 스왑
// VertexShader.hlsl
struct VertexInput
{
float4 pos : POSITION;
float4 color : COLOR;
};
struct VertexOutput
{
float4 pos : SV_POSITION;
float4 color : COLOR;
};
VertexOutput main(VertexInput input)
{
VertexOutput output;
output.pos = float4(input.pos);
output.color = input.color;
return output;
}
// PixelShader.hlsl
struct PixelInput
{
float4 pos : SV_POSITION;
float4 color : COLOR;
};
float4 main(PixelInput input) : SV_TARGET
{
return input.color;
}