Scientia Conditorium

[Vulkan004][번역] Vulkan Tutorial - 삼각형 그리기 기본 코드(Drawing a triangle - Base code) 본문

프로그래밍/컴퓨터 그래픽스

[Vulkan004][번역] Vulkan Tutorial - 삼각형 그리기 기본 코드(Drawing a triangle - Base code)

크썸 2023. 11. 18. 18:34

원문 : https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Base_code

[Vulkan004][번역] Vulkan Tutorial - 삼각형 그리기 기본 코드(Drawing a triangle - Base code)

 

Base code - Vulkan Tutorial

General structure In the previous chapter you've created a Vulkan project with all of the proper configuration and tested it with the sample code. In this chapter we're starting from scratch with the following code: #include #include #include #include clas

vulkan-tutorial.com

 


 

전체 구조

이전 장에서는 모든 적절한 구성이 포함된 Vulkan 프로젝트를 생성하고 예시 코드로 테스트했습니다. 이 장에서는 다음 코드로 처음부터 다시 시작하겠습니다.

#include <vulkan/vulkan.h>

#include <iostream>
#include <stdexcept>
#include <cstdlib>

class HelloTriangleApplication {
public:
    void run() {
        initVulkan();
        mainLoop();
        cleanup();
    }

private:
    void initVulkan() {

    }

    void mainLoop() {

    }

    void cleanup() {

    }
};

int main() {
    HelloTriangleApplication app;

    try {
        app.run();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

 

먼저 함수, 구조체와 열거형을 제공하는 LunarG SDK의 Vulkan 헤더를 포함합니다.  stdexcept  와  iostream  헤더는 입출력과 예외 처리를 위해 사용됩니다.  cstdlib  헤더는  EXIT_SUCCESS  와  EXIT_FAIURE  매크로를 제공합니다.

 

프로그램 자체는 클래스로 래핑되어 Vulkan 개체들을 private 멤버와 함수로 저장하고 각 개체를 시작하는 함수를 추가할 것입니다. 이 함수는  initVulkan  함수에서 호출될 것입니다. 일단 모든 준비가 완료되면, 메인 루프에 들어가 프레임 렌더링을 시작합니다. 잠시 후 창이 닫힐 때까지 반복하는 루프를 포함하도록  mainLoop  함수를 채우겠습니다. 창이 닫히고  mainLoop  가 반환되면  cleanup  함수에서 사용한 리소스를 할당 해제합니다. 

 

실행 중에 치명적인 에러가 발생하면 설명 메시지와 함께  std::runtime_error  예외가 발생하고, 이 예외는 다시 메인 함수로 전파되어 명령 프롬프트에 출력됩니다. 다양한 표준 예외 유형도 처리하기 위해 보다 일반적인  std::exception  을 포착합니다. 곧 처리하게 될 오류의 한 가지 예는 특정 필수 확장이 지원되지 않는다는 것을 발견하는 것입니다.

 

이 장 이후에 이어지는 거의 모든 장에서는  initVulkan  에서 호출되는 새 함수를 하나씩 추가하고, 마지막에  cleanup  함수에서 해제해야 하는 private 클래스 멤버에 하나 이상의 새 Vulkan 개체를 추가할 것입니다.

 

리소스 관리(Resource management)

 malloc  함수로 할당된 각 메모리들 중 못 쓰는 것들(chunk)은  free  함수 호출이 필요한 것처럼, 우리가 생성하는 모든 Vulkan 개체는 더 이상 필요하지 않을 때 명시적으로 소멸해야 합니다. C++에는  <memory>  헤더에 제공된 RAII 또는 스마트 포인터를 사용하여 자동 리소스 관리를 수행할 수 있습니다. 하지만 이 튜토리얼에서는 Vulkan 개체의 할당 및 해제를 명시하기로 했습니다. 결국 Vulkan의 역할은 실수를 피하기 위해 모든 작업에 대해 명시적인 것이므로  API 작동 방식을 배우려면 개체의 수명에 대해 명시적으로 기술하는 것이 좋습니다. 

 

이 튜토리얼을 따라한 후에는 생성자에서 Vulkan 개체를 획득하고, 소멸자에서 해제하는 C++ 클래스를 작성하거나 소유권 요구 사항에 따라  std::unique_ptr  또는  std::shared_ptr  에 사용자 정의 삭제자를 제공함으로써 자동 리소스 관리를 구현할 수 있습니다. RAII는 대규모 Vulkan 프로그램에 권장되는 모델이지만, 학습 목적으로는 백그라운드에서 어떤 일이 일어나는지 알아두는 것이 좋습니다.

 

Vulkan 개체는  vkCreateXXX  와 같은 함수를 사용하여 직접 생성하거나  vkAllocateXXX  와 같은 함수를 사용하여 다른 개체를 통해 할당합니다. 개체가 더 이상 사용되지 않는지 확인한 후에는 해당 함수에 대응하는  vkDestoryXXX  및  vkFreeXXX  를 사용하여 개체를 소멸해야 합니다. 이러한 함수의 매개 변수는 일반적으로 개체 유형에 따라 다르지만 모두 공유하는 매개 변수가 하나 있습니다:  pAllocator . 이 매개 변수는 사용자 정의 메모리 할당자에 대한 콜백을 지정할 수 있는 선택적 매개변수입니다. 튜토리얼에서는 이 매개 변수를 무시하고 항상  nullptr  을 인수로 전달하겠습니다.

 

GLFW 연동(Integrating GLFW)

Vulkan은 오프스크린 렌더링에 사용하려는 경우 창을 만들지 않고도 완벽하게 작동하지만, 실제로 무언가를 표시하는 것이 훨씬 더 흥미롭습니다! 먼저  #include <vulkan/vulkan.h>  줄을 다음과 같이 바꿉니다.

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

 

이렇게 하면 GLFW에 자체 정의가 포함되고 Vulkan 헤더가 자동으로 로드됩니다.  initWindow  함수를 추가하고 다른 호출 전에  run  함수에서 이 함수에 대한 호출을 추가합니다. 이 함수를 사용하여 GLFW를 초기화하고 창을 생성합니다.

void run() {
    initWindow();
    initVulkan();
    mainLoop();
    cleanup();
}

private:
    void initWindow() {

    }

 

 initWindow  의 첫 번째 호출은 GLFW 라이브러리를 초기화하는  glfwInit()  이어야 합니다. GLFW는 원래 OpenGL 컨텍스트를 생성하도록 설계되었으므로 후속 호출에서 OpenGL 컨텍스트를 생성하지 않도록 지시해야 합니다.
(일반저으로 OpenGL의 상태를 OpenGL context라고 합니다.)

glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);

크기가 조정된 창을 처리하는 데는 특별한 주의가 필요하여 나중에 살펴볼테니, 지금은 다른 창 힌트 호출을 사용하여 비활성화하세요:

glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

이제 남은 것은 실제 창을 만드는 것입니다. 이에 대한 참조를 저장하고 창을 초기화할 private 클래스 멤버로  GLFWwindow* window  를 추가합니다.

window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr);

