一、着色器代码更新及构建时自动编译着色器脚本

用内存中的顶点缓冲区替换顶点着色器中硬编码的顶点数据

之前的顶点着色器:

#version 450layout(location = 0) out vec3 fragColor;// 顶点数据硬编码
vec2 positions[3] = vec2[](vec2(0.0, -0.5),vec2(0.5, 0.5),vec2(-0.5, 0.5)
);// 颜色数据硬编码
vec3 colors[3] = vec3[](vec3(1.0, 0.0, 0.0),vec3(0.0, 1.0, 0.0),vec3(0.0, 0.0, 1.0)
);void main() {gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);fragColor = colors[gl_VertexIndex];
}

现在的顶点着色器:

#version 450layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;layout(location = 0) out vec3 fragColor;void main() {gl_Position = vec4(inPosition, 0.0, 1.0);fragColor = inColor;
}

每次改动着色器都手动输入glslc 命令把着色器编译为 spv 字节码有些效率低下,可在 CMake 构建阶段自动调用 glslc 命令编译着色器,修改 CMakeLists.txt :

# ------------后面追加修改的配置部分-----------# 查找所有着色器源文件
file(GLOB SHADER_SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/*.vert" "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/*.frag")
set(SHADER_SPV_FILES "")
foreach(SHADER ${SHADER_SRC_FILES})message("compile shader file "${SHADER})get_filename_component(FILE_NAME ${SHADER} NAME)set(SPV "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/${FILE_NAME}.spv")message("spv path: "${SPV})add_custom_command(OUTPUT ${SPV}COMMAND glslc ${SHADER} -o ${SPV}DEPENDS ${SHADER}COMMENT "Compiling shader ${FILE_NAME}")list(APPEND SHADER_SPV_FILES ${SPV})
endforeach()add_custom_target(CompileShaders ALL DEPENDS ${SHADER_SPV_FILES})add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy_directory${CMAKE_CURRENT_SOURCE_DIR}/assets$<TARGET_FILE_DIR:${PROJECT_NAME}>/assetsCOMMENT "Copying assets to build directory..."
)

二、顶点数据与绑定描述

创建一个VkTypes.h头文件,添加顶点数据结构体 Vertex,顶点/颜色都作为属性,这称为交错顶点属性。

#pragma once
#include <vulkan/vulkan.h>#include <array>
#include <glm/glm.hpp>namespace renderer {  // 渲染器相关类和结构的命名空间// 顶点数据结构,包含位置和颜色信息struct Vertex {glm::vec2 pos;  // 二维坐标位置(x,y)glm::vec3 color;  // RGB颜色值// 获取顶点输入绑定描述// 描述顶点数据如何按字节偏移量组织在内存中static VkVertexInputBindingDescription getBindingDescription() {VkVertexInputBindingDescription bindingDescription{};bindingDescription.binding = 0;  // 绑定点索引bindingDescription.stride = sizeof(Vertex);  // 顶点数据大小(字节)bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;  // 按顶点处理数据return bindingDescription;}// 获取顶点属性描述数组// 描述如何从顶点数据中提取各个属性static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() {std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{};// 位置属性描述attributeDescriptions[0].binding = 0;  // 对应绑定点索引attributeDescriptions[0].location = 0;  // 在顶点着色器中对应的locationattributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;  // 32位浮点数x2 (x,y)attributeDescriptions[0].offset = offsetof(Vertex, pos);  // 位置属性的内存偏移// 颜色属性描述attributeDescriptions[1].binding = 0;  // 对应绑定点索引attributeDescriptions[1].location = 1;  // 在顶点着色器中对应的locationattributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;  // 32位浮点数x3 (R,G,B)attributeDescriptions[1].offset = offsetof(Vertex, color);  // 颜色属性的内存偏移return attributeDescriptions;}};}  // namespace renderer
  1. Vertex 结构:包含顶点的位置 (pos) 和颜色 (color) 数据
  2. getBindingDescription
    • 告诉 Vulkan 顶点数据如何组织
    • stride 设置为整个 Vertex 大小,表示每个顶点数据是连续排列的
  3. getAttributeDescriptions
    • 定义每个属性的存储格式和内存偏移
    • pos 使用 2 个 32 位浮点数 (VK_FORMAT_R32G32_SFLOAT)
    • color 使用 3 个 32 位浮点数 (VK_FORMAT_R32G32B32_SFLOAT)
  4. offsetof 宏:计算结构体成员相对于起始地址的字节偏移量

三、顶点缓冲

可见缓冲区并使用 memcpy 将顶点数据直接复制到其中的最简单方法开始,之后我们将了解如何使用暂存缓冲区将顶点数据复制到高性能内存中。

在 vkInit 中添加创建顶点缓冲代码:

// 创建命令池
{...
}// 创建顶点缓冲
{// 1. 创建顶点缓冲区对象VkBufferCreateInfo bufferInfo{};bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; // 结构体类型bufferInfo.size = vkcontext->vertexBufferSize; // 缓冲区大小(字节)bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; // 用作顶点缓冲区bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // 独占模式(单队列族访问)// 创建缓冲区if (vkCreateBuffer(vkcontext->device, &bufferInfo, nullptr, &vkcontext->vertexBuffer) != VK_SUCCESS) {throw std::runtime_error("顶点缓冲创建失败!");} // 2. 查询缓冲区内存需求VkMemoryRequirements memRequirements;vkGetBufferMemoryRequirements(vkcontext->device, vkcontext->vertexBuffer, &memRequirements);// 3. 分配内存VkMemoryAllocateInfo allocInfo{};allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; // 结构体类型allocInfo.allocationSize = memRequirements.size; // 分配大小与需求一致// 查找合适的内存类型:主机可见(可映射CPU内存)且具有一致性allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, // 可用内存类型位掩码VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, // 内存属性vkcontext->physicalDevice // 物理设备);// 分配内存if (vkAllocateMemory(vkcontext->device, &allocInfo, nullptr, &vkcontext->vertexBufferMemory) != VK_SUCCESS) {throw std::runtime_error("为顶点缓冲分配内存失败!");}// 4. 将内存绑定到缓冲区vkBindBufferMemory(vkcontext->device, vkcontext->vertexBuffer, vkcontext->vertexBufferMemory, 0);// 5. 向顶点缓冲区写入数据void* data;// 映射内存到CPU可访问的地址空间vkMapMemory(vkcontext->device, vkcontext->vertexBufferMemory, 0, bufferInfo.size, 0, &data);// 使用memcpy复制顶点数据到映射的内存区域memcpy(data, vkcontext->vertexData, (size_t) bufferInfo.size);// 解除内存映射(数据已提交)vkUnmapMemory(vkcontext->device, vkcontext->vertexBufferMemory);// 注:由于使用了VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,// 无需额外的内存屏障来确保GPU可见性
}// 创建命令缓冲
{...
}

关键步骤解析:

  1. 创建缓冲区对象

    • 指定缓冲区大小和用途(顶点缓冲区)
    • 分享模式设为独占,因为通常只由图形队列族访问
  2. 查询内存需求

    • Vulkan 要求先查询缓冲区的内存需求(大小、对齐和可用内存类型)
  3. 分配内存

    • 创建 CPU 使用 findMemoryType 函数查找符合条件的内存类型
    • 选择主机可见内存以便 CPU 可以写入数据
    • 选择一致性内存以避免手动刷新内存范围
  4. 绑定内存到缓冲区

    • 将分配的内存块与缓冲区对象关联
  5. 数据传输

    • 映射内存到 CPU 地址空间
    • 使用memcpy复制顶点数据
    • 解除映射,完成数据传输

四、绑定顶点缓冲

修改HelloTriangle类,添加顶点颜色数据:

--------------HelloTriangle.h----------
#pragma once
#include <vector>#include "renderer/VkTypes.h"using namespace renderer;class HelloTriangle {
public:HelloTriangle();std::vector<Vertex> vertices;
};-------------HelloTriangle.cpp-------------
#include "HelloTriangle.h"HelloTriangle::HelloTriangle(): vertices({{{0.0f, -0.5f}, {1.0f, 1.0f, 1.0f}},{{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}},{{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}
}) {}

在VkContext 中新增成员变量:

struct VkContext {...void* vertexData;uint32_t vertexNum{0};size_t vertexBufferSize{0};VkBuffer vertexBuffer;VkDeviceMemory vertexBufferMemory;};

在修改主函数逻辑:

int main() {initWindow();vkcontext.window = window;HelloTriangle helloTriangleApp;vkcontext.vertexData = helloTriangleApp.vertices.data();vkcontext.vertexNum = helloTriangleApp.vertices.size();vkcontext.vertexBufferSize = helloTriangleApp.vertices.size() * sizeof(Vertex);if (!vkInit(&vkcontext)) {throw std::runtime_error("Vulkan 初始化失败!");}try {while (!glfwWindowShouldClose(window)) {glfwPollEvents();vkRender(&vkcontext);}vkDeviceWaitIdle(vkcontext.device);vkClean(&vkcontext);glfwDestroyWindow(window);glfwTerminate();} catch (const std::exception& e) {std::cerr << e.what() << std::endl;return EXIT_FAILURE;}return EXIT_SUCCESS;
}

修改命令录制部分,绑定顶点缓冲:

VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = vkcontext->swapChainExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);// --------修改部分开始-----------------------------
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vkcontext->vertexBuffer, offsets);vkCmdDraw(commandBuffer, vkcontext->vertexNum, 1, 0, 0);
//---------修改部分结束--------------------------------------
vkCmdEndRenderPass(commandBuffer);

现在修改下顶点颜色值,构建运行看看效果:

一切正常,Validation Layer 也没有输出错误日志!

当前代码分支为 06_vertexInputdescription_vertexbuffer

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

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

相关文章

Day04_数据结构(栈链栈循环队列)

01.栈 main.c #include "stack.h" int main() { stack_p S(stack_p)create_stack(); //1.入栈 …

PyTorch 的 CUDA GPU 支持 · 安装五条铁律(最新版 2025 修订)(适用于所有用户)

相关参考资料&#xff08;往期博客&#xff09;&#xff1a; 是否需要预先安装 CUDA Toolkit&#xff1f;——按使用场景分级推荐及进阶说明-CSDN博客 太方便&#xff0c;WIN系统CUDA12.4下使用conda便捷管理虚拟环境中的不同版本的CUDA、cuDNN、PyTorch-CSDN博客 好消息&#…

Django构建简易视频编辑管理系统

Django构建简易视频编辑管理系统 以下是基于Django构建简易视频编辑管理系统的可运行代码框架&#xff0c;包含核心功能模块和实现逻辑。该系统支持视频上传、基本剪辑操作和管理功能。 环境准备 安装必要依赖包&#xff1a; pip install django pillow moviepy django-cri…

Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构

Java求职者面试题详解&#xff1a;计算机网络、操作系统、设计模式与数据结构 第一轮&#xff1a;基础概念问题 1. 请解释TCP和UDP的区别。 2. 什么是操作系统&#xff1f;它的主要功能是什么&#xff1f; 3. 请解释设计模式中的单例模式&#xff0c;并给出一个实际应用的例…

【mysql】docker运行mysql8.0

背景 mariadb10.5.8报错&#xff1a;Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘LIMIT ?’ at line 1 所以更换为mysql8.0.39试试 docker run启动…

C#实现语音预处理:降噪/静音检测/自动增益

无论是在音视频录制系统&#xff0c;还是音视频通话系统、或视频会议系统中&#xff0c;对从麦克风采集到的说话的声音数据进行预处理&#xff0c;都是是非常必要的。 语音数据预处理主要包括&#xff1a;​​降噪&#xff08;Noise Reduction&#xff09;、静音检测&#xff0…

组合模式Composite Pattern

模式定义 又称整体-部分模式 组合多个对象形成 树形结构 以表示“整体-部分”的结构层次 组合模式对单个对象&#xff08;即叶子对象&#xff09;和组合对象&#xff08;即容器对象&#xff09;的使用具有一致性对象结构型模式 模式结构 Component&#xff1a;抽象构件Leaf&a…

商代大模型:智能重构下的文明曙光与青铜密码

引言&#xff1a;技术奇点的历史想象 在人类文明的长河中&#xff0c;技术的进步始终是推动社会变革的核心动力。从青铜冶炼到文字发明&#xff0c;从农业革命到工业革命&#xff0c;每一次技术飞跃都重塑了人类对世界的认知与生存方式。而如今&#xff0c;人工智能的崛起正以…

【Python】python系列之函数作用域

Python 系列文章学习记录&#xff1a; Python系列之Windows环境安装配置_开着拖拉机回家的博客-CSDN博客 Python系列之变量和运算符_开着拖拉机回家的博客-CSDN博客 Python系列之判断和循环_开着拖拉机回家的博客-CSDN博客 Python系列之字符串和列表_开着拖拉机回家的博客…

Unity UI 核心类解析之Graphic

&#x1f9f1; Unity UI 核心类解析&#xff1a;Graphic 类详解 一、什么是 Graphic&#xff1f; 在 Unity 的 UI 系统中&#xff0c;Graphic 是一个抽象基类&#xff0c;继承自 UIBehaviour 并实现了 ICanvasElement 接口。它是所有可以被绘制到屏幕上的 UI 元素的基础类。 …

【Elasticsearch】文档迁移(Reindex)

文档迁移 1.为什么要进行 reindex 操作2.Reindex 操作的本质3.实际案例3.1 同集群索引之间的全量数据迁移3.2 同集群索引之间基于特定条件的数据迁移3.2.1 源索引设置检索条件3.2.2 基于 script 脚本的索引迁移3.2.3 基于预处理管道的数据迁移 3.3 不同集群之间的索引迁移3.4 查…

WordPress 区块版面配置指南

WordPress 的区块编辑器(Gutenberg)提供了灵活的版面配置选项&#xff0c;以下是主要配置方法&#xff1a; 基本区块布局 添加区块&#xff1a;点击””按钮或按”/”键快速插入区块 常用内容区块&#xff1a; 段落(Paragraph) 标题(Heading) 图像(Image) 画廊(Gallery)…

TensorFlow基础之理解张量

2.理解张量 张量&#xff08;Tensors&#xff09;介绍 张量是物理和工程领域的基础数学结构。但是过去张量很少在计算机科学里使用。它与离散数学和逻辑学有更多的联系。随着机器学习的出现&#xff0c;这种状态开始显著的改变&#xff0c;成为连续向量的计算基础。现代机器学…

Flume 安装与配置步骤

1.解压 tar -zxvf apache-flume-1.9.0-bin.tar.gz 2.配置环境变量 vim /etc/profile export FLUME_HOME/home/wang/soft/flume/apache-flume-1.9.0-bin export PATH$PATH:$FLUME_HOME/bin source /etc/profile 3.创建必要的目录 mkdir -p $FLUME_HOME/conf 4.创建 Flume 配置文…

还原线上 WebView 异常:手机端APP远程调试

前端调试总被理解为开发阶段的事&#xff0c;但在实际项目中&#xff0c;真正困难的调试往往发生在产品上线之后。用户反馈“看不到内容”、“一直转圈”、“点了没反应”&#xff0c;而开发环境无法复现&#xff0c;测试机也正常运行&#xff0c;这时怎么定位、验证和解决问题…

102页满分PPT | 汽车设备制造业企业信息化业务解决方案智能制造汽车黑灯工厂解决方案

这份文档是一份汽车设备制造业企业信息化业务解决方案&#xff0c;详细阐述了企业从生产到销售的全流程信息化建设。针对企业目前手工管理为主、信息化程度低、数据追溯困难等问题&#xff0c;提出了建立统一信息化平台的目标&#xff0c;涵盖财务、业务、流程和数据的整合。方…

SQLite 表达式详解

SQLite 表达式详解 引言 SQLite 是一个轻量级的数据库,广泛用于移动设备和桌面应用程序。SQLite 的表达式是 SQL 语句的核心,它们用于查询、更新和删除数据库中的数据。本文将详细解释 SQLite 的各种表达式,并探讨它们在数据库操作中的重要性。 表达式概述 在 SQLite 中…

沉浸式AI交互数字人技术解析

360智汇云沉浸式AI交互数字人支持开发者灵活接入和私有化部署大模型服务&#xff0c;构建面向业务场景的实时音视频交互能力。系统集成了360智汇云自研的沉浸式AI交互数字人引擎与高性能 RTC 模块&#xff0c;保障音视频传输过程中的低延迟、高稳定性和高并发承载能力&#xff…

HarmonyOS 评论回复弹窗最佳实践

HarmonyOS 评论回复弹窗最佳实践 前言 在移动应用开发中&#xff0c;评论回复功能是一个常见且重要的交互场景。本文将详细介绍如何在 HarmonyOS 中实现一个功能完善的评论回复弹窗&#xff0c;包括弹窗选型、富文本编辑、软键盘适配等关键技术点。 功能概述 我们要实现的评…

Git 回退操作详解:带示例的“小白”指南

前言 在日常开发中&#xff0c;我们难免会遇到&#xff1a; 改错代码&#xff1a;推送之前才发现某些行根本就不该动提交错误&#xff1a;commit 信息打错、提交到错误分支想回到之前版本&#xff1a;测试时发现之前版本是好的&#xff0c;需要回去查看 这就需要用到 Git 的…