[译]Vulkan教程(21)顶点input描述

Vertex input description 顶点input描述

Introduction 入门

In the next few chapters, we're going to replace the hardcoded vertex data in the vertex shader with a vertex buffer in memory. We'll start with the easiest approach of creating a CPU visible buffer and using memcpy to copy the vertex data into it directly, and after that we'll see how to use a staging buffer to copy the vertex data to high performance memory.

接下来的几章,我们将替换顶点shader里硬编码的顶点数据with一个内存中的顶点buffer。我们从最简单的方式开始,创建一个CPU可见的buffer,用memcpy 直接复制顶点数据进顶点buffer,之后,我们将看一下如何用一个阶段buffer to复制顶点数据to高性能内存。

Vertex shader 顶点shader

First change the vertex shader to no longer include the vertex data in the shader code itself. The vertex shader takes input from a vertex buffer using the in keyword.

首先,修改顶点shader,不再包含顶点数据。顶点shader从一个顶点buffer(用in 关键字表示)接收input。

#version
#extension GL_ARB_separate_shader_objects : enable layout(location = ) in vec2 inPosition;
layout(location = ) in vec3 inColor; layout(location = ) out vec3 fragColor; void main() {
gl_Position = vec4(inPosition, 0.0, 1.0);
fragColor = inColor;
}

The inPosition and inColor variables are vertex attributes. They're properties that are specified per-vertex in the vertex buffer, just like we manually specified a position and color per vertex using the two arrays. Make sure to recompile the vertex shader!

inPosition 和inColor 变量是顶点属性。它们是顶点buffer中逐顶点的属性,就像我们手工逐顶点地指定一个位置和颜色-用2个数组。确保重新编译这个顶点shader!

Just like fragColor, the layout(location = x) annotations assign indices to the inputs that we can later use to reference them. It is important to know that some types, like dvec3 64 bit vectors, use multiple slots. That means that the index after it must be at least 2 higher:

就像fragColor一样,layout(location = x) 注解赋予索引to输入that我们之后可以用来引用它们。重要的一点是,有的类型(例如64位向量dvec3 )使用多个slot。这意味着,它之后的索引必须最少增加2:

layout(location = ) in dvec3 inPosition;
layout(location = ) in vec3 inColor;

You can find more info about the layout qualifier in the OpenGL wiki.

你可以在OpenGL wiki找到更多关于布局标识符的信息。

Vertex data 顶点数据

We're moving the vertex data from the shader code to an array in the code of our program. Start by including the GLM library, which provides us with linear algebra related types like vectors and matrices. We're going to use these types to specify the position and color vectors.

我们要把shader代码中的数据移动到我们程序代码的一个数组里。首先,引入GLM库,which提供线性代数相关的类型-例如向量和矩阵。我们要用这些类型to指定位置和颜色向量。

#include <glm/glm.hpp>

Create a new structure called Vertex with the two attributes that we're going to use in the vertex shader inside it:

创建新结构体Vertex ,添加2个属性that我们要在顶点shader中使用:

struct Vertex {
glm::vec2 pos;
glm::vec3 color;
};

GLM conveniently provides us with C++ types that exactly match the vector types used in the shader language.

GLM贴心地提供的C++类型与shader语言的向量类型完全匹配。