처음 세 개의 매개변수는 창의 너비, 높이 및 제목을 지정합니다. 네 번째 매개변수는 창을 열 모니터를 선택적으로 지정할 수 있으며, 마지막 매개변수는 OpenGL에만 해당됩니다.

 

앞으로 이 값을 몇 번 참조할 것이므로 하드코딩된 너비 및 높이 숫자 대신 상수를 사용하는 것이 좋습니다.  HelloTriangleApplication  클래스 정의 위에 다음 줄을 추가합니다.

const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;

그리고 다음과 같이 창 생성 호출을 수정합니다.

window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);

이제 다음과 같은  initWindow  함수가 생겼을 것입니다.

void initWindow() {
    glfwInit();

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

    window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}

오류가 발생하거나 창이 닫힐 때까지 응용프로그램을 계속 실행하려면 다음과 같이  mainLoop  함수에 이벤트 루프를 추가해야 합니다.

void mainLoop() {
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
    }
}

이 코드는 설명이 필요 없을 정도로 간단합니다. 이 코드는 사용자가 창을 닫을 때까지 X 버튼을 누르는 등의 이벤트를 반복해서 확인합니다. 이 루프는 나중에 단일 프레임을 렌더링하는 함수를 호출할 떄도 사용됩니다.

 

창이 닫히면 창을 삭제하고 GLFW 자체를 종료하여 리소스를 정리해야 합니다. 이것이 첫 번째  cleanup  코드가 될 것입니다.

void cleanup() {
    glfwDestroyWindow(window);

    glfwTerminate();
}

이제 프로그램을 실행하면 창을 닫아 응용프로그램을 종료할 때까지  Vulkan  이라는 제목의 창이 표시될 것입니다. 이제 Vulkan 응용프로그램의 골격이 완성되었으니 첫 번째 Vulkan 개체를 생성해보겠습니다.

 

이번 장 C++ 전체 코드


별도 코드

개인적으로 try-catch를 사용하는 것을 선호하지 않습니다. 그런데 이 Vulkan 예제 코드는 시작부터 try-catch를 사용합니다. 따라서 개인적인 공부도 할겸 다른 방식으로 코드를 구현하여 공유드립니다.

 

https://github.com/twoo0220/vulkanTW