[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头文件vulkan/vulkan.h,引入了函数、结构体和枚举。stdexceptiostream用来使用异常和打印日志。cstdlib引入EXIT_SUCCESSEXIT_FAILURE

我们将程序封装为一个类,将Vulkan对象作为其私有成员,同时创建一些函数初始化它们,在initVulkan中调用它。所有都准备好后,我们就进入主循环来渲染画面。我们在mainLoop中创建一个无限循环,直到窗口退出。窗口退出,mainLoop返回之后,我们使用cleanup函数来释放资源。

运行时发生任何异常,我们就抛出一个附带描述性信息的std::runtime_error异常。它会被main函数收到并打印出来。我们用std::exception接受类型更全面的异常类型。我们很快就会接触到一个异常的例子,它在程序未找到需要的扩展时产生。

资源管理

mallocfree一样,每个我们创建的Vulkan对象,在不需要时都需要被显式销毁。在C++中,你可以使用RAII机制或memory提供的智能指针来完成资源管理工作。不过,在本教程中,我们选择显式分配或销毁Vulkan对象,毕竟,Vulkan重要的特性就是显式操作以避免出错。所以,明白Vulkan对象的生命周期对于学习API如何工作非常有利。

本教程之后,你可以实现自动的内存管理机制。比如,编写自定义类型,在构造函数时获取Vulkan对象,在析构函数时释放对象。又比如,你可以使用std::unique_ptrstd::shared_ptr。这取决与你的需求。RAII是大型Vulkan程序的推荐模型。不过对于学习而言,了解幕后发生的事情总是有好处的。

Vulkan对象要么是通过vkCreateXXX直接创建的,要么是通过其它对象使用vkAllocateXXX分配的。当一个对象不再使用后,你应该使用对应的vkDestroyXXXvkFreeXXX销毁它。这些函数的参数一般是不同的,但它们都有一个共同的参数:pAllocator。它是一个可选的参数,它允许你自定义内存分配器。在本教程中,我们会忽略它并始终传入nullptr

集成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中,我们首先调用glfwInit()来初始化GLFW库。GLFW一开始是用来创建OpenGL上下文的,我们需要告诉它不要这样做:

glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);

改变窗口大小需要我们有一些特别的操作,之后会讨论,我们现在先禁止窗口改变大小:

glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

接下来就是创建一个实际的窗口。添加一个私有成员变量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();
    }
}

这段代码不言自明。它检查诸如键盘事件等直到窗口被用户关闭。这里也是我们调用绘制一帧函数的地方。

窗口关闭之后,我们需要清理资源,结束GLFW,现在的cleanup是这样的:

void cleanup() {
    glfwDestroyWindow(window);

    glfwTerminate();
}

现在运行程序的话,会显示一个叫Vulkan的窗口。程序会一直运行,直到窗口关闭。现在,Vulkan程序的基本结构已经有了,接下来就是创建第一个Vulkan对象了。

目录
上一节 开发环境
下一节 绘制一个三角形/设置/实例(Instance)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值