const std::vector<Vertex> vertices = {
{{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}},
{{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}
};

Now use the Vertex structure to specify an array of vertex data. We're using exactly the same position and color values as before, but now they're combined into one array of vertices. This is known as interleaving vertex attributes.

现在用Vertex 结构体指定顶点数据的数组。我们使用和之前完全一样的位置和颜色值,但现在它们组合进一个顶点数组了。这被称为交错顶点属性。

Binding descriptions 绑定描述

The next step is to tell Vulkan how to pass this data format to the vertex shader once it's been uploaded into GPU memory. There are two types of structures needed to convey this information.

下一步是告诉Vulkan,当它被上传到GPU内存后,如何传入这个数据格式到顶点shader。需要用2种类型的结构体来传达这个信息。

The first structure is VkVertexInputBindingDescription and we'll add a member function to the Vertex struct to populate it with the right data.

第一个结构体是VkVertexInputBindingDescription ,我们将添加一个成员函数到结构体Vertex  to填入正确的数据给它。

struct Vertex {
glm::vec2 pos;
glm::vec3 color; static VkVertexInputBindingDescription getBindingDescription() {
VkVertexInputBindingDescription bindingDescription = {}; return bindingDescription;
}
};

A vertex binding describes at which rate to load data from memory throughout the vertices. It specifies the number of bytes between data entries and whether to move to the next data entry after each vertex or after each instance.

一个顶点binding描述了,以何等频率从内存中加载顶点数组中的数据。它指定了数据实体直接的字节数,是移动到下一个数据实体还是下一个instance。

VkVertexInputBindingDescription bindingDescription = {};
bindingDescription.binding = ;
bindingDescription.stride = sizeof(Vertex);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

All of our per-vertex data is packed together in one array, so we're only going to have one binding. The binding parameter specifies the index of the binding in the array of bindings. The stride parameter specifies the number of bytes from one entry to the next, and the inputRate parameter can have one of the following values:

  • VK_VERTEX_INPUT_RATE_VERTEX: Move to the next data entry after each vertex
  • VK_VERTEX_INPUT_RATE_INSTANCE: Move to the next data entry after each instance

所有的逐顶点数据都被打包进了一个数组,所以我们只需有一个binding。binding参数指定了binding数组的某个元素的索引。stride 参数指定了相邻实体间隔的字节数,inputRate 参数值为下述2个之一:

  • VK_VERTEX_INPUT_RATE_VERTEX:每个顶点之后,移动到下一个数据实体。
  • VK_VERTEX_INPUT_RATE_INSTANCE:每个实例之后,移动到下一个数据实体。

We're not going to use instanced rendering, so we'll stick to per-vertex data.

我们不会使用instanced渲染,所以我们只用逐顶点的数据。

Attribute descriptions 属性描述

The second structure that describes how to handle vertex input is VkVertexInputAttributeDescription. We're going to add another helper function to Vertex to fill in these structs.

第二个结构体that描述如何处理顶点 是VkVertexInputAttributeDescription。我们要添加另一个辅助函数到Vertex  to填入这些结构体。

#include <array>

...

static std::array<VkVertexInputAttributeDescription, > getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, > attributeDescriptions = {}; return attributeDescriptions;
}

As the function prototype indicates, there are going to be two of these structures. An attribute description struct describes how to extract a vertex attribute from a chunk of vertex data originating from a binding description. We have two attributes, position and color, so we need two attribute description structs.

如函数原型所示,会有2个这种结构体。一个属性描述结构体,描述如何根据binding描述从顶点数据块里提取顶点数据。我们有2个属性,位置和颜色,所以我们需要2个属性描述结构体。

attributeDescriptions[].binding = ;
attributeDescriptions[].location = ;
attributeDescriptions[].format = VK_FORMAT_R32G32_SFLOAT;
attributeDescriptions[].offset = offsetof(Vertex, pos);

The binding parameter tells Vulkan from which binding the per-vertex data comes. The location parameter references the location directive of the input in the vertex shader. The input in the vertex shader with location 0 is the position, which has two 32-bit float components.

binding 参数告诉Vulkan,逐顶点数据从哪个binding开始。location 参数指向顶点shader中的location 指令。顶点shader中的位置为0的input是position,它有2个32位浮点数。

The format parameter describes the type of data for the attribute. A bit confusingly, the formats are specified using the same enumeration as color formats. The following shader types and formats are commonly used together:

format 参数描述了属性数据的类型。有点困惑的是,这个格式是用与颜色格式相同的枚举类型指定的。下述shader类型和格式通常是一起用的。

  • floatVK_FORMAT_R32_SFLOAT
  • vec2VK_FORMAT_R32G32_SFLOAT
  • vec3VK_FORMAT_R32G32B32_SFLOAT
  • vec4VK_FORMAT_R32G32B32A32_SFLOAT

