Vulkan Tutorial 10 重采样

目录

30 多重采样

获得可用的样本数

设置一个渲染目标

添加新的附件


30 多重采样

我们的程序现在可以为纹理加载多层次的细节,这修复了在渲染离观众较远的物体时出现的假象。现在的图像平滑了许多,然而仔细观察,你会发现在绘制的几何图形的边缘有锯齿状的图案。在我们早期的一个程序中,当我们渲染一个四边形时,这一点尤其明显:

在普通的渲染中,像素的颜色是根据单个采样点确定的,在大多数情况下,这个采样点就是屏幕上目标像素的中心。如果绘制的线条有一部分穿过某个像素点,但没有覆盖到采样点,那么这个像素点就会留下空白,导致锯齿状的 “阶梯”效果。

MSAA所做的是,它使用每个像素的多个采样点(因此而得名)来确定其最终颜色。正如人们所期望的那样,更多的样本会带来更好的结果,但是它的计算成本也更高。

获得可用的样本数

让我们首先确定我们的硬件可以使用多少个样本。大多数现代GPU至少支持8个样本,但这个数字不能保证在任何地方都是一样的。我们将通过添加一个新的类成员来跟踪它:

...
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
...//。准确的最大样本数可以从与我们选定的物理设备相关的VkPhysicalDeviceProperties中提取。
//我们必须考虑到颜色和深度的样本数。两者都支持的最高采样数将是我们能支持的最大限度。
//添加一个函数,为我们获取这些信息:VkSampleCountFlagBits getMaxUsableSampleCount() {VkPhysicalDeviceProperties physicalDeviceProperties;vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }return VK_SAMPLE_COUNT_1_BIT;
}

现在我们将在物理设备选择过程中使用这个函数来设置msaaSamples变量。为此,我们必须稍微修改pickPhysicalDevice函数:

void pickPhysicalDevice() {...for (const auto& device : devices) {if (isDeviceSuitable(device)) {physicalDevice = device;msaaSamples = getMaxUsableSampleCount();break;}}...
}

设置一个渲染目标

在MSAA中,每个像素在屏幕外的缓冲区中被采样,然后被渲染到屏幕上。这个新的缓冲区与我们一直在渲染的普通图像略有不同–它们必须能够存储每个像素的一个以上的样本。一旦多采样缓冲区被创建,它就必须被解析为默认的帧缓冲区(每个像素只存储一个样本)。

//必须创建一个额外的渲染目标并修改我们当前的绘图过程。我们只需要一个渲染目标,因为每次只有一个绘制操作是活动的,就像深度缓冲器一样。添加以下类成员:...
VkImage colorImage;
VkDeviceMemory colorImageMemory;
VkImageView colorImageView;
...//这个新图像将必须存储每个像素所需的样本数,所以我们需要在图像创建过程中把这个数字传给VkImageCreateInfo。修改createImage函数,增加一个numSamples参数:void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {...imageInfo.samples = numSamples;...//使用`VK_SAMPLE_COUNT_1_BIT’更新对该函数的所有调用 - 我们将在实施过程中用适当的值替换它:createImage(swapChainExtent.width, swapChainExtent.height, 1, VK_SAMPLE_COUNT_1_BIT, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
...
createImage(texWidth, texHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);

我们现在将创建一个多采样的颜色缓冲区。添加一个createColorResources函数,注意我们在这里使用msaaSamples作为createImage的一个函数参数。我们也只使用一个mip级别,因为这是Vulkan规范在每个像素有一个以上的样本的情况下强制执行的。另外,这个颜色缓冲区不需要mipmaps,因为它不会被用作纹理:

void createColorResources() {VkFormat colorFormat = swapChainImageFormat;createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, colorFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, colorImage, colorImageMemory);colorImageView = createImageView(colorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
}//在createDepthResources之前调用该函数://我们现在已经创建了几个新的Vulkan资源,所以我们不要忘记在必要时释放它们:void cleanupSwapChain() {vkDestroyImageView(device, colorImageView, nullptr);vkDestroyImage(device, colorImage, nullptr);vkFreeMemory(device, colorImageMemory, nullptr);...
}

添加新的附件

让我们先来处理一下渲染通道的问题。修改createRenderPass并更新颜色和深度附件创建信息结构:

void createRenderPass() {...colorAttachment.samples = msaaSamples;colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
//你会注意到,我们把最终布局从VK_IMAGE_LAYOUT_PRESENT_SRC_KHR改为VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL。
//这是因为多采样图像不能直接呈现。我们首先需要将它们解析为普通图像。
//这个要求并不适用于深度缓冲区,因为它不会在任何时候被呈现。
//因此,我们将不得不为颜色添加一个新的附件,这是一个所谓的解析附件:...depthAttachment.samples = msaaSamples;...VkAttachmentDescription colorAttachmentResolve{};colorAttachmentResolve.format = swapChainImageFormat;colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;...//现在必须指示渲染通道将多采样的彩色图像解析为常规附件。
//创建一个新的附件引用,它将指向作为解析目标的颜色缓冲区:...VkAttachmentReference colorAttachmentResolveRef{};colorAttachmentResolveRef.attachment = 2;colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;//设置pResolveAttachments子通道结构成员,指向新创建的附件引用。subpass.pResolveAttachments = &colorAttachmentResolveRef;...

现在用新的颜色附件更新渲染通道信息结构:

    ...std::array<VkAttachmentDescription, 3> attachments = {colorAttachment, depthAttachment, colorAttachmentResolve};...//渲染通道到位后,修改createFramebuffers并将新的图像视图添加到列表中:
std::array<VkImageView, 3> attachments = {colorImageView,depthImageView,swapChainImageViews[i]};//修改createGraphicsPipeline,告诉新创建的管道使用一个以上的样本:void createGraphicsPipeline() {...multisampling.rasterizationSamples = msaaSamples;...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/8820.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】复杂的菱形继承 及 菱形虚拟继承的底层原理

文章目录 1. 单继承2. 多继承3. 菱形继承3.1 菱形继承的问题——数据冗余和二义性3.2 解决方法——虚拟继承3.3 虚拟继承的原理 4. 继承和组合5. 继承的反思和总结 1. 单继承 在上一篇文章中&#xff0c;我们给大家演示的其实都是单继承。 单继承的概念&#xff1a; 单继承&a…

Flutter如何获取屏幕的分辨率和实际画布的分辨率

Flutter如何获取分辨率 在Flutter中&#xff0c;你可以使用MediaQuery来获取屏幕的分辨率和实际画布的分辨率。 要获取屏幕的分辨率&#xff0c;你可以使用MediaQuery.of(context).size属性&#xff0c;它返回一个Size对象&#xff0c;其中包含屏幕的宽度和高度。下面是一个获…

POSTGRESQL SQL 执行用 IN 还是 EXISTS 还是 ANY

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到3群&#xff08;共…

SQL频率低但笔试会遇到: 触发器、索引、外键约束

一. 前言 在SQL面笔试中&#xff0c;对于表的连接方式&#xff0c;过滤条件&#xff0c;窗口函数等肯定是考察的重中之重&#xff0c;但是有一些偶尔会出现&#xff0c;频率比较低但是至少几乎会遇见一两次的题目&#xff0c;就比如触发器&#xff0c;索引和外键约束&#xff0…

Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean

Spring源码系列文章 Spring源码解析(一)&#xff1a;环境搭建 Spring源码解析(二)&#xff1a; 目录 一、Spring源码基础组件1、bean定义接口体系2、bean工厂接口体系3、ApplicationContext上下文体系 二、AnnotationConfigApplicationContext注解容器1、创建bean工厂-beanFa…

【Nginx05】Nginx学习:HTTP核心模块(二)Server

Nginx学习&#xff1a;HTTP核心模块&#xff08;二&#xff09;Server 第一个重要的子模块就是这个 Server 相关的模块。Server 代表服务的意思&#xff0c;其实就是这个 Nginx 的 HTTP 服务端所能提供的服务。或者更直白点说&#xff0c;就是虚拟主机的配置。通过 Server &…

iview切换Select时选项丢失,重置Seletc时选项丢失

分析原因 在旧版本的iview中如果和filterable一起使用时&#xff0c;当值清空选项或者使用重置按钮清空时选项会丢失。 解决方式一 把去掉filterable 解决方式二 使用ref&#xff0c;调用clearSingleSelect()方法清空 ref"perfSelect" this.$refs.perfSelect.c…

Java链式编程

一、链式编程 1.1.释义 链式编程&#xff0c;也叫级联式编程&#xff0c;调用对象的函数时返回一个this对象指向对象本身&#xff0c;达到链式效果&#xff0c;可以级联调用。 1.2.特点 可以通过一个方法调用多个方法&#xff0c;将多个方法调用链接起来&#xff0c;形成一…

UE4中创建的瞄准偏移或者混合空间无法拖入动画

UE4系列文章目录 文章目录 UE4系列文章目录前言一、解决办法 前言 UE4 AimOffset(瞄准偏移)动画融合时&#xff0c;AimOffse动画拖入不了融合框的解决办法&#xff0c;你会发现动画无法拖入到融合框&#xff0c;ue4编辑器提示“Invalid Additive animation Type”&#xff0c;…

C#核心知识回顾——10.List、Dictionary、数据结构

1.List List<int> list new List<int>(); List<String> strings new List<String>();//增list.Add(0);list.Add(1);List<int> ints new List<int>();ints.Add(0);list.AddRange(ints);//插入list.Insert(0, 1);// 位置0插入1//删//1.移…

使用GPIO来模拟UART

前言 最近在看一些秋招的笔试和面试题&#xff0c;刚好看到一个老哥的经验贴&#xff0c;他面试的时候被问到了如果芯片串口资源不够了该怎么办&#xff1f;其实可以用IO口来模拟串口&#xff0c;但我之前也没有具体用代码实现过&#xff0c;借此机会用32开发板上的两个IO口来…

从0开始,手写MySQL数据管理器DM

说在前面 从0开始&#xff0c;手写一个MySQL的学习价值在于&#xff1a; 可以深入地理解MySQL的内部机制和原理&#xff0c;MySQL可谓是面试的绝对重点和难点&#xff0c; 尼恩曾经指导过的一个7年经验小伙&#xff0c;凭借精通MySQL 搞定月薪40K。 从而更好地掌握MySQL的使…