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

[译]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 450
#extension GL_ARB_separate_shader_objects : enable
 
layout(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;
}

 

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 = 0) in dvec3 inPosition;
layout(location = 2) 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 = 0;
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, 2> getAttributeDescriptions() {
    std::array<VkVertexInputAttributeDescription, 2> 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[0].binding = 0;
attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
attributeDescriptions[0].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[1].binding = 0;
attributeDescriptions[1].location = 1;
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].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 = 1;
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

 

 

posted @ 2019-07-10 17:36  BIT祝威  阅读(1094)  评论(0编辑  收藏  举报