[[stage_in]],MTLVertexDescriptor和MTKMesh之间的连接

hyp*_*not 3 3d graphics swift metal metalkit

之间的联系是什么:

  1. 使用[[stage_in]]在金属着色器
  2. 使用 MTLVertexDescriptor
  3. 使用 MTKMesh

例如

  1. [[stage_in]]不使用就可以使用MTLVertexDescriptor吗?
  2. 是否可以MTLVertexDescriptor不使用MTKMesh而是使用基于自定义结构的数据结构的数组来使用?如struct Vertex {...}, Array<Vertex>
  3. MTKMesh不使用就可以使用MTLVertexDescriptor吗?例如使用相同的基于结构的数据结构?

我在互联网上找不到此信息,并且“金属底纹语言规范”甚至没有包含“描述符”或“网格”一词。

war*_*enm 6

  1. 否。如果您尝试从没有顶点描述符的管道描述符创建渲染管道状态,并且相应的顶点函数具有[[stage_in]]参数,则管道状态创建调用将失败。

  2. 是。毕竟,当您绘制一个时MTKMesh,您仍然必须调用setVertexBuffer(...)由网格的MTKMeshBuffers 包装的缓冲区。您可以轻松地创建MTLBuffer自己并将自己的自定义顶点结构复制到其中。

  3. 是。除了使用[[stage_in]]参数之外,您还需要拥有一个类型为的参数[[buffer(0)]](假设所有顶点数据都交织在一个顶点缓冲区中)MyVertexType *,以及一个[[vertex_id]]参数,该参数告诉您在哪里索引该缓冲区。

这是从MTKMesh渲染命令编码器上设置顶点缓冲区的示例:

for (index, vertexBuffer) in mesh.vertexBuffers.enumerated() {
    commandEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: index)
}
Run Code Online (Sandbox Code Playgroud)

vertexBuffer是类型的MTKMeshBuffer,而它的buffer属性是类型的MTLBuffer; 我提到这一点是因为它可能会造成混淆。

您可以通过以下一种方式创建顶点描述符,以告知模型I / O和MetalKit布置要加载的网格数据:

let mdlVertexDescriptor = MDLVertexDescriptor()
mdlVertexDescriptor.attributes[0] = MDLVertexAttribute(name: MDLVertexAttributePosition, format: MDLVertexFormat.float3, offset: 0, bufferIndex: 0)
mdlVertexDescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeNormal, format: MDLVertexFormat.float3, offset: 12, bufferIndex: 0)
mdlVertexDescriptor.attributes[2] = MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate, format: MDLVertexFormat.float2, offset: 24, bufferIndex: 0)
mdlVertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: 32)
Run Code Online (Sandbox Code Playgroud)

您可以创建一个对应项MTLVertexDescriptor,以创建适合于渲染此类网格的渲染管道状态:

let vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mdlVertexDescriptor)!
Run Code Online (Sandbox Code Playgroud)

这是与该布局匹配的顶点结构:

struct VertexIn {
    float3 position  [[attribute(0)]];
    float3 normal    [[attribute(1)]];
    float2 texCoords [[attribute(2)]];
};
Run Code Online (Sandbox Code Playgroud)

这是一个使用以下顶点之一的存根顶点函数:

vertex VertexOut vertex_main(VertexIn in [[stage_in]])
{
}
Run Code Online (Sandbox Code Playgroud)

最后,这是一个顶点结构和顶点函数,可用于在没有顶点描述符的情况下渲染完全相同的网格数据:

struct VertexIn {
    packed_float3 position;
    packed_float3 normal;
    packed_float2 texCoords;
};

vertex VertexOut vertex_main(device VertexIn *vertices [[buffer(0)]],
                             uint vid [[vertex_id]])
{
    VertexIn in = vertices[vid];
}
Run Code Online (Sandbox Code Playgroud)

请注意,在这最后一种情况下,我需要将结构成员标记为压缩,因为默认情况下,为对齐目的而填充Metal的simd类型(具体来说,a的跨度float316个字节,而不是我们在顶点描述符中要求的12 字节)。