Vulkan教程 – 07 图像视图和图形管线

图像视图

使用任何的VkImage,包括在交换链中的,我们都要在渲染管线中创建VkImageView对象。图像视图描述了如何获取图像,获取图像的哪个部分,比如它是否应该被当作2D贴图或深度贴图对待而没有任何mipmapping等级。

本章我们写一个createImageViews方法,为交换链中每个图像创建一个基础图像视图,以便我们后续用来作为颜色目标。首先添加一个类成员存储图像视图:

std::vector<VkImageView> swapChainImageViews;

创建一个createImageViews方法,在调用创建交换链之后调用它。第一件事就是重新调整列表大小以便存放我们将要创建的所有的图像视图:

swapChainImageViews.resize(swapChainImages.size());

接着,建立一个循环来遍历整个交换链图像:

for (size_t i = 0; i < swapChainImages.size(); i++) {

}

图像视图创建的参数在VkImageViewCreateInfo中,最开始的几个参数比较直白:

VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = swapChainImages[i];

viewType和format表明了如何解读图像数据,viewType参数允许你将图像作为1D,2D,3D材质和cube map对待:

createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapChainImageFormat;

components部分允许你将颜色通道混合起来,比如,你可以将所有通道都映射到红色通道,形成单色材质。我们还是用默认映射:

createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;

subresourceRange描述了图像的意图以及哪些部分能被访问,我们的图像将会被用作颜色目标,而不用任何mipmapping等级或者多层级:

createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;

如果你在开发三维立体应用,那么你要创建有多层的交换链。然后你可以为每个图像创建多个图像视图,通过访问不同的层来表示左右眼的视图。现在创建图像视图就是一句调用的事情了:

if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
    throw std::runtime_error("failed to create image views!");
}

和图像不一样,图像视图是由我们显式创建的,送一我们要添加一个类似的循环清理掉:

void cleanup() {
    for (auto imageView : swapChainImageViews) {
        vkDestroyImageView(device, imageView, nullptr);
    }
    ...
}

一个图像视图就足够支持使用图像作为材质了,但是作为渲染对象还不太够。这还需要一步操作就是framebuffer,但是在此之前还需要建立起图形管线才行。

图形管线

接下来的几章,我们会建立图形管线,然后配置好就能画出我们第一个三角形了。图形管线是一系列操作的集合,将mesh的顶点、材质送到渲染目标的像素上。简单来说,它的大致流程如下:

输入组装器(input assembler)从你指定的缓冲中收集原始顶点数据,也可能使用索引缓冲(index buffer)来重复某些元素,避免复制顶点数据本身。

顶点着色器(vertex shader)会把每个顶点都运行一遍,就是将顶点位置从模型空间变换到屏幕空间,它也会将每个顶点数据传递给管线。

曲面细分着色器(tessellation shader)会让你将几何元素再分割,提高网格质量。这常用于在靠近时将砖墙和楼梯变得不那么平整。

几何着色器(geometry shader)会在每个图元(primitive,例如三角面片、线、点)上运行一遍,可以丢弃它或者得到比输入的图元更多的输出。这和曲面细分有点类似,但是却更加便捷。但是这个在当今的程序中用的不多,因为基本都不能很好运行,除非是英特尔的集成显卡。

光栅化(rasterization)阶段将图元离散化为片段(fragment),这些则是填充帧缓冲的像素元素。任何不在屏幕内的片段就会被丢弃,而且由顶点着色器输出的属性会在片段上就行插值。通常由于深度测试原因,在原始片段后的片段会被丢弃。

片段着色器(fragment shader)每个留存下来的片段都会调用,并且决定了片段写到哪个帧缓冲去,也决定了颜色和深度值。它可以通过顶点着色器的插值数据,如光照所需的材质坐标和法线,来做该操作。

颜色混合(color blending)阶段应用一些操作来将帧缓冲中映射到统一像素的不同片段混合起来。片段可以简单地相互重写,相加或者基于透明度进行混合。

绿色的阶段就是固定管线(fixed-function)阶段。这些阶段能让你通过参数调整一些操作,但是其本身工作确是预定义好的。

橙色阶段就是可编程的,表明你可以上传自己的代码到显卡中来应用你想要的操作。这能让你使用片段着色器,比如实现从材质和光照到光线追踪的任何东西。这些操作在多个GPU核心上同时运行来处理多个对象,比如并行地处理顶点和片段。

如果你曾用过较老的API如OpenGL和Direct3D,那么你可以用glBlendFunc和OMSetBlendState类似的调用来改变任何管线设置。而Vulkan的图形管线则基本是完全不能改变的,所以你想要改着色器,绑定不同帧缓冲或者改变混合函数,就要从头创建一个管线。其缺点是你必须创建很多管线,代表所有你想要的不同的组合状态。但是由于你的操作都是预先知道的,驱动能很好地进行优化。

某些可编程阶段是可选的,这基于你想要做什么。例如曲面细分和几何阶段可以禁用,如果你仅仅是画简单的几何图形。如果你只对深度值感兴趣,你可以禁用片段着色器阶段,这对生成阴影图很有用。

下一章我们会创建两个可编程阶段,以便将三角形绘制到屏幕的时候用:顶点着色器和片段着色器。固定管线配置如混合模式,视口,光栅化将会在其后的章节建立。最后阶段是建立图形管线,涉及到输入明细和输出帧缓冲。建立一个方法,以便在initVulkan的createImageViews后调用,方法名为createGraphicsPipeline:

void initVulkan() {
    createInstance();
    setupDebugMessenger();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createGraphicsPipeline();
}

该函数目前留空即可。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片