否。如果您尝试从没有顶点描述符的管道描述符创建渲染管道状态,并且相应的顶点函数有[[stage_in]]
参数,则管道状态创建调用将失败。
是的。毕竟,当您绘制s 时MTKMesh
,您仍然有义务使用由网格的组成部分ssetVertexBuffer(...)
包裹的缓冲区进行调用。MTKMeshBuffer
您可以轻松地创建MTLBuffer
自己并将自定义顶点结构复制到其中。
是的。[[stage_in]]
您将拥有一个类型为[[buffer(0)]]
(假设所有顶点数据交错在单个顶点缓冲区中)的参数,而不是MyVertexType *
一个参数,以及一个[[vertex_id]]
告诉您在哪里索引到该缓冲区的参数。
MTKMesh
这是从渲染命令编码器设置顶点缓冲区的示例:
for (index, vertexBuffer) in mesh.vertexBuffers.enumerated() {
commandEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: index)
}
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)
您可以创建一个对应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 类型被填充以用于对齐目的(具体而言,a 的步幅float3
是16个字节,而不是我们在顶点描述符中要求的 12 个字节)。