Vulkan教程翻译之八 创建 Uniform Buffer

本文详细介绍了在Vulkan图形API中创建和使用Uniform Buffer的过程。Uniform Buffer用于存储shader可读取的静态参数,如MVP矩阵。文章涵盖了Uniform Buffer的创建、内存分配、数据映射及设置等内容。

原文链接:https://vulkan.lunarg.com/doc/sdk/1.2.131.2/windows/tutorial/html/07-init_uniform_buffer.html

创建 Uniform Buffer

这一章节的代码文件是 07-init_uniform_buffer.cpp

你在最近的示例中已经创建过缓冲区,不妨现在再处理另一个。

uniform buffer 是一个面向 shader 以只读方式访问的缓冲区,以使 shader 能够读取静态参数的数据。

这又是一个例子,这一步是你在 Vulkan 程序里必须处理的,在其他图形API里就不必做。在 GLES 里,你只要简单的调一下API来设置发送给 shader 的 uniform 变量的内容。但是在这,你必须分配内存并且填写。

生成 Uniform Data

示例使用 uniform buffer 来传递 MVP(Model-View-Projection) 矩阵给 shader ,好让 shader 可以使用矩阵来转换顶点。

示例里像这样来生成数据:

info.Projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);
info.View = glm::lookAt(
    glm::vec3(-5, 3, -10), // Camera is at (-5,3,-10), in World Space
    glm::vec3(0, 0, 0),    // and looks at the origin
    glm::vec3(0, -1, 0)    // Head is up (set to 0,-1,0 to look upside-down)
    );
info.Model = glm::mat4(1.0f);
// Vulkan clip space has inverted Y and half Z.
info.Clip = glm::mat4(1.0f,  0.0f, 0.0f, 0.0f,
                      0.0f, -1.0f, 0.0f, 0.0f,
                      0.0f,  0.0f, 0.5f, 0.0f,
                      0.0f,  0.0f, 0.5f, 1.0f);

info.MVP = info.Clip * info.Projection * info.View * info.Model;

请注意,这里用 glm 库来简化代码。info.MVP 是一个 4x4 矩阵。

创建 Uniform Buffer 对象

创建这个 buffer 和在前一个示例里你如何创建 depth buffer 非常相似,只是改变一下用法:

VkBufferCreateInfo buf_info = {};
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buf_info.pNext = NULL;
buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
buf_info.size = sizeof(info.MVP);
buf_info.queueFamilyIndexCount = 0;
buf_info.pQueueFamilyIndices = NULL;
buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buf_info.flags = 0;
res = vkCreateBuffer(info.device, &buf_info, NULL, &info.uniform_data.buf);
assert(res == VK_SUCCESS);

分配 Uniform Buffer 内存

像 depth buffer 一样,你需要为 uniform buffer 显式地分配内存:

VkMemoryRequirements mem_reqs;
vkGetBufferMemoryRequirements(info.device, info.uniform_data.buf,
                              &mem_reqs);

VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = NULL;
alloc_info.memoryTypeIndex = 0;

alloc_info.allocationSize = mem_reqs.size;
pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits,
                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
                                       VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                                   &alloc_info.memoryTypeIndex);

res = vkAllocateMemory(info.device, &alloc_info, NULL,
                       &(info.uniform_data.mem));

VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 表示该内存应该被映射,以使 CPU(host) 能够访问它。

VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 要求主机(host)对内存的写入对设备(device)可见(反之亦然),而不需要刷新内存的缓存。这对程序来说能简单一点,因为不必再调用 vkFlushMappedMemoryRanges 和 vkInvalidateMappedMemoryRanges 来确保数据对 GPU 可见。

映射和设置 Uniform Buffer 内存

在上一个示例中,在你分配 depth buffer 内存时,你不需要初始化它的内容。这是因为 GPU 负责读写它。但是对于 uniform buffer,你需要填充你想让 shader 读取的数据。当前情况下,数据是 MVP 矩阵。为了让 CPU 能访问内存,你需要映射:

res = vkMapMemory(info.device, info.uniform_data.mem, 0, mem_reqs.size, 0,
                  (void **)&pData);

拷贝 MVP 到 uniform buffer 里然后再取消映射就是相当简单的操作了:

memcpy(pData, &info.MVP, sizeof(info.MVP));

vkUnmapMemory(info.device, info.uniform_data.mem);

现在你就可以取消映射,因为我们在画一个静态的画面,不需要再更新MVP矩阵。但是如果你计划扩展这个示例来改变视野,保持着映射更高效。

最后,把你刚刚分配的内存和 buffer 对象关联起来:

res = vkBindBufferMemory(info.device, info.uniform_data.buf,
                         info.uniform_data.mem, 0);

好了,完成了。

### 代码概述 以下是Unity中开启`VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT`的最佳设置代码,适用于Vulkan API的统一缓冲区(Uniform Buffer)使用场景。此代码展示了如何正确配置和创建一个具有统一缓冲区用途的缓冲区,并确保其与Vulkan API兼容。 ```csharp using UnityEngine; public class VulkanUniformBufferSetup : MonoBehaviour { private ComputeBuffer uniformBuffer; void Start() { if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan) { // 设置缓冲区大小和每个元素的字节大小 int bufferSize = 1024; // 缓冲区大小(字节数) int stride = 16; // 每个元素的对齐字节数(通常是16字节对齐) // 创建带有 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT 标志的缓冲区 uniformBuffer = new ComputeBuffer(bufferSize / stride, stride, ComputeBufferType.Default); // 如果需要初始化缓冲区内容,可以在这里设置数据 // float[] uniformData = new float[bufferSize / sizeof(float)]; // uniformBuffer.SetData(uniformData); // 将缓冲区绑定到材质或计算着色器 Material material = new Material(Shader.Find("YourShaderName")); material.SetBuffer("YourUniformBufferName", uniformBuffer); Debug.Log("Vulkan Uniform Buffer Created with VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT!"); } else { Debug.LogWarning("Current Graphics API is not Vulkan."); } } void OnDestroy() { if (uniformBuffer != null) { uniformBuffer.Release(); Debug.Log("Vulkan Uniform Buffer Released."); } } } ``` --- ### 代码解析 1. **判断API类型**:通过`SystemInfo.graphicsDeviceType`检测当前是否使用Vulkan API。 2. **创建缓冲区**:调用`ComputeBuffer`构造函数时,指定第三个参数为`ComputeBufferType.Default`,并结合适当的对齐方式(如16字节对齐),以确保缓冲区适合作为统一缓冲区。 3. **初始化缓冲区内容**(可选):如果需要在创建时初始化缓冲区内容,可以使用`SetData`方法设置数据。 4. **绑定缓冲区**:将创建的缓冲区绑定到材质或计算着色器中,以便在渲染过程中使用。 5. **释放资源**:在对象销毁时,通过`OnDestroy`方法释放缓冲区资源,避免内存泄漏。 6. **调试输出**:如果当前API不是Vulkan,则给出警告提示;成功创建或释放缓冲区时,输出相应的日志信息。 --- ### 知识点 1. **Vulkan API** Vulkan是现代图形API,支持精细的资源管理,特别适合高性能图形应用。 2. **统一缓冲区 (Uniform Buffer)** 用于存储着色器所需的常量数据,如矩阵、光照参数等,提高渲染效率。 3. **资源管理** 正确创建和释放GPU资源,防止内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值