본문 바로가기
Programming/DirectX11

DirectX11 초기화 단계

by Dev_카페인 2023. 3. 19.
반응형

[DirectX11] 초기화 단계

 

DirectX 11을 사용하여 그래픽 애플리케이션을 개발하기 위해서는 몇 가지 초기화 단계를 거쳐야 합니다. 

 

DirectX 11 디바이스(Direct3D 장치) 생성하기

● D3D11CreateDevice 함수를 호출하여 DirectX 11 디바이스를 생성합니다. 이 함수는 지원되는 하드웨어 디바이스를 나열하고, 해당 디바이스 중 하나를 선택하여 DirectX 11 디바이스를 생성합니다.

 

스왑체인(swap chain) 생성하기

● DXGI(DirectX Graphics Infrastructure) 라이브러리를 사용하여 스왑체인을 생성합니다. 스왑체인은 프레임을 출력할 후면 버퍼(back buffer)와 전면 버퍼(front buffer)를 관리하는 역할을 합니다.

 

후면 버퍼에서 렌더링 대상 뷰(Render Target View) 생성하기

● 후면 버퍼를 렌더링 대상으로 지정하기 위해 D3D11RenderTargetView 객체를 생성합니다. 이 객체는 후면 버퍼를 렌더링 대상으로 지정하여 렌더링 작업을 수행하는 데 사용됩니다.

 

깊이 스텐실 뷰(Depth Stencil View) 생성하기

● 깊이 버퍼를 사용하여 렌더링 작업에 깊이 정보를 추가할 수 있습니다. D3D11DepthStencilView 객체를 생성하여 깊이 스텐실 뷰를 생성하고, 후면 버퍼에 대한 깊이 스텐실 뷰를 설정합니다.

 

뷰포트(Viewport) 설정하기

● 뷰포트는 렌더링되는 화면 영역을 지정하는 역할을 합니다. D3D11_VIEWPORT 구조체를 사용하여 뷰포트의 위치, 크기, 최소/최대 깊이 값을 설정합니다.

 

셰이더 컴파일하기

● 셰이더는 DirectX 11에서 렌더링 작업을 처리하는 데 필요한 핵심 요소 중 하나입니다. HLSL(High-Level Shading Language)로 작성된 셰이더 코드를 D3DCompile 함수를 사용하여 컴파일하여 생성합니다.

 

입력 레이아웃(Input Layout) 생성하기

● 입력 레이아웃은 버텍스 데이터의 레이아웃을 정의하는 데 사용됩니다. CreateInputLayout 함수를 사용하여 입력 레이아웃을 생성합니다.

 

버텍스 버퍼(Vertex Buffer) 생성하기

● 버텍스 버퍼는 그래픽 객체의 정점 데이터를 저장하는 메모리 버퍼입니다. D3D11_BUFFER_DESC 구조체를 사용하여 버텍스 버퍼의 속성을 정의하고, D3D11_SUBRESOURCE_DATA 구조체를 사용하여 버텍스 데이터를 초기화합니다. 버텍스 버퍼를 생성하기 위해 ID3D11Device::CreateBuffer 메서드를 호출합니다.

 

상수 버퍼(Constant Buffer) 생성하기

● 상수 버퍼는 셰이더에 전달되는 상수 값을 저장하는 메모리 버퍼입니다. 상수 버퍼를 생성하기 위해 D3D11_BUFFER_DESC 구조체를 사용하여 버퍼의 속성을 정의하고, ID3D11Device::CreateBuffer 메서드를 호출합니다.

 

렌더링 파이프라인(Rendering Pipeline) 설정하기

● 렌더링 파이프라인은 그래픽 객체를 렌더링하는 데 사용되는 일련의 처리 단계를 의미합니다. 이러한 단계는 셰이더, 입력 레이아웃, 버텍스 버퍼, 레스터라이저(Rasterizer), 블렌더(Blender) 등을 포함합니다. 

 

렌더링 루프(Render Loop) 시작하기

● 렌더링 루프는 실제로 그래픽 객체를 렌더링하는 데 사용됩니다. 렌더링 루프에서는 각 프레임마다 렌더링 파이프라인을 설정하고, 그래픽 객체를 렌더링하여 화면에 출력합니다.

 

종료하기

● 애플리케이션 종료 시 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;
}
반응형

'Programming > DirectX11' 카테고리의 다른 글

DirectX11 Device & CreateDevice  (0) 2023.03.19
DirectX11 Draw 관련 메서드  (0) 2023.03.19