As you can see, you should use the format where the amount of color channels matches the number of components in the shader data type. It is allowed to use more channels than the number of components in the shader, but they will be silently discarded. If the number of channels is lower than the number of components, then the BGA components will use default values of (0, 0, 1). The color type (SFLOATUINTSINT) and bit width should also match the type of the shader input. See the following examples:

如你所见,你应当用格式where颜色通道的数量与shader中数据类型的元素数量匹配。你可以用比shader中元素数量更多的通道,但是它们会被默默地忽略。如果通道数量低于元素数量,那么BGA元素会用默认值(0, 0, 1)。颜色类型(SFLOATUINTSINT)和位宽度也应当与shader的输入数据类型匹配。看下面的例子:

  • ivec2VK_FORMAT_R32G32_SINT, a 2-component vector of 32-bit signed integers 32位整型有符号2元素向量
  • uvec4VK_FORMAT_R32G32B32A32_UINT, a 4-component vector of 32-bit unsigned integers 32位整型无符号4元素向量
  • doubleVK_FORMAT_R64_SFLOAT, a double-precision (64-bit) float 双精度(64位)浮点数

The format parameter implicitly defines the byte size of attribute data and the offset parameter specifies the number of bytes since the start of the per-vertex data to read from. The binding is loading one Vertex at a time and the position attribute (pos) is at an offset of 0 bytes from the beginning of this struct. This is automatically calculated using the offsetof macro.

format 参数隐式地定义了属性数据的字节数,offset 参数指定了从第几个字节开始读逐顶点的数据。Binding每个Vertex 加载一次,位置属性(pos)位于这个结构体的第0个偏移量。这可以通过宏offsetof 自动计算。

attributeDescriptions[].binding = ;
attributeDescriptions[].location = ;
attributeDescriptions[].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[].offset = offsetof(Vertex, color);

The color attribute is described in much the same way.

颜色属性也用相同的方式描述。

Pipeline vertex input 管道顶点input

We now need to set up the graphics pipeline to accept vertex data in this format by referencing the structures in createGraphicsPipeline. Find the vertexInputInfo struct and modify it to reference the two descriptions:

我们现在需要设置图形管道to接收这种格式的顶点数据by引用createGraphicsPipeline中的结构体。找到vertexInputInfo 结构体,修改它to引用这2个描述:

auto bindingDescription = Vertex::getBindingDescription();
auto attributeDescriptions = Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = ;
vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

The pipeline is now ready to accept vertex data in the format of the vertices container and pass it on to our vertex shader. If you run the program now with validation layers enabled, you'll see that it complains that there is no vertex buffer bound to the binding. The next step is to create a vertex buffer and move the vertex data to it so the GPU is able to access it.

管道现在准备好接收vertices 容器格式的顶点数据并将其传入顶点shader了。如果你现在运行程序with验证层启用,你会看到它说没有顶点buffer绑定到binding。下一个是创建顶点buffer,将顶点数据放入其中,这样GPU就可以读取它了。

C++ code / Vertex shader / Fragment shader

