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



两者之间有什么联系:

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

例如

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

我没有在互联网上找到这些信息,金属着色语言规范甚至不包括"描述符"或"网格"这两个词。

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

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

  3. 是的。您没有[[stage_in]]参数,而是具有MyVertexType *类型的[[buffer(0)]](假设所有顶点数据都在单个顶点缓冲区中交错)的参数,以及一个告诉您在该缓冲区中索引的位置的[[vertex_id]]参数。

下面是从呈现命令编码器上的MTKMesh设置顶点缓冲区的示例:

for (index, vertexBuffer) in mesh.vertexBuffers.enumerated() {
commandEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: index)
}

vertexBufferMTKMeshBuffer类型,而它的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)

您可以创建相应的MTLVertexDescriptor,以创建适合渲染此类网格的渲染管线状态:

let vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mdlVertexDescriptor)!

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

struct VertexIn {
float3 position  [[attribute(0)]];
float3 normal    [[attribute(1)]];
float2 texCoords [[attribute(2)]];
};

下面是一个使用其中一个顶点的存根顶点函数:

vertex VertexOut vertex_main(VertexIn in [[stage_in]])
{
}

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

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];
}

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

相关内容

  • 没有找到相关文章

最新更新