[译]Vulkan教程(21)顶点input描述的更多相关文章

  1. [译]Vulkan教程(26)描述符池和set

    [译]Vulkan教程(26)描述符池和set Descriptor pool and sets 描述符池和set Introduction 入门 The descriptor layout from ...

  2. [译]Vulkan教程(25)描述符布局和buffer

    [译]Vulkan教程(25)描述符布局和buffer Descriptor layout and buffer 描述符布局和buffer Introduction 入门 We're now able ...

  3. [译]Vulkan教程(22)创建顶点buffer

    [译]Vulkan教程(22)创建顶点buffer Vertex buffer creation 创建顶点buffer Introduction 入门 Buffers in Vulkan are re ...

  4. [译]Vulkan教程(29)组合的Image采样器

    [译]Vulkan教程(29)组合的Image采样器 Combined image sampler 组合的image采样器 Introduction 入门 We looked at descripto ...

  5. [译]Vulkan教程(27)Image

    [译]Vulkan教程(27)Image Images Introduction 入门 The geometry has been colored using per-vertex colors so ...

  6. [译]Vulkan教程(14)图形管道基础之固定功能

    [译]Vulkan教程(14)图形管道基础之固定功能 Fixed functions 固定功能 The older graphics APIs provided default state for m ...

  7. [译]Vulkan教程(13)图形管道基础之Shader模块

    [译]Vulkan教程(13)图形管道基础之Shader模块 Shader modules Unlike earlier APIs, shader code in Vulkan has to be s ...

  8. [译]Vulkan教程(30)深度缓存

    [译]Vulkan教程(30)深度缓存 Depth buffering 深度缓存 Introduction 入门 The geometry we've worked with so far is pr ...

  9. [译]Vulkan教程(16)图形管道基础之总结

    [译]Vulkan教程(16)图形管道基础之总结 Conclusion 总结 We can now combine all of the structures and objects from the ...

随机推荐

  1. 超简单!asp.net core前后端分离项目使用gitlab-ci持续集成到IIS

    现在好多使用gitlab-ci的持续集成的教程,大部分都是发布到linux系统上的,但是目前还是有很大一部分企业使用的都是windows系统使用IIS在部署.NET应用程序.这里写一下如何使用gitl ...

  2. 计算机二级Python

    概述 计算机二级在近两年新加了python的选择,趁机考了一下,顺便记录一下学习的一些所获 第一章 程序设计语言概述 考纲考点: 这一部分主要是介绍计算机语言的公共常识,一些尝试我就按照自己的理解方式 ...

  3. 深入浅出分析 PriorityQueue

    一.摘要 在前几篇文章中,咱们了解到,Queue 的实现类有 ArrayDeque.LinkedList.PriorityQueue. 在上一章节中,陆续的介绍到 ArrayDeque 和 Linke ...

  4. 手动启动Oracle服务的.bat文件

    Oracle数据库的基本服务会占用很大的内存,有的程序员会在不用的时候Oracle服务关闭来减少对电脑内存资源的占用. 我在这准备了一个可以开启/关闭Oracle服务的bat文件,希望被采纳!!! 新 ...

  5. JS基础知识——变量类型和计算(一)

    JS中使用typeof能得到的哪些类型? 何时使用===何时使用==? JS中有哪些内置函数? JS变量按照存储方式区分为哪些类型,描述其特点? 如何理解JSON? 知识点梳理 一.变量类型: (1) ...

  6. Python自定义包引入【新手必学】

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:sys_song python中的Module是比较重要的概念.常见的情 ...

  7. 【Python进阶】来谈谈几个常用的内置函数

    匿名函数(lambda表达式) 在Python中,函数可以算的上是“一等公民”了,我们先回顾下函数的优点: 减少代码重复量 模块化代码 但是我们有没有想过,如果我们需要一个函数,比较简短,而且只需要使 ...

  8. JavaScript动画实例:旋转的圆球

    1.绕椭圆轨道旋转的圆球 在Canvas画布中绘制一个椭圆,然后在椭圆上绘制一个用绿色填充的实心圆.之后每隔0.1秒刷新,重新绘制椭圆和实心圆,重新绘制时,实心圆的圆心坐标发生变化,但圆心坐标仍然位于 ...

  9. 初识PMP PMBOK初解(指南第一章引论)

    引论 1.1指南概述和目的 普遍认可:大多数时候是适用于大多数项目,价值和有效性已获得一致认可. 良好实践:知识.技能.工具和技术能够达到预期的商业价值和成果,提高很多项目成功的可能性. 确定过程.输 ...

  10. sql server的简单分页

    --显示前条数据 select top(4) * from students; --pageSize:每页显示的条数 --pageNow:当前页 )) sno from students); --带条